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