37d6f8cfdce5fe8ec6475ac7a6e8eac02fff76bb
[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     )
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.')
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')