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