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