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