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