Credit @Ruirize for newgrounds
[youtube-dl] / youtube_dl / __init__.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 __authors__  = (
5     'Ricardo Garcia Gonzalez',
6     'Danny Colligan',
7     'Benjamin Johnson',
8     'Vasyl\' Vavrychuk',
9     'Witold Baryluk',
10     'Paweł Paprota',
11     'Gergely Imreh',
12     'Rogério Brito',
13     'Philipp Hagemeister',
14     'Sören Schulze',
15     'Kevin Ngo',
16     'Ori Avtalion',
17     'shizeeg',
18     'Filippo Valsorda',
19     'Christian Albrecht',
20     'Dave Vasilevsky',
21     'Jaime Marquínez Ferrándiz',
22     'Jeff Crouse',
23     'Osama Khalid',
24     'Michael Walter',
25     'M. Yasoob Ullah Khalid',
26     'Julien Fraichard',
27     'Johny Mo Swag',
28     'Axel Noack',
29     'Albert Kim',
30     'Pierre Rudloff',
31     'Huarong Huo',
32     'Ismael Mejía',
33     'Steffan \'Ruirize\' James',
34 )
35
36 __license__ = 'Public Domain'
37
38 import codecs
39 import getpass
40 import optparse
41 import os
42 import random
43 import re
44 import shlex
45 import socket
46 import subprocess
47 import sys
48 import warnings
49 import platform
50
51
52 from .utils import *
53 from .update import update_self
54 from .version import __version__
55 from .FileDownloader import *
56 from .extractor import gen_extractors
57 from .YoutubeDL import YoutubeDL
58 from .PostProcessor import *
59
60 def parseOpts(overrideArguments=None):
61     def _readOptions(filename_bytes):
62         try:
63             optionf = open(filename_bytes)
64         except IOError:
65             return [] # silently skip if file is not present
66         try:
67             res = []
68             for l in optionf:
69                 res += shlex.split(l, comments=True)
70         finally:
71             optionf.close()
72         return res
73
74     def _format_option_string(option):
75         ''' ('-o', '--option') -> -o, --format METAVAR'''
76
77         opts = []
78
79         if option._short_opts:
80             opts.append(option._short_opts[0])
81         if option._long_opts:
82             opts.append(option._long_opts[0])
83         if len(opts) > 1:
84             opts.insert(1, ', ')
85
86         if option.takes_value(): opts.append(' %s' % option.metavar)
87
88         return "".join(opts)
89
90     def _comma_separated_values_options_callback(option, opt_str, value, parser):
91         setattr(parser.values, option.dest, value.split(','))
92
93     def _find_term_columns():
94         columns = os.environ.get('COLUMNS', None)
95         if columns:
96             return int(columns)
97
98         try:
99             sp = subprocess.Popen(['stty', 'size'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
100             out,err = sp.communicate()
101             return int(out.split()[1])
102         except:
103             pass
104         return None
105
106     def _hide_login_info(opts):
107         opts = list(opts)
108         for private_opt in ['-p', '--password', '-u', '--username']:
109             try:
110                 i = opts.index(private_opt)
111                 opts[i+1] = '<PRIVATE>'
112             except ValueError:
113                 pass
114         return opts
115
116     max_width = 80
117     max_help_position = 80
118
119     # No need to wrap help messages if we're on a wide console
120     columns = _find_term_columns()
121     if columns: max_width = columns
122
123     fmt = optparse.IndentedHelpFormatter(width=max_width, max_help_position=max_help_position)
124     fmt.format_option_strings = _format_option_string
125
126     kw = {
127         'version'   : __version__,
128         'formatter' : fmt,
129         'usage' : '%prog [options] url [url...]',
130         'conflict_handler' : 'resolve',
131     }
132
133     parser = optparse.OptionParser(**kw)
134
135     # option groups
136     general        = optparse.OptionGroup(parser, 'General Options')
137     selection      = optparse.OptionGroup(parser, 'Video Selection')
138     authentication = optparse.OptionGroup(parser, 'Authentication Options')
139     video_format   = optparse.OptionGroup(parser, 'Video Format Options')
140     subtitles      = optparse.OptionGroup(parser, 'Subtitle Options')
141     downloader     = optparse.OptionGroup(parser, 'Download Options')
142     postproc       = optparse.OptionGroup(parser, 'Post-processing Options')
143     filesystem     = optparse.OptionGroup(parser, 'Filesystem Options')
144     verbosity      = optparse.OptionGroup(parser, 'Verbosity / Simulation Options')
145
146     general.add_option('-h', '--help',
147             action='help', help='print this help text and exit')
148     general.add_option('-v', '--version',
149             action='version', help='print program version and exit')
150     general.add_option('-U', '--update',
151             action='store_true', dest='update_self', help='update this program to latest version. Make sure that you have sufficient permissions (run with sudo if needed)')
152     general.add_option('-i', '--ignore-errors',
153             action='store_true', dest='ignoreerrors', help='continue on download errors, for example to to skip unavailable videos in a playlist', default=False)
154     general.add_option('--dump-user-agent',
155             action='store_true', dest='dump_user_agent',
156             help='display the current browser identification', default=False)
157     general.add_option('--user-agent',
158             dest='user_agent', help='specify a custom user agent', metavar='UA')
159     general.add_option('--referer',
160             dest='referer', help='specify a custom referer, use if the video access is restricted to one domain',
161             metavar='REF', default=None)
162     general.add_option('--list-extractors',
163             action='store_true', dest='list_extractors',
164             help='List all supported extractors and the URLs they would handle', default=False)
165     general.add_option('--extractor-descriptions',
166             action='store_true', dest='list_extractor_descriptions',
167             help='Output descriptions of all supported extractors', default=False)
168     general.add_option('--proxy', dest='proxy', default=None, help='Use the specified HTTP/HTTPS proxy', metavar='URL')
169     general.add_option('--no-check-certificate', action='store_true', dest='no_check_certificate', default=False, help='Suppress HTTPS certificate validation.')
170
171
172     selection.add_option('--playlist-start',
173             dest='playliststart', metavar='NUMBER', help='playlist video to start at (default is %default)', default=1)
174     selection.add_option('--playlist-end',
175             dest='playlistend', metavar='NUMBER', help='playlist video to end at (default is last)', default=-1)
176     selection.add_option('--match-title', dest='matchtitle', metavar='REGEX',help='download only matching titles (regex or caseless sub-string)')
177     selection.add_option('--reject-title', dest='rejecttitle', metavar='REGEX',help='skip download for matching titles (regex or caseless sub-string)')
178     selection.add_option('--max-downloads', metavar='NUMBER', dest='max_downloads', help='Abort after downloading NUMBER files', default=None)
179     selection.add_option('--min-filesize', metavar='SIZE', dest='min_filesize', help="Do not download any videos smaller than SIZE (e.g. 50k or 44.6m)", default=None)
180     selection.add_option('--max-filesize', metavar='SIZE', dest='max_filesize', help="Do not download any videos larger than SIZE (e.g. 50k or 44.6m)", default=None)
181     selection.add_option('--date', metavar='DATE', dest='date', help='download only videos uploaded in this date', default=None)
182     selection.add_option('--datebefore', metavar='DATE', dest='datebefore', help='download only videos uploaded before this date', default=None)
183     selection.add_option('--dateafter', metavar='DATE', dest='dateafter', help='download only videos uploaded after this date', default=None)
184
185
186     authentication.add_option('-u', '--username',
187             dest='username', metavar='USERNAME', help='account username')
188     authentication.add_option('-p', '--password',
189             dest='password', metavar='PASSWORD', help='account password')
190     authentication.add_option('-n', '--netrc',
191             action='store_true', dest='usenetrc', help='use .netrc authentication data', default=False)
192     authentication.add_option('--video-password',
193             dest='videopassword', metavar='PASSWORD', help='video password (vimeo only)')
194
195
196     video_format.add_option('-f', '--format',
197             action='store', dest='format', metavar='FORMAT',
198             help='video format code, specifiy the order of preference using slashes: "-f 22/17/18". "-f mp4" and "-f flv" are also supported')
199     video_format.add_option('--all-formats',
200             action='store_const', dest='format', help='download all available video formats', const='all')
201     video_format.add_option('--prefer-free-formats',
202             action='store_true', dest='prefer_free_formats', default=False, help='prefer free video formats unless a specific one is requested')
203     video_format.add_option('--max-quality',
204             action='store', dest='format_limit', metavar='FORMAT', help='highest quality format to download')
205     video_format.add_option('-F', '--list-formats',
206             action='store_true', dest='listformats', help='list all available formats (currently youtube only)')
207
208     subtitles.add_option('--write-sub', '--write-srt',
209             action='store_true', dest='writesubtitles',
210             help='write subtitle file', default=False)
211     subtitles.add_option('--write-auto-sub', '--write-automatic-sub',
212             action='store_true', dest='writeautomaticsub',
213             help='write automatic subtitle file (youtube only)', default=False)
214     subtitles.add_option('--all-subs',
215             action='store_true', dest='allsubtitles',
216             help='downloads all the available subtitles of the video', default=False)
217     subtitles.add_option('--list-subs',
218             action='store_true', dest='listsubtitles',
219             help='lists all available subtitles for the video', default=False)
220     subtitles.add_option('--sub-format',
221             action='store', dest='subtitlesformat', metavar='FORMAT',
222             help='subtitle format (default=srt) ([sbv/vtt] youtube only)', default='srt')
223     subtitles.add_option('--sub-lang', '--sub-langs', '--srt-lang',
224             action='callback', dest='subtitleslangs', metavar='LANGS', type='str',
225             default=[], callback=_comma_separated_values_options_callback,
226             help='languages of the subtitles to download (optional) separated by commas, use IETF language tags like \'en,pt\'')
227
228     downloader.add_option('-r', '--rate-limit',
229             dest='ratelimit', metavar='LIMIT', help='maximum download rate (e.g. 50k or 44.6m)')
230     downloader.add_option('-R', '--retries',
231             dest='retries', metavar='RETRIES', help='number of retries (default is %default)', default=10)
232     downloader.add_option('--buffer-size',
233             dest='buffersize', metavar='SIZE', help='size of download buffer (e.g. 1024 or 16k) (default is %default)', default="1024")
234     downloader.add_option('--no-resize-buffer',
235             action='store_true', dest='noresizebuffer',
236             help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.', default=False)
237     downloader.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
238
239     verbosity.add_option('-q', '--quiet',
240             action='store_true', dest='quiet', help='activates quiet mode', default=False)
241     verbosity.add_option('-s', '--simulate',
242             action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False)
243     verbosity.add_option('--skip-download',
244             action='store_true', dest='skip_download', help='do not download the video', default=False)
245     verbosity.add_option('-g', '--get-url',
246             action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
247     verbosity.add_option('-e', '--get-title',
248             action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
249     verbosity.add_option('--get-id',
250             action='store_true', dest='getid', help='simulate, quiet but print id', default=False)
251     verbosity.add_option('--get-thumbnail',
252             action='store_true', dest='getthumbnail',
253             help='simulate, quiet but print thumbnail URL', default=False)
254     verbosity.add_option('--get-description',
255             action='store_true', dest='getdescription',
256             help='simulate, quiet but print video description', default=False)
257     verbosity.add_option('--get-filename',
258             action='store_true', dest='getfilename',
259             help='simulate, quiet but print output filename', default=False)
260     verbosity.add_option('--get-format',
261             action='store_true', dest='getformat',
262             help='simulate, quiet but print output format', default=False)
263     verbosity.add_option('--newline',
264             action='store_true', dest='progress_with_newline', help='output progress bar as new lines', default=False)
265     verbosity.add_option('--no-progress',
266             action='store_true', dest='noprogress', help='do not print progress bar', default=False)
267     verbosity.add_option('--console-title',
268             action='store_true', dest='consoletitle',
269             help='display progress in console titlebar', default=False)
270     verbosity.add_option('-v', '--verbose',
271             action='store_true', dest='verbose', help='print various debugging information', default=False)
272     verbosity.add_option('--dump-intermediate-pages',
273             action='store_true', dest='dump_intermediate_pages', default=False,
274             help='print downloaded pages to debug problems(very verbose)')
275
276     filesystem.add_option('-t', '--title',
277             action='store_true', dest='usetitle', help='use title in file name (default)', default=False)
278     filesystem.add_option('--id',
279             action='store_true', dest='useid', help='use only video ID in file name', default=False)
280     filesystem.add_option('-l', '--literal',
281             action='store_true', dest='usetitle', help='[deprecated] alias of --title', default=False)
282     filesystem.add_option('-A', '--auto-number',
283             action='store_true', dest='autonumber',
284             help='number downloaded files starting from 00000', default=False)
285     filesystem.add_option('-o', '--output',
286             dest='outtmpl', metavar='TEMPLATE',
287             help=('output filename template. Use %(title)s to get the title, '
288                   '%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, '
289                   '%(autonumber)s to get an automatically incremented number, '
290                   '%(ext)s for the filename extension, %(upload_date)s for the upload date (YYYYMMDD), '
291                   '%(extractor)s for the provider (youtube, metacafe, etc), '
292                   '%(id)s for the video id , %(playlist)s for the playlist the video is in, '
293                   '%(playlist_index)s for the position in the playlist and %% for a literal percent. '
294                   'Use - to output to stdout. Can also be used to download to a different directory, '
295                   'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .'))
296     filesystem.add_option('--autonumber-size',
297             dest='autonumber_size', metavar='NUMBER',
298             help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --autonumber option is given')
299     filesystem.add_option('--restrict-filenames',
300             action='store_true', dest='restrictfilenames',
301             help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False)
302     filesystem.add_option('-a', '--batch-file',
303             dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)')
304     filesystem.add_option('-w', '--no-overwrites',
305             action='store_true', dest='nooverwrites', help='do not overwrite files', default=False)
306     filesystem.add_option('-c', '--continue',
307             action='store_true', dest='continue_dl', help='resume partially downloaded files', default=True)
308     filesystem.add_option('--no-continue',
309             action='store_false', dest='continue_dl',
310             help='do not resume partially downloaded files (restart from beginning)')
311     filesystem.add_option('--cookies',
312             dest='cookiefile', metavar='FILE', help='file to read cookies from and dump cookie jar in')
313     filesystem.add_option('--no-part',
314             action='store_true', dest='nopart', help='do not use .part files', default=False)
315     filesystem.add_option('--no-mtime',
316             action='store_false', dest='updatetime',
317             help='do not use the Last-modified header to set the file modification time', default=True)
318     filesystem.add_option('--write-description',
319             action='store_true', dest='writedescription',
320             help='write video description to a .description file', default=False)
321     filesystem.add_option('--write-info-json',
322             action='store_true', dest='writeinfojson',
323             help='write video metadata to a .info.json file', default=False)
324     filesystem.add_option('--write-thumbnail',
325             action='store_true', dest='writethumbnail',
326             help='write thumbnail image to disk', default=False)
327
328
329     postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
330             help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)')
331     postproc.add_option('--audio-format', metavar='FORMAT', dest='audioformat', default='best',
332             help='"best", "aac", "vorbis", "mp3", "m4a", "opus", or "wav"; best by default')
333     postproc.add_option('--audio-quality', metavar='QUALITY', dest='audioquality', default='5',
334             help='ffmpeg/avconv audio quality specification, insert a value between 0 (better) and 9 (worse) for VBR or a specific bitrate like 128K (default 5)')
335     postproc.add_option('--recode-video', metavar='FORMAT', dest='recodevideo', default=None,
336             help='Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm)')
337     postproc.add_option('-k', '--keep-video', action='store_true', dest='keepvideo', default=False,
338             help='keeps the video file on disk after the post-processing; the video is erased by default')
339     postproc.add_option('--no-post-overwrites', action='store_true', dest='nopostoverwrites', default=False,
340             help='do not overwrite post-processed files; the post-processed files are overwritten by default')
341     postproc.add_option('--embed-subs', action='store_true', dest='embedsubtitles', default=False,
342             help='embed subtitles in the video (only for mp4 videos)')
343
344
345     parser.add_option_group(general)
346     parser.add_option_group(selection)
347     parser.add_option_group(downloader)
348     parser.add_option_group(filesystem)
349     parser.add_option_group(verbosity)
350     parser.add_option_group(video_format)
351     parser.add_option_group(subtitles)
352     parser.add_option_group(authentication)
353     parser.add_option_group(postproc)
354
355     if overrideArguments is not None:
356         opts, args = parser.parse_args(overrideArguments)
357         if opts.verbose:
358             write_string(u'[debug] Override config: ' + repr(overrideArguments) + '\n')
359     else:
360         xdg_config_home = os.environ.get('XDG_CONFIG_HOME')
361         if xdg_config_home:
362             userConfFile = os.path.join(xdg_config_home, 'youtube-dl.conf')
363         else:
364             userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf')
365         systemConf = _readOptions('/etc/youtube-dl.conf')
366         userConf = _readOptions(userConfFile)
367         commandLineConf = sys.argv[1:]
368         argv = systemConf + userConf + commandLineConf
369         opts, args = parser.parse_args(argv)
370         if opts.verbose:
371             write_string(u'[debug] System config: ' + repr(_hide_login_info(systemConf)) + '\n')
372             write_string(u'[debug] User config: ' + repr(_hide_login_info(userConf)) + '\n')
373             write_string(u'[debug] Command-line args: ' + repr(_hide_login_info(commandLineConf)) + '\n')
374
375     return parser, opts, args
376
377 def _real_main(argv=None):
378     # Compatibility fixes for Windows
379     if sys.platform == 'win32':
380         # https://github.com/rg3/youtube-dl/issues/820
381         codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)
382
383     parser, opts, args = parseOpts(argv)
384
385     # Open appropriate CookieJar
386     if opts.cookiefile is None:
387         jar = compat_cookiejar.CookieJar()
388     else:
389         try:
390             jar = compat_cookiejar.MozillaCookieJar(opts.cookiefile)
391             if os.access(opts.cookiefile, os.R_OK):
392                 jar.load()
393         except (IOError, OSError) as err:
394             if opts.verbose:
395                 traceback.print_exc()
396             write_string(u'ERROR: unable to open cookie file\n')
397             sys.exit(101)
398     # Set user agent
399     if opts.user_agent is not None:
400         std_headers['User-Agent'] = opts.user_agent
401
402     # Set referer
403     if opts.referer is not None:
404         std_headers['Referer'] = opts.referer
405
406     # Dump user agent
407     if opts.dump_user_agent:
408         compat_print(std_headers['User-Agent'])
409         sys.exit(0)
410
411     # Batch file verification
412     batchurls = []
413     if opts.batchfile is not None:
414         try:
415             if opts.batchfile == '-':
416                 batchfd = sys.stdin
417             else:
418                 batchfd = open(opts.batchfile, 'r')
419             batchurls = batchfd.readlines()
420             batchurls = [x.strip() for x in batchurls]
421             batchurls = [x for x in batchurls if len(x) > 0 and not re.search(r'^[#/;]', x)]
422             if opts.verbose:
423                 write_string(u'[debug] Batch file urls: ' + repr(batchurls) + u'\n')
424         except IOError:
425             sys.exit(u'ERROR: batch file could not be read')
426     all_urls = batchurls + args
427     all_urls = [url.strip() for url in all_urls]
428
429     # General configuration
430     cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
431     if opts.proxy is not None:
432         if opts.proxy == '':
433             proxies = {}
434         else:
435             proxies = {'http': opts.proxy, 'https': opts.proxy}
436     else:
437         proxies = compat_urllib_request.getproxies()
438         # Set HTTPS proxy to HTTP one if given (https://github.com/rg3/youtube-dl/issues/805)
439         if 'http' in proxies and 'https' not in proxies:
440             proxies['https'] = proxies['http']
441     proxy_handler = compat_urllib_request.ProxyHandler(proxies)
442     https_handler = make_HTTPS_handler(opts)
443     opener = compat_urllib_request.build_opener(https_handler, proxy_handler, cookie_processor, YoutubeDLHandler())
444     # Delete the default user-agent header, which would otherwise apply in
445     # cases where our custom HTTP handler doesn't come into play
446     # (See https://github.com/rg3/youtube-dl/issues/1309 for details)
447     opener.addheaders =[]
448     compat_urllib_request.install_opener(opener)
449     socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
450
451     extractors = gen_extractors()
452
453     if opts.list_extractors:
454         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
455             compat_print(ie.IE_NAME + (' (CURRENTLY BROKEN)' if not ie._WORKING else ''))
456             matchedUrls = [url for url in all_urls if ie.suitable(url)]
457             all_urls = [url for url in all_urls if url not in matchedUrls]
458             for mu in matchedUrls:
459                 compat_print(u'  ' + mu)
460         sys.exit(0)
461     if opts.list_extractor_descriptions:
462         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
463             if not ie._WORKING:
464                 continue
465             desc = getattr(ie, 'IE_DESC', ie.IE_NAME)
466             if hasattr(ie, 'SEARCH_KEY'):
467                 _SEARCHES = (u'cute kittens', u'slithering pythons', u'falling cat', u'angry poodle', u'purple fish', u'running tortoise')
468                 _COUNTS = (u'', u'5', u'10', u'all')
469                 desc += u' (Example: "%s%s:%s" )' % (ie.SEARCH_KEY, random.choice(_COUNTS), random.choice(_SEARCHES))
470             compat_print(desc)
471         sys.exit(0)
472
473
474     # Conflicting, missing and erroneous options
475     if opts.usenetrc and (opts.username is not None or opts.password is not None):
476         parser.error(u'using .netrc conflicts with giving username/password')
477     if opts.password is not None and opts.username is None:
478         parser.error(u' account username missing\n')
479     if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
480         parser.error(u'using output template conflicts with using title, video ID or auto number')
481     if opts.usetitle and opts.useid:
482         parser.error(u'using title conflicts with using video ID')
483     if opts.username is not None and opts.password is None:
484         opts.password = getpass.getpass(u'Type account password and press return:')
485     if opts.ratelimit is not None:
486         numeric_limit = FileDownloader.parse_bytes(opts.ratelimit)
487         if numeric_limit is None:
488             parser.error(u'invalid rate limit specified')
489         opts.ratelimit = numeric_limit
490     if opts.min_filesize is not None:
491         numeric_limit = FileDownloader.parse_bytes(opts.min_filesize)
492         if numeric_limit is None:
493             parser.error(u'invalid min_filesize specified')
494         opts.min_filesize = numeric_limit
495     if opts.max_filesize is not None:
496         numeric_limit = FileDownloader.parse_bytes(opts.max_filesize)
497         if numeric_limit is None:
498             parser.error(u'invalid max_filesize specified')
499         opts.max_filesize = numeric_limit
500     if opts.retries is not None:
501         try:
502             opts.retries = int(opts.retries)
503         except (TypeError, ValueError) as err:
504             parser.error(u'invalid retry count specified')
505     if opts.buffersize is not None:
506         numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize)
507         if numeric_buffersize is None:
508             parser.error(u'invalid buffer size specified')
509         opts.buffersize = numeric_buffersize
510     try:
511         opts.playliststart = int(opts.playliststart)
512         if opts.playliststart <= 0:
513             raise ValueError(u'Playlist start must be positive')
514     except (TypeError, ValueError) as err:
515         parser.error(u'invalid playlist start number specified')
516     try:
517         opts.playlistend = int(opts.playlistend)
518         if opts.playlistend != -1 and (opts.playlistend <= 0 or opts.playlistend < opts.playliststart):
519             raise ValueError(u'Playlist end must be greater than playlist start')
520     except (TypeError, ValueError) as err:
521         parser.error(u'invalid playlist end number specified')
522     if opts.extractaudio:
523         if opts.audioformat not in ['best', 'aac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav']:
524             parser.error(u'invalid audio format specified')
525     if opts.audioquality:
526         opts.audioquality = opts.audioquality.strip('k').strip('K')
527         if not opts.audioquality.isdigit():
528             parser.error(u'invalid audio quality specified')
529     if opts.recodevideo is not None:
530         if opts.recodevideo not in ['mp4', 'flv', 'webm', 'ogg']:
531             parser.error(u'invalid video recode format specified')
532     if opts.date is not None:
533         date = DateRange.day(opts.date)
534     else:
535         date = DateRange(opts.dateafter, opts.datebefore)
536
537     # --all-sub automatically sets --write-sub if --write-auto-sub is not given
538     # this was the old behaviour if only --all-sub was given.
539     if opts.allsubtitles and (opts.writeautomaticsub == False):
540         opts.writesubtitles = True
541
542     if sys.version_info < (3,):
543         # In Python 2, sys.argv is a bytestring (also note http://bugs.python.org/issue2128 for Windows systems)
544         if opts.outtmpl is not None:
545             opts.outtmpl = opts.outtmpl.decode(preferredencoding())
546     outtmpl =((opts.outtmpl is not None and opts.outtmpl)
547             or (opts.format == '-1' and opts.usetitle and u'%(title)s-%(id)s-%(format)s.%(ext)s')
548             or (opts.format == '-1' and u'%(id)s-%(format)s.%(ext)s')
549             or (opts.usetitle and opts.autonumber and u'%(autonumber)s-%(title)s-%(id)s.%(ext)s')
550             or (opts.usetitle and u'%(title)s-%(id)s.%(ext)s')
551             or (opts.useid and u'%(id)s.%(ext)s')
552             or (opts.autonumber and u'%(autonumber)s-%(id)s.%(ext)s')
553             or u'%(title)s-%(id)s.%(ext)s')
554
555     # YoutubeDL
556     ydl = YoutubeDL({
557         'usenetrc': opts.usenetrc,
558         'username': opts.username,
559         'password': opts.password,
560         'videopassword': opts.videopassword,
561         'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
562         'forceurl': opts.geturl,
563         'forcetitle': opts.gettitle,
564         'forceid': opts.getid,
565         'forcethumbnail': opts.getthumbnail,
566         'forcedescription': opts.getdescription,
567         'forcefilename': opts.getfilename,
568         'forceformat': opts.getformat,
569         'simulate': opts.simulate,
570         'skip_download': (opts.skip_download or opts.simulate or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
571         'format': opts.format,
572         'format_limit': opts.format_limit,
573         'listformats': opts.listformats,
574         'outtmpl': outtmpl,
575         'autonumber_size': opts.autonumber_size,
576         'restrictfilenames': opts.restrictfilenames,
577         'ignoreerrors': opts.ignoreerrors,
578         'ratelimit': opts.ratelimit,
579         'nooverwrites': opts.nooverwrites,
580         'retries': opts.retries,
581         'buffersize': opts.buffersize,
582         'noresizebuffer': opts.noresizebuffer,
583         'continuedl': opts.continue_dl,
584         'noprogress': opts.noprogress,
585         'progress_with_newline': opts.progress_with_newline,
586         'playliststart': opts.playliststart,
587         'playlistend': opts.playlistend,
588         'logtostderr': opts.outtmpl == '-',
589         'consoletitle': opts.consoletitle,
590         'nopart': opts.nopart,
591         'updatetime': opts.updatetime,
592         'writedescription': opts.writedescription,
593         'writeinfojson': opts.writeinfojson,
594         'writethumbnail': opts.writethumbnail,
595         'writesubtitles': opts.writesubtitles,
596         'writeautomaticsub': opts.writeautomaticsub,
597         'allsubtitles': opts.allsubtitles,
598         'listsubtitles': opts.listsubtitles,
599         'subtitlesformat': opts.subtitlesformat,
600         'subtitleslangs': opts.subtitleslangs,
601         'matchtitle': decodeOption(opts.matchtitle),
602         'rejecttitle': decodeOption(opts.rejecttitle),
603         'max_downloads': opts.max_downloads,
604         'prefer_free_formats': opts.prefer_free_formats,
605         'verbose': opts.verbose,
606         'dump_intermediate_pages': opts.dump_intermediate_pages,
607         'test': opts.test,
608         'keepvideo': opts.keepvideo,
609         'min_filesize': opts.min_filesize,
610         'max_filesize': opts.max_filesize,
611         'daterange': date,
612         })
613
614     if opts.verbose:
615         write_string(u'[debug] youtube-dl version ' + __version__ + u'\n')
616         try:
617             sp = subprocess.Popen(
618                 ['git', 'rev-parse', '--short', 'HEAD'],
619                 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
620                 cwd=os.path.dirname(os.path.abspath(__file__)))
621             out, err = sp.communicate()
622             out = out.decode().strip()
623             if re.match('[0-9a-f]+', out):
624                 write_string(u'[debug] Git HEAD: ' + out + u'\n')
625         except:
626             try:
627                 sys.exc_clear()
628             except:
629                 pass
630         write_string(u'[debug] Python version %s - %s' %(platform.python_version(), platform_name()) + u'\n')
631         write_string(u'[debug] Proxy map: ' + str(proxy_handler.proxies) + u'\n')
632
633     ydl.add_default_info_extractors()
634
635     # PostProcessors
636     if opts.extractaudio:
637         ydl.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, nopostoverwrites=opts.nopostoverwrites))
638     if opts.recodevideo:
639         ydl.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo))
640     if opts.embedsubtitles:
641         ydl.add_post_processor(FFmpegEmbedSubtitlePP(subtitlesformat=opts.subtitlesformat))
642
643     # Update version
644     if opts.update_self:
645         update_self(ydl.to_screen, opts.verbose, sys.argv[0])
646
647     # Maybe do nothing
648     if len(all_urls) < 1:
649         if not opts.update_self:
650             parser.error(u'you must provide at least one URL')
651         else:
652             sys.exit()
653
654     try:
655         retcode = ydl.download(all_urls)
656     except MaxDownloadsReached:
657         ydl.to_screen(u'--max-download limit reached, aborting.')
658         retcode = 101
659
660     # Dump cookie jar if requested
661     if opts.cookiefile is not None:
662         try:
663             jar.save()
664         except (IOError, OSError) as err:
665             sys.exit(u'ERROR: unable to save cookie jar')
666
667     sys.exit(retcode)
668
669 def main(argv=None):
670     try:
671         _real_main(argv)
672     except DownloadError:
673         sys.exit(1)
674     except SameFileError:
675         sys.exit(u'ERROR: fixed output name but more than one file to download')
676     except KeyboardInterrupt:
677         sys.exit(u'\nERROR: Interrupted by user')