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