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