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