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