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