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