[youtube] Improve cache and add an option to print the extracted signatures
[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     general.add_option('--cache-dir', dest='cachedir', default=u'~/.youtube-dl/cache', help='Location in the filesystem where youtube-dl can store downloaded information permanently. NONE to disable filesystem caching, %default by default')
171
172
173     selection.add_option('--playlist-start',
174             dest='playliststart', metavar='NUMBER', help='playlist video to start at (default is %default)', default=1)
175     selection.add_option('--playlist-end',
176             dest='playlistend', metavar='NUMBER', help='playlist video to end at (default is last)', default=-1)
177     selection.add_option('--match-title', dest='matchtitle', metavar='REGEX',help='download only matching titles (regex or caseless sub-string)')
178     selection.add_option('--reject-title', dest='rejecttitle', metavar='REGEX',help='skip download for matching titles (regex or caseless sub-string)')
179     selection.add_option('--max-downloads', metavar='NUMBER', dest='max_downloads', help='Abort after downloading NUMBER files', default=None)
180     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)
181     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)
182     selection.add_option('--date', metavar='DATE', dest='date', help='download only videos uploaded in this date', default=None)
183     selection.add_option('--datebefore', metavar='DATE', dest='datebefore', help='download only videos uploaded before this date', default=None)
184     selection.add_option('--dateafter', metavar='DATE', dest='dateafter', help='download only videos uploaded after this date', default=None)
185
186
187     authentication.add_option('-u', '--username',
188             dest='username', metavar='USERNAME', help='account username')
189     authentication.add_option('-p', '--password',
190             dest='password', metavar='PASSWORD', help='account password')
191     authentication.add_option('-n', '--netrc',
192             action='store_true', dest='usenetrc', help='use .netrc authentication data', default=False)
193     authentication.add_option('--video-password',
194             dest='videopassword', metavar='PASSWORD', help='video password (vimeo only)')
195
196
197     video_format.add_option('-f', '--format',
198             action='store', dest='format', metavar='FORMAT',
199             help='video format code, specifiy the order of preference using slashes: "-f 22/17/18". "-f mp4" and "-f flv" are also supported')
200     video_format.add_option('--all-formats',
201             action='store_const', dest='format', help='download all available video formats', const='all')
202     video_format.add_option('--prefer-free-formats',
203             action='store_true', dest='prefer_free_formats', default=False, help='prefer free video formats unless a specific one is requested')
204     video_format.add_option('--max-quality',
205             action='store', dest='format_limit', metavar='FORMAT', help='highest quality format to download')
206     video_format.add_option('-F', '--list-formats',
207             action='store_true', dest='listformats', help='list all available formats (currently youtube only)')
208
209     subtitles.add_option('--write-sub', '--write-srt',
210             action='store_true', dest='writesubtitles',
211             help='write subtitle file', default=False)
212     subtitles.add_option('--write-auto-sub', '--write-automatic-sub',
213             action='store_true', dest='writeautomaticsub',
214             help='write automatic subtitle file (youtube only)', default=False)
215     subtitles.add_option('--all-subs',
216             action='store_true', dest='allsubtitles',
217             help='downloads all the available subtitles of the video', default=False)
218     subtitles.add_option('--list-subs',
219             action='store_true', dest='listsubtitles',
220             help='lists all available subtitles for the video', default=False)
221     subtitles.add_option('--sub-format',
222             action='store', dest='subtitlesformat', metavar='FORMAT',
223             help='subtitle format (default=srt) ([sbv/vtt] youtube only)', default='srt')
224     subtitles.add_option('--sub-lang', '--sub-langs', '--srt-lang',
225             action='callback', dest='subtitleslangs', metavar='LANGS', type='str',
226             default=[], callback=_comma_separated_values_options_callback,
227             help='languages of the subtitles to download (optional) separated by commas, use IETF language tags like \'en,pt\'')
228
229     downloader.add_option('-r', '--rate-limit',
230             dest='ratelimit', metavar='LIMIT', help='maximum download rate (e.g. 50k or 44.6m)')
231     downloader.add_option('-R', '--retries',
232             dest='retries', metavar='RETRIES', help='number of retries (default is %default)', default=10)
233     downloader.add_option('--buffer-size',
234             dest='buffersize', metavar='SIZE', help='size of download buffer (e.g. 1024 or 16k) (default is %default)', default="1024")
235     downloader.add_option('--no-resize-buffer',
236             action='store_true', dest='noresizebuffer',
237             help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.', default=False)
238     downloader.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
239
240     verbosity.add_option('-q', '--quiet',
241             action='store_true', dest='quiet', help='activates quiet mode', default=False)
242     verbosity.add_option('-s', '--simulate',
243             action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False)
244     verbosity.add_option('--skip-download',
245             action='store_true', dest='skip_download', help='do not download the video', default=False)
246     verbosity.add_option('-g', '--get-url',
247             action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
248     verbosity.add_option('-e', '--get-title',
249             action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
250     verbosity.add_option('--get-id',
251             action='store_true', dest='getid', help='simulate, quiet but print id', default=False)
252     verbosity.add_option('--get-thumbnail',
253             action='store_true', dest='getthumbnail',
254             help='simulate, quiet but print thumbnail URL', default=False)
255     verbosity.add_option('--get-description',
256             action='store_true', dest='getdescription',
257             help='simulate, quiet but print video description', default=False)
258     verbosity.add_option('--get-filename',
259             action='store_true', dest='getfilename',
260             help='simulate, quiet but print output filename', default=False)
261     verbosity.add_option('--get-format',
262             action='store_true', dest='getformat',
263             help='simulate, quiet but print output format', default=False)
264     verbosity.add_option('--newline',
265             action='store_true', dest='progress_with_newline', help='output progress bar as new lines', default=False)
266     verbosity.add_option('--no-progress',
267             action='store_true', dest='noprogress', help='do not print progress bar', default=False)
268     verbosity.add_option('--console-title',
269             action='store_true', dest='consoletitle',
270             help='display progress in console titlebar', default=False)
271     verbosity.add_option('-v', '--verbose',
272             action='store_true', dest='verbose', help='print various debugging information', default=False)
273     verbosity.add_option('--dump-intermediate-pages',
274             action='store_true', dest='dump_intermediate_pages', default=False,
275             help='print downloaded pages to debug problems(very verbose)')
276     verbosity.add_option('--youtube-print-sig-code',
277             action='store_true', dest='youtube_print_sig_code', default=False,
278             help=optparse.SUPPRESS_HELP)
279
280
281     filesystem.add_option('-t', '--title',
282             action='store_true', dest='usetitle', help='use title in file name (default)', default=False)
283     filesystem.add_option('--id',
284             action='store_true', dest='useid', help='use only video ID in file name', default=False)
285     filesystem.add_option('-l', '--literal',
286             action='store_true', dest='usetitle', help='[deprecated] alias of --title', default=False)
287     filesystem.add_option('-A', '--auto-number',
288             action='store_true', dest='autonumber',
289             help='number downloaded files starting from 00000', default=False)
290     filesystem.add_option('-o', '--output',
291             dest='outtmpl', metavar='TEMPLATE',
292             help=('output filename template. Use %(title)s to get the title, '
293                   '%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, '
294                   '%(autonumber)s to get an automatically incremented number, '
295                   '%(ext)s for the filename extension, %(upload_date)s for the upload date (YYYYMMDD), '
296                   '%(extractor)s for the provider (youtube, metacafe, etc), '
297                   '%(id)s for the video id , %(playlist)s for the playlist the video is in, '
298                   '%(playlist_index)s for the position in the playlist and %% for a literal percent. '
299                   'Use - to output to stdout. Can also be used to download to a different directory, '
300                   'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .'))
301     filesystem.add_option('--autonumber-size',
302             dest='autonumber_size', metavar='NUMBER',
303             help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --autonumber option is given')
304     filesystem.add_option('--restrict-filenames',
305             action='store_true', dest='restrictfilenames',
306             help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False)
307     filesystem.add_option('-a', '--batch-file',
308             dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)')
309     filesystem.add_option('-w', '--no-overwrites',
310             action='store_true', dest='nooverwrites', help='do not overwrite files', default=False)
311     filesystem.add_option('-c', '--continue',
312             action='store_true', dest='continue_dl', help='resume partially downloaded files', default=True)
313     filesystem.add_option('--no-continue',
314             action='store_false', dest='continue_dl',
315             help='do not resume partially downloaded files (restart from beginning)')
316     filesystem.add_option('--cookies',
317             dest='cookiefile', metavar='FILE', help='file to read cookies from and dump cookie jar in')
318     filesystem.add_option('--no-part',
319             action='store_true', dest='nopart', help='do not use .part files', default=False)
320     filesystem.add_option('--no-mtime',
321             action='store_false', dest='updatetime',
322             help='do not use the Last-modified header to set the file modification time', default=True)
323     filesystem.add_option('--write-description',
324             action='store_true', dest='writedescription',
325             help='write video description to a .description file', default=False)
326     filesystem.add_option('--write-info-json',
327             action='store_true', dest='writeinfojson',
328             help='write video metadata to a .info.json file', default=False)
329     filesystem.add_option('--write-thumbnail',
330             action='store_true', dest='writethumbnail',
331             help='write thumbnail image to disk', default=False)
332
333
334     postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
335             help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)')
336     postproc.add_option('--audio-format', metavar='FORMAT', dest='audioformat', default='best',
337             help='"best", "aac", "vorbis", "mp3", "m4a", "opus", or "wav"; best by default')
338     postproc.add_option('--audio-quality', metavar='QUALITY', dest='audioquality', default='5',
339             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)')
340     postproc.add_option('--recode-video', metavar='FORMAT', dest='recodevideo', default=None,
341             help='Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm)')
342     postproc.add_option('-k', '--keep-video', action='store_true', dest='keepvideo', default=False,
343             help='keeps the video file on disk after the post-processing; the video is erased by default')
344     postproc.add_option('--no-post-overwrites', action='store_true', dest='nopostoverwrites', default=False,
345             help='do not overwrite post-processed files; the post-processed files are overwritten by default')
346     postproc.add_option('--embed-subs', action='store_true', dest='embedsubtitles', default=False,
347             help='embed subtitles in the video (only for mp4 videos)')
348
349
350     parser.add_option_group(general)
351     parser.add_option_group(selection)
352     parser.add_option_group(downloader)
353     parser.add_option_group(filesystem)
354     parser.add_option_group(verbosity)
355     parser.add_option_group(video_format)
356     parser.add_option_group(subtitles)
357     parser.add_option_group(authentication)
358     parser.add_option_group(postproc)
359
360     if overrideArguments is not None:
361         opts, args = parser.parse_args(overrideArguments)
362         if opts.verbose:
363             write_string(u'[debug] Override config: ' + repr(overrideArguments) + '\n')
364     else:
365         xdg_config_home = os.environ.get('XDG_CONFIG_HOME')
366         if xdg_config_home:
367             userConfFile = os.path.join(xdg_config_home, 'youtube-dl.conf')
368         else:
369             userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf')
370         systemConf = _readOptions('/etc/youtube-dl.conf')
371         userConf = _readOptions(userConfFile)
372         commandLineConf = sys.argv[1:]
373         argv = systemConf + userConf + commandLineConf
374         opts, args = parser.parse_args(argv)
375         if opts.verbose:
376             write_string(u'[debug] System config: ' + repr(_hide_login_info(systemConf)) + '\n')
377             write_string(u'[debug] User config: ' + repr(_hide_login_info(userConf)) + '\n')
378             write_string(u'[debug] Command-line args: ' + repr(_hide_login_info(commandLineConf)) + '\n')
379
380     return parser, opts, args
381
382 def _real_main(argv=None):
383     # Compatibility fixes for Windows
384     if sys.platform == 'win32':
385         # https://github.com/rg3/youtube-dl/issues/820
386         codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)
387
388     parser, opts, args = parseOpts(argv)
389
390     # Open appropriate CookieJar
391     if opts.cookiefile is None:
392         jar = compat_cookiejar.CookieJar()
393     else:
394         try:
395             jar = compat_cookiejar.MozillaCookieJar(opts.cookiefile)
396             if os.access(opts.cookiefile, os.R_OK):
397                 jar.load()
398         except (IOError, OSError) as err:
399             if opts.verbose:
400                 traceback.print_exc()
401             write_string(u'ERROR: unable to open cookie file\n')
402             sys.exit(101)
403     # Set user agent
404     if opts.user_agent is not None:
405         std_headers['User-Agent'] = opts.user_agent
406
407     # Set referer
408     if opts.referer is not None:
409         std_headers['Referer'] = opts.referer
410
411     # Dump user agent
412     if opts.dump_user_agent:
413         compat_print(std_headers['User-Agent'])
414         sys.exit(0)
415
416     # Batch file verification
417     batchurls = []
418     if opts.batchfile is not None:
419         try:
420             if opts.batchfile == '-':
421                 batchfd = sys.stdin
422             else:
423                 batchfd = open(opts.batchfile, 'r')
424             batchurls = batchfd.readlines()
425             batchurls = [x.strip() for x in batchurls]
426             batchurls = [x for x in batchurls if len(x) > 0 and not re.search(r'^[#/;]', x)]
427             if opts.verbose:
428                 write_string(u'[debug] Batch file urls: ' + repr(batchurls) + u'\n')
429         except IOError:
430             sys.exit(u'ERROR: batch file could not be read')
431     all_urls = batchurls + args
432     all_urls = [url.strip() for url in all_urls]
433
434     # General configuration
435     cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
436     if opts.proxy is not None:
437         if opts.proxy == '':
438             proxies = {}
439         else:
440             proxies = {'http': opts.proxy, 'https': opts.proxy}
441     else:
442         proxies = compat_urllib_request.getproxies()
443         # Set HTTPS proxy to HTTP one if given (https://github.com/rg3/youtube-dl/issues/805)
444         if 'http' in proxies and 'https' not in proxies:
445             proxies['https'] = proxies['http']
446     proxy_handler = compat_urllib_request.ProxyHandler(proxies)
447     https_handler = make_HTTPS_handler(opts)
448     opener = compat_urllib_request.build_opener(https_handler, proxy_handler, cookie_processor, YoutubeDLHandler())
449     # Delete the default user-agent header, which would otherwise apply in
450     # cases where our custom HTTP handler doesn't come into play
451     # (See https://github.com/rg3/youtube-dl/issues/1309 for details)
452     opener.addheaders =[]
453     compat_urllib_request.install_opener(opener)
454     socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
455
456     extractors = gen_extractors()
457
458     if opts.list_extractors:
459         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
460             compat_print(ie.IE_NAME + (' (CURRENTLY BROKEN)' if not ie._WORKING else ''))
461             matchedUrls = [url for url in all_urls if ie.suitable(url)]
462             all_urls = [url for url in all_urls if url not in matchedUrls]
463             for mu in matchedUrls:
464                 compat_print(u'  ' + mu)
465         sys.exit(0)
466     if opts.list_extractor_descriptions:
467         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
468             if not ie._WORKING:
469                 continue
470             desc = getattr(ie, 'IE_DESC', ie.IE_NAME)
471             if hasattr(ie, 'SEARCH_KEY'):
472                 _SEARCHES = (u'cute kittens', u'slithering pythons', u'falling cat', u'angry poodle', u'purple fish', u'running tortoise')
473                 _COUNTS = (u'', u'5', u'10', u'all')
474                 desc += u' (Example: "%s%s:%s" )' % (ie.SEARCH_KEY, random.choice(_COUNTS), random.choice(_SEARCHES))
475             compat_print(desc)
476         sys.exit(0)
477
478
479     # Conflicting, missing and erroneous options
480     if opts.usenetrc and (opts.username is not None or opts.password is not None):
481         parser.error(u'using .netrc conflicts with giving username/password')
482     if opts.password is not None and opts.username is None:
483         parser.error(u' account username missing\n')
484     if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
485         parser.error(u'using output template conflicts with using title, video ID or auto number')
486     if opts.usetitle and opts.useid:
487         parser.error(u'using title conflicts with using video ID')
488     if opts.username is not None and opts.password is None:
489         opts.password = getpass.getpass(u'Type account password and press return:')
490     if opts.ratelimit is not None:
491         numeric_limit = FileDownloader.parse_bytes(opts.ratelimit)
492         if numeric_limit is None:
493             parser.error(u'invalid rate limit specified')
494         opts.ratelimit = numeric_limit
495     if opts.min_filesize is not None:
496         numeric_limit = FileDownloader.parse_bytes(opts.min_filesize)
497         if numeric_limit is None:
498             parser.error(u'invalid min_filesize specified')
499         opts.min_filesize = numeric_limit
500     if opts.max_filesize is not None:
501         numeric_limit = FileDownloader.parse_bytes(opts.max_filesize)
502         if numeric_limit is None:
503             parser.error(u'invalid max_filesize specified')
504         opts.max_filesize = numeric_limit
505     if opts.retries is not None:
506         try:
507             opts.retries = int(opts.retries)
508         except (TypeError, ValueError) as err:
509             parser.error(u'invalid retry count specified')
510     if opts.buffersize is not None:
511         numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize)
512         if numeric_buffersize is None:
513             parser.error(u'invalid buffer size specified')
514         opts.buffersize = numeric_buffersize
515     try:
516         opts.playliststart = int(opts.playliststart)
517         if opts.playliststart <= 0:
518             raise ValueError(u'Playlist start must be positive')
519     except (TypeError, ValueError) as err:
520         parser.error(u'invalid playlist start number specified')
521     try:
522         opts.playlistend = int(opts.playlistend)
523         if opts.playlistend != -1 and (opts.playlistend <= 0 or opts.playlistend < opts.playliststart):
524             raise ValueError(u'Playlist end must be greater than playlist start')
525     except (TypeError, ValueError) as err:
526         parser.error(u'invalid playlist end number specified')
527     if opts.extractaudio:
528         if opts.audioformat not in ['best', 'aac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav']:
529             parser.error(u'invalid audio format specified')
530     if opts.audioquality:
531         opts.audioquality = opts.audioquality.strip('k').strip('K')
532         if not opts.audioquality.isdigit():
533             parser.error(u'invalid audio quality specified')
534     if opts.recodevideo is not None:
535         if opts.recodevideo not in ['mp4', 'flv', 'webm', 'ogg']:
536             parser.error(u'invalid video recode format specified')
537     if opts.date is not None:
538         date = DateRange.day(opts.date)
539     else:
540         date = DateRange(opts.dateafter, opts.datebefore)
541
542     # --all-sub automatically sets --write-sub if --write-auto-sub is not given
543     # this was the old behaviour if only --all-sub was given.
544     if opts.allsubtitles and (opts.writeautomaticsub == False):
545         opts.writesubtitles = True
546
547     if sys.version_info < (3,):
548         # In Python 2, sys.argv is a bytestring (also note http://bugs.python.org/issue2128 for Windows systems)
549         if opts.outtmpl is not None:
550             opts.outtmpl = opts.outtmpl.decode(preferredencoding())
551     outtmpl =((opts.outtmpl is not None and opts.outtmpl)
552             or (opts.format == '-1' and opts.usetitle and u'%(title)s-%(id)s-%(format)s.%(ext)s')
553             or (opts.format == '-1' and u'%(id)s-%(format)s.%(ext)s')
554             or (opts.usetitle and opts.autonumber and u'%(autonumber)s-%(title)s-%(id)s.%(ext)s')
555             or (opts.usetitle and u'%(title)s-%(id)s.%(ext)s')
556             or (opts.useid and u'%(id)s.%(ext)s')
557             or (opts.autonumber and u'%(autonumber)s-%(id)s.%(ext)s')
558             or u'%(title)s-%(id)s.%(ext)s')
559     if '%(ext)s' not in outtmpl and opts.extractaudio:
560         parser.error(u'Cannot download a video and extract audio into the same'
561                      u' file! Use "%%(ext)s" instead of %r' %
562                      determine_ext(outtmpl, u''))
563
564     # YoutubeDL
565     ydl = YoutubeDL({
566         'usenetrc': opts.usenetrc,
567         'username': opts.username,
568         'password': opts.password,
569         'videopassword': opts.videopassword,
570         'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
571         'forceurl': opts.geturl,
572         'forcetitle': opts.gettitle,
573         'forceid': opts.getid,
574         'forcethumbnail': opts.getthumbnail,
575         'forcedescription': opts.getdescription,
576         'forcefilename': opts.getfilename,
577         'forceformat': opts.getformat,
578         'simulate': opts.simulate,
579         '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),
580         'format': opts.format,
581         'format_limit': opts.format_limit,
582         'listformats': opts.listformats,
583         'outtmpl': outtmpl,
584         'autonumber_size': opts.autonumber_size,
585         'restrictfilenames': opts.restrictfilenames,
586         'ignoreerrors': opts.ignoreerrors,
587         'ratelimit': opts.ratelimit,
588         'nooverwrites': opts.nooverwrites,
589         'retries': opts.retries,
590         'buffersize': opts.buffersize,
591         'noresizebuffer': opts.noresizebuffer,
592         'continuedl': opts.continue_dl,
593         'noprogress': opts.noprogress,
594         'progress_with_newline': opts.progress_with_newline,
595         'playliststart': opts.playliststart,
596         'playlistend': opts.playlistend,
597         'logtostderr': opts.outtmpl == '-',
598         'consoletitle': opts.consoletitle,
599         'nopart': opts.nopart,
600         'updatetime': opts.updatetime,
601         'writedescription': opts.writedescription,
602         'writeinfojson': opts.writeinfojson,
603         'writethumbnail': opts.writethumbnail,
604         'writesubtitles': opts.writesubtitles,
605         'writeautomaticsub': opts.writeautomaticsub,
606         'allsubtitles': opts.allsubtitles,
607         'listsubtitles': opts.listsubtitles,
608         'subtitlesformat': opts.subtitlesformat,
609         'subtitleslangs': opts.subtitleslangs,
610         'matchtitle': decodeOption(opts.matchtitle),
611         'rejecttitle': decodeOption(opts.rejecttitle),
612         'max_downloads': opts.max_downloads,
613         'prefer_free_formats': opts.prefer_free_formats,
614         'verbose': opts.verbose,
615         'dump_intermediate_pages': opts.dump_intermediate_pages,
616         'test': opts.test,
617         'keepvideo': opts.keepvideo,
618         'min_filesize': opts.min_filesize,
619         'max_filesize': opts.max_filesize,
620         'daterange': date,
621         'youtube_print_sig_code': opts.youtube_print_sig_code
622         })
623
624     if opts.verbose:
625         write_string(u'[debug] youtube-dl version ' + __version__ + u'\n')
626         try:
627             sp = subprocess.Popen(
628                 ['git', 'rev-parse', '--short', 'HEAD'],
629                 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
630                 cwd=os.path.dirname(os.path.abspath(__file__)))
631             out, err = sp.communicate()
632             out = out.decode().strip()
633             if re.match('[0-9a-f]+', out):
634                 write_string(u'[debug] Git HEAD: ' + out + u'\n')
635         except:
636             try:
637                 sys.exc_clear()
638             except:
639                 pass
640         write_string(u'[debug] Python version %s - %s' %(platform.python_version(), platform_name()) + u'\n')
641         write_string(u'[debug] Proxy map: ' + str(proxy_handler.proxies) + u'\n')
642
643     ydl.add_default_info_extractors()
644
645     # PostProcessors
646     if opts.extractaudio:
647         ydl.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, nopostoverwrites=opts.nopostoverwrites))
648     if opts.recodevideo:
649         ydl.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo))
650     if opts.embedsubtitles:
651         ydl.add_post_processor(FFmpegEmbedSubtitlePP(subtitlesformat=opts.subtitlesformat))
652
653     # Update version
654     if opts.update_self:
655         update_self(ydl.to_screen, opts.verbose, sys.argv[0])
656
657     # Maybe do nothing
658     if len(all_urls) < 1:
659         if not opts.update_self:
660             parser.error(u'you must provide at least one URL')
661         else:
662             sys.exit()
663
664     try:
665         retcode = ydl.download(all_urls)
666     except MaxDownloadsReached:
667         ydl.to_screen(u'--max-download limit reached, aborting.')
668         retcode = 101
669
670     # Dump cookie jar if requested
671     if opts.cookiefile is not None:
672         try:
673             jar.save()
674         except (IOError, OSError) as err:
675             sys.exit(u'ERROR: unable to save cookie jar')
676
677     sys.exit(retcode)
678
679 def main(argv=None):
680     try:
681         _real_main(argv)
682     except DownloadError:
683         sys.exit(1)
684     except SameFileError:
685         sys.exit(u'ERROR: fixed output name but more than one file to download')
686     except KeyboardInterrupt:
687         sys.exit(u'\nERROR: Interrupted by user')