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