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