]> Raphaël G. Git Repositories - youtubedl/blob - youtube_dl/__init__.py
debian/README.source: Update my own instructions.
[youtubedl] / 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 GenericIE()
402 ]
403
404 def _real_main():
405 parser, opts, args = parseOpts()
406
407 # Open appropriate CookieJar
408 if opts.cookiefile is None:
409 jar = compat_cookiejar.CookieJar()
410 else:
411 try:
412 jar = compat_cookiejar.MozillaCookieJar(opts.cookiefile)
413 if os.path.isfile(opts.cookiefile) and os.access(opts.cookiefile, os.R_OK):
414 jar.load()
415 except (IOError, OSError) as err:
416 sys.exit(u'ERROR: unable to open cookie file')
417 # Set user agent
418 if opts.user_agent is not None:
419 std_headers['User-Agent'] = opts.user_agent
420
421 # Dump user agent
422 if opts.dump_user_agent:
423 print(std_headers['User-Agent'])
424 sys.exit(0)
425
426 # Batch file verification
427 batchurls = []
428 if opts.batchfile is not None:
429 try:
430 if opts.batchfile == '-':
431 batchfd = sys.stdin
432 else:
433 batchfd = open(opts.batchfile, 'r')
434 batchurls = batchfd.readlines()
435 batchurls = [x.strip() for x in batchurls]
436 batchurls = [x for x in batchurls if len(x) > 0 and not re.search(r'^[#/;]', x)]
437 except IOError:
438 sys.exit(u'ERROR: batch file could not be read')
439 all_urls = batchurls + args
440 all_urls = [url.strip() for url in all_urls]
441
442 # General configuration
443 cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
444 proxy_handler = compat_urllib_request.ProxyHandler()
445 opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
446 compat_urllib_request.install_opener(opener)
447 socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
448
449 extractors = gen_extractors()
450
451 if opts.list_extractors:
452 for ie in extractors:
453 print(ie.IE_NAME + (' (CURRENTLY BROKEN)' if not ie._WORKING else ''))
454 matchedUrls = filter(lambda url: ie.suitable(url), all_urls)
455 all_urls = filter(lambda url: url not in matchedUrls, all_urls)
456 for mu in matchedUrls:
457 print(u' ' + mu)
458 sys.exit(0)
459
460 # Conflicting, missing and erroneous options
461 if opts.usenetrc and (opts.username is not None or opts.password is not None):
462 parser.error(u'using .netrc conflicts with giving username/password')
463 if opts.password is not None and opts.username is None:
464 parser.error(u'account username missing')
465 if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
466 parser.error(u'using output template conflicts with using title, video ID or auto number')
467 if opts.usetitle and opts.useid:
468 parser.error(u'using title conflicts with using video ID')
469 if opts.username is not None and opts.password is None:
470 opts.password = getpass.getpass(u'Type account password and press return:')
471 if opts.ratelimit is not None:
472 numeric_limit = FileDownloader.parse_bytes(opts.ratelimit)
473 if numeric_limit is None:
474 parser.error(u'invalid rate limit specified')
475 opts.ratelimit = numeric_limit
476 if opts.retries is not None:
477 try:
478 opts.retries = int(opts.retries)
479 except (TypeError, ValueError) as err:
480 parser.error(u'invalid retry count specified')
481 if opts.buffersize is not None:
482 numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize)
483 if numeric_buffersize is None:
484 parser.error(u'invalid buffer size specified')
485 opts.buffersize = numeric_buffersize
486 try:
487 opts.playliststart = int(opts.playliststart)
488 if opts.playliststart <= 0:
489 raise ValueError(u'Playlist start must be positive')
490 except (TypeError, ValueError) as err:
491 parser.error(u'invalid playlist start number specified')
492 try:
493 opts.playlistend = int(opts.playlistend)
494 if opts.playlistend != -1 and (opts.playlistend <= 0 or opts.playlistend < opts.playliststart):
495 raise ValueError(u'Playlist end must be greater than playlist start')
496 except (TypeError, ValueError) as err:
497 parser.error(u'invalid playlist end number specified')
498 if opts.extractaudio:
499 if opts.audioformat not in ['best', 'aac', 'mp3', 'vorbis', 'm4a', 'wav']:
500 parser.error(u'invalid audio format specified')
501 if opts.audioquality:
502 opts.audioquality = opts.audioquality.strip('k').strip('K')
503 if not opts.audioquality.isdigit():
504 parser.error(u'invalid audio quality specified')
505
506 # File downloader
507 fd = FileDownloader({
508 'usenetrc': opts.usenetrc,
509 'username': opts.username,
510 'password': opts.password,
511 'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
512 'forceurl': opts.geturl,
513 'forcetitle': opts.gettitle,
514 'forcethumbnail': opts.getthumbnail,
515 'forcedescription': opts.getdescription,
516 'forcefilename': opts.getfilename,
517 'forceformat': opts.getformat,
518 'simulate': opts.simulate,
519 '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),
520 'format': opts.format,
521 'format_limit': opts.format_limit,
522 'listformats': opts.listformats,
523 'outtmpl': ((opts.outtmpl is not None and opts.outtmpl.decode(preferredencoding()))
524 or (opts.format == '-1' and opts.usetitle and u'%(title)s-%(id)s-%(format)s.%(ext)s')
525 or (opts.format == '-1' and u'%(id)s-%(format)s.%(ext)s')
526 or (opts.usetitle and opts.autonumber and u'%(autonumber)s-%(title)s-%(id)s.%(ext)s')
527 or (opts.usetitle and u'%(title)s-%(id)s.%(ext)s')
528 or (opts.useid and u'%(id)s.%(ext)s')
529 or (opts.autonumber and u'%(autonumber)s-%(id)s.%(ext)s')
530 or u'%(id)s.%(ext)s'),
531 'restrictfilenames': opts.restrictfilenames,
532 'ignoreerrors': opts.ignoreerrors,
533 'ratelimit': opts.ratelimit,
534 'nooverwrites': opts.nooverwrites,
535 'retries': opts.retries,
536 'buffersize': opts.buffersize,
537 'noresizebuffer': opts.noresizebuffer,
538 'continuedl': opts.continue_dl,
539 'noprogress': opts.noprogress,
540 'playliststart': opts.playliststart,
541 'playlistend': opts.playlistend,
542 'logtostderr': opts.outtmpl == '-',
543 'consoletitle': opts.consoletitle,
544 'nopart': opts.nopart,
545 'updatetime': opts.updatetime,
546 'writedescription': opts.writedescription,
547 'writeinfojson': opts.writeinfojson,
548 'writesubtitles': opts.writesubtitles,
549 'subtitleslang': opts.subtitleslang,
550 'matchtitle': opts.matchtitle,
551 'rejecttitle': opts.rejecttitle,
552 'max_downloads': opts.max_downloads,
553 'prefer_free_formats': opts.prefer_free_formats,
554 'verbose': opts.verbose,
555 'test': opts.test,
556 })
557
558 if opts.verbose:
559 fd.to_screen(u'[debug] Proxy map: ' + str(proxy_handler.proxies))
560
561 for extractor in extractors:
562 fd.add_info_extractor(extractor)
563
564 # PostProcessors
565 if opts.extractaudio:
566 fd.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, keepvideo=opts.keepvideo))
567
568 # Update version
569 if opts.update_self:
570 updateSelf(fd, sys.argv[0])
571
572 # Maybe do nothing
573 if len(all_urls) < 1:
574 if not opts.update_self:
575 parser.error(u'you must provide at least one URL')
576 else:
577 sys.exit()
578
579 try:
580 retcode = fd.download(all_urls)
581 except MaxDownloadsReached:
582 fd.to_screen(u'--max-download limit reached, aborting.')
583 retcode = 101
584
585 # Dump cookie jar if requested
586 if opts.cookiefile is not None:
587 try:
588 jar.save()
589 except (IOError, OSError) as err:
590 sys.exit(u'ERROR: unable to save cookie jar')
591
592 sys.exit(retcode)
593
594 def main():
595 try:
596 _real_main()
597 except DownloadError:
598 sys.exit(1)
599 except SameFileError:
600 sys.exit(u'ERROR: fixed output name but more than one file to download')
601 except KeyboardInterrupt:
602 sys.exit(u'\nERROR: Interrupted by user')