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