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