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