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