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