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