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