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