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