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