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