Document %(format)s (#1612)
[youtube-dl] / youtube_dl / __init__.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 __authors__  = (
5     'Ricardo Garcia Gonzalez',
6     'Danny Colligan',
7     'Benjamin Johnson',
8     'Vasyl\' Vavrychuk',
9     'Witold Baryluk',
10     'Paweł Paprota',
11     'Gergely Imreh',
12     'Rogério Brito',
13     'Philipp Hagemeister',
14     'Sören Schulze',
15     'Kevin Ngo',
16     'Ori Avtalion',
17     'shizeeg',
18     'Filippo Valsorda',
19     'Christian Albrecht',
20     'Dave Vasilevsky',
21     'Jaime Marquínez Ferrándiz',
22     'Jeff Crouse',
23     'Osama Khalid',
24     'Michael Walter',
25     'M. Yasoob Ullah Khalid',
26     'Julien Fraichard',
27     'Johny Mo Swag',
28     'Axel Noack',
29     'Albert Kim',
30     'Pierre Rudloff',
31     'Huarong Huo',
32     'Ismael Mejía',
33     'Steffan \'Ruirize\' James',
34     'Andras Elso',
35 )
36
37 __license__ = 'Public Domain'
38
39 import codecs
40 import collections
41 import getpass
42 import optparse
43 import os
44 import random
45 import re
46 import shlex
47 import socket
48 import subprocess
49 import sys
50 import traceback
51 import platform
52
53
54 from .utils import (
55     compat_cookiejar,
56     compat_print,
57     compat_str,
58     compat_urllib_request,
59     DateRange,
60     decodeOption,
61     determine_ext,
62     DownloadError,
63     get_cachedir,
64     make_HTTPS_handler,
65     MaxDownloadsReached,
66     platform_name,
67     preferredencoding,
68     SameFileError,
69     std_headers,
70     write_string,
71     YoutubeDLHandler,
72 )
73 from .update import update_self
74 from .version import __version__
75 from .FileDownloader import (
76     FileDownloader,
77 )
78 from .extractor import gen_extractors
79 from .YoutubeDL import YoutubeDL
80 from .PostProcessor import (
81     FFmpegMetadataPP,
82     FFmpegVideoConvertor,
83     FFmpegExtractAudioPP,
84     FFmpegEmbedSubtitlePP,
85 )
86
87
88 def parseOpts(overrideArguments=None):
89     def _readOptions(filename_bytes):
90         try:
91             optionf = open(filename_bytes)
92         except IOError:
93             return [] # silently skip if file is not present
94         try:
95             res = []
96             for l in optionf:
97                 res += shlex.split(l, comments=True)
98         finally:
99             optionf.close()
100         return res
101
102     def _format_option_string(option):
103         ''' ('-o', '--option') -> -o, --format METAVAR'''
104
105         opts = []
106
107         if option._short_opts:
108             opts.append(option._short_opts[0])
109         if option._long_opts:
110             opts.append(option._long_opts[0])
111         if len(opts) > 1:
112             opts.insert(1, ', ')
113
114         if option.takes_value(): opts.append(' %s' % option.metavar)
115
116         return "".join(opts)
117
118     def _comma_separated_values_options_callback(option, opt_str, value, parser):
119         setattr(parser.values, option.dest, value.split(','))
120
121     def _find_term_columns():
122         columns = os.environ.get('COLUMNS', None)
123         if columns:
124             return int(columns)
125
126         try:
127             sp = subprocess.Popen(['stty', 'size'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
128             out,err = sp.communicate()
129             return int(out.split()[1])
130         except:
131             pass
132         return None
133
134     def _hide_login_info(opts):
135         opts = list(opts)
136         for private_opt in ['-p', '--password', '-u', '--username']:
137             try:
138                 i = opts.index(private_opt)
139                 opts[i+1] = '<PRIVATE>'
140             except ValueError:
141                 pass
142         return opts
143
144     max_width = 80
145     max_help_position = 80
146
147     # No need to wrap help messages if we're on a wide console
148     columns = _find_term_columns()
149     if columns: max_width = columns
150
151     fmt = optparse.IndentedHelpFormatter(width=max_width, max_help_position=max_help_position)
152     fmt.format_option_strings = _format_option_string
153
154     kw = {
155         'version'   : __version__,
156         'formatter' : fmt,
157         'usage' : '%prog [options] url [url...]',
158         'conflict_handler' : 'resolve',
159     }
160
161     parser = optparse.OptionParser(**kw)
162
163     # option groups
164     general        = optparse.OptionGroup(parser, 'General Options')
165     selection      = optparse.OptionGroup(parser, 'Video Selection')
166     authentication = optparse.OptionGroup(parser, 'Authentication Options')
167     video_format   = optparse.OptionGroup(parser, 'Video Format Options')
168     subtitles      = optparse.OptionGroup(parser, 'Subtitle Options')
169     downloader     = optparse.OptionGroup(parser, 'Download Options')
170     postproc       = optparse.OptionGroup(parser, 'Post-processing Options')
171     filesystem     = optparse.OptionGroup(parser, 'Filesystem Options')
172     verbosity      = optparse.OptionGroup(parser, 'Verbosity / Simulation Options')
173
174     general.add_option('-h', '--help',
175             action='help', help='print this help text and exit')
176     general.add_option('-v', '--version',
177             action='version', help='print program version and exit')
178     general.add_option('-U', '--update',
179             action='store_true', dest='update_self', help='update this program to latest version. Make sure that you have sufficient permissions (run with sudo if needed)')
180     general.add_option('-i', '--ignore-errors',
181             action='store_true', dest='ignoreerrors', help='continue on download errors, for example to to skip unavailable videos in a playlist', default=False)
182     general.add_option('--dump-user-agent',
183             action='store_true', dest='dump_user_agent',
184             help='display the current browser identification', default=False)
185     general.add_option('--user-agent',
186             dest='user_agent', help='specify a custom user agent', metavar='UA')
187     general.add_option('--referer',
188             dest='referer', help='specify a custom referer, use if the video access is restricted to one domain',
189             metavar='REF', default=None)
190     general.add_option('--list-extractors',
191             action='store_true', dest='list_extractors',
192             help='List all supported extractors and the URLs they would handle', default=False)
193     general.add_option('--extractor-descriptions',
194             action='store_true', dest='list_extractor_descriptions',
195             help='Output descriptions of all supported extractors', default=False)
196     general.add_option('--proxy', dest='proxy', default=None, help='Use the specified HTTP/HTTPS proxy', metavar='URL')
197     general.add_option('--no-check-certificate', action='store_true', dest='no_check_certificate', default=False, help='Suppress HTTPS certificate validation.')
198     general.add_option(
199         '--cache-dir', dest='cachedir', default=get_cachedir(), metavar='DIR',
200         help='Location in the filesystem where youtube-dl can store downloaded information permanently. By default $XDG_CACHE_HOME/youtube-dl or ~/.cache/youtube-dl .')
201     general.add_option(
202         '--no-cache-dir', action='store_const', const=None, dest='cachedir',
203         help='Disable filesystem caching')
204
205
206     selection.add_option('--playlist-start',
207             dest='playliststart', metavar='NUMBER', help='playlist video to start at (default is %default)', default=1)
208     selection.add_option('--playlist-end',
209             dest='playlistend', metavar='NUMBER', help='playlist video to end at (default is last)', default=-1)
210     selection.add_option('--match-title', dest='matchtitle', metavar='REGEX',help='download only matching titles (regex or caseless sub-string)')
211     selection.add_option('--reject-title', dest='rejecttitle', metavar='REGEX',help='skip download for matching titles (regex or caseless sub-string)')
212     selection.add_option('--max-downloads', metavar='NUMBER', dest='max_downloads', help='Abort after downloading NUMBER files', default=None)
213     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)
214     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)
215     selection.add_option('--date', metavar='DATE', dest='date', help='download only videos uploaded in this date', default=None)
216     selection.add_option('--datebefore', metavar='DATE', dest='datebefore', help='download only videos uploaded before this date', default=None)
217     selection.add_option('--dateafter', metavar='DATE', dest='dateafter', help='download only videos uploaded after this date', default=None)
218     selection.add_option('--no-playlist', action='store_true', dest='noplaylist', help='download only the currently playing video', default=False)
219     selection.add_option('--age-limit', metavar='YEARS', dest='age_limit',
220                          help='download only videos suitable for the given age',
221                          default=None, type=int)
222     selection.add_option('--download-archive', metavar='FILE',
223                          dest='download_archive',
224                          help='Download only videos not present in the archive file. Record all downloaded videos in it.')
225
226
227     authentication.add_option('-u', '--username',
228             dest='username', metavar='USERNAME', help='account username')
229     authentication.add_option('-p', '--password',
230             dest='password', metavar='PASSWORD', help='account password')
231     authentication.add_option('-n', '--netrc',
232             action='store_true', dest='usenetrc', help='use .netrc authentication data', default=False)
233     authentication.add_option('--video-password',
234             dest='videopassword', metavar='PASSWORD', help='video password (vimeo only)')
235
236
237     video_format.add_option('-f', '--format',
238             action='store', dest='format', metavar='FORMAT', default='best',
239             help='video format code, specifiy the order of preference using slashes: "-f 22/17/18". "-f mp4" and "-f flv" are also supported')
240     video_format.add_option('--all-formats',
241             action='store_const', dest='format', help='download all available video formats', const='all')
242     video_format.add_option('--prefer-free-formats',
243             action='store_true', dest='prefer_free_formats', default=False, help='prefer free video formats unless a specific one is requested')
244     video_format.add_option('--max-quality',
245             action='store', dest='format_limit', metavar='FORMAT', help='highest quality format to download')
246     video_format.add_option('-F', '--list-formats',
247             action='store_true', dest='listformats', help='list all available formats (currently youtube only)')
248
249     subtitles.add_option('--write-sub', '--write-srt',
250             action='store_true', dest='writesubtitles',
251             help='write subtitle file', default=False)
252     subtitles.add_option('--write-auto-sub', '--write-automatic-sub',
253             action='store_true', dest='writeautomaticsub',
254             help='write automatic subtitle file (youtube only)', default=False)
255     subtitles.add_option('--all-subs',
256             action='store_true', dest='allsubtitles',
257             help='downloads all the available subtitles of the video', default=False)
258     subtitles.add_option('--list-subs',
259             action='store_true', dest='listsubtitles',
260             help='lists all available subtitles for the video', default=False)
261     subtitles.add_option('--sub-format',
262             action='store', dest='subtitlesformat', metavar='FORMAT',
263             help='subtitle format (default=srt) ([sbv/vtt] youtube only)', default='srt')
264     subtitles.add_option('--sub-lang', '--sub-langs', '--srt-lang',
265             action='callback', dest='subtitleslangs', metavar='LANGS', type='str',
266             default=[], callback=_comma_separated_values_options_callback,
267             help='languages of the subtitles to download (optional) separated by commas, use IETF language tags like \'en,pt\'')
268
269     downloader.add_option('-r', '--rate-limit',
270             dest='ratelimit', metavar='LIMIT', help='maximum download rate in bytes per second (e.g. 50K or 4.2M)')
271     downloader.add_option('-R', '--retries',
272             dest='retries', metavar='RETRIES', help='number of retries (default is %default)', default=10)
273     downloader.add_option('--buffer-size',
274             dest='buffersize', metavar='SIZE', help='size of download buffer (e.g. 1024 or 16K) (default is %default)', default="1024")
275     downloader.add_option('--no-resize-buffer',
276             action='store_true', dest='noresizebuffer',
277             help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.', default=False)
278     downloader.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
279
280     verbosity.add_option('-q', '--quiet',
281             action='store_true', dest='quiet', help='activates quiet mode', default=False)
282     verbosity.add_option('-s', '--simulate',
283             action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False)
284     verbosity.add_option('--skip-download',
285             action='store_true', dest='skip_download', help='do not download the video', default=False)
286     verbosity.add_option('-g', '--get-url',
287             action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
288     verbosity.add_option('-e', '--get-title',
289             action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
290     verbosity.add_option('--get-id',
291             action='store_true', dest='getid', help='simulate, quiet but print id', default=False)
292     verbosity.add_option('--get-thumbnail',
293             action='store_true', dest='getthumbnail',
294             help='simulate, quiet but print thumbnail URL', default=False)
295     verbosity.add_option('--get-description',
296             action='store_true', dest='getdescription',
297             help='simulate, quiet but print video description', default=False)
298     verbosity.add_option('--get-filename',
299             action='store_true', dest='getfilename',
300             help='simulate, quiet but print output filename', default=False)
301     verbosity.add_option('--get-format',
302             action='store_true', dest='getformat',
303             help='simulate, quiet but print output format', default=False)
304     verbosity.add_option('--newline',
305             action='store_true', dest='progress_with_newline', help='output progress bar as new lines', default=False)
306     verbosity.add_option('--no-progress',
307             action='store_true', dest='noprogress', help='do not print progress bar', default=False)
308     verbosity.add_option('--console-title',
309             action='store_true', dest='consoletitle',
310             help='display progress in console titlebar', default=False)
311     verbosity.add_option('-v', '--verbose',
312             action='store_true', dest='verbose', help='print various debugging information', default=False)
313     verbosity.add_option('--dump-intermediate-pages',
314             action='store_true', dest='dump_intermediate_pages', default=False,
315             help='print downloaded pages to debug problems(very verbose)')
316     verbosity.add_option('--youtube-print-sig-code',
317             action='store_true', dest='youtube_print_sig_code', default=False,
318             help=optparse.SUPPRESS_HELP)
319
320
321     filesystem.add_option('-t', '--title',
322             action='store_true', dest='usetitle', help='use title in file name (default)', default=False)
323     filesystem.add_option('--id',
324             action='store_true', dest='useid', help='use only video ID in file name', default=False)
325     filesystem.add_option('-l', '--literal',
326             action='store_true', dest='usetitle', help='[deprecated] alias of --title', default=False)
327     filesystem.add_option('-A', '--auto-number',
328             action='store_true', dest='autonumber',
329             help='number downloaded files starting from 00000', default=False)
330     filesystem.add_option('-o', '--output',
331             dest='outtmpl', metavar='TEMPLATE',
332             help=('output filename template. Use %(title)s to get the title, '
333                   '%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, '
334                   '%(autonumber)s to get an automatically incremented number, '
335                   '%(ext)s for the filename extension, '
336                   '%(format)s for the format description (like "22 - 1280x720" or "HD")'
337                   '%(upload_date)s for the upload date (YYYYMMDD), '
338                   '%(extractor)s for the provider (youtube, metacafe, etc), '
339                   '%(id)s for the video id , %(playlist)s for the playlist the video is in, '
340                   '%(playlist_index)s for the position in the playlist and %% for a literal percent. '
341                   'Use - to output to stdout. Can also be used to download to a different directory, '
342                   'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .'))
343     filesystem.add_option('--autonumber-size',
344             dest='autonumber_size', metavar='NUMBER',
345             help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --autonumber option is given')
346     filesystem.add_option('--restrict-filenames',
347             action='store_true', dest='restrictfilenames',
348             help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False)
349     filesystem.add_option('-a', '--batch-file',
350             dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)')
351     filesystem.add_option('-w', '--no-overwrites',
352             action='store_true', dest='nooverwrites', help='do not overwrite files', default=False)
353     filesystem.add_option('-c', '--continue',
354             action='store_true', dest='continue_dl', help='resume partially downloaded files', default=True)
355     filesystem.add_option('--no-continue',
356             action='store_false', dest='continue_dl',
357             help='do not resume partially downloaded files (restart from beginning)')
358     filesystem.add_option('--cookies',
359             dest='cookiefile', metavar='FILE', help='file to read cookies from and dump cookie jar in')
360     filesystem.add_option('--no-part',
361             action='store_true', dest='nopart', help='do not use .part files', default=False)
362     filesystem.add_option('--no-mtime',
363             action='store_false', dest='updatetime',
364             help='do not use the Last-modified header to set the file modification time', default=True)
365     filesystem.add_option('--write-description',
366             action='store_true', dest='writedescription',
367             help='write video description to a .description file', default=False)
368     filesystem.add_option('--write-info-json',
369             action='store_true', dest='writeinfojson',
370             help='write video metadata to a .info.json file', default=False)
371     filesystem.add_option('--write-annotations',
372             action='store_true', dest='writeannotations',
373             help='write video annotations to a .annotation file', default=False)
374     filesystem.add_option('--write-thumbnail',
375             action='store_true', dest='writethumbnail',
376             help='write thumbnail image to disk', default=False)
377
378
379     postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
380             help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)')
381     postproc.add_option('--audio-format', metavar='FORMAT', dest='audioformat', default='best',
382             help='"best", "aac", "vorbis", "mp3", "m4a", "opus", or "wav"; best by default')
383     postproc.add_option('--audio-quality', metavar='QUALITY', dest='audioquality', default='5',
384             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)')
385     postproc.add_option('--recode-video', metavar='FORMAT', dest='recodevideo', default=None,
386             help='Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm)')
387     postproc.add_option('-k', '--keep-video', action='store_true', dest='keepvideo', default=False,
388             help='keeps the video file on disk after the post-processing; the video is erased by default')
389     postproc.add_option('--no-post-overwrites', action='store_true', dest='nopostoverwrites', default=False,
390             help='do not overwrite post-processed files; the post-processed files are overwritten by default')
391     postproc.add_option('--embed-subs', action='store_true', dest='embedsubtitles', default=False,
392             help='embed subtitles in the video (only for mp4 videos)')
393     postproc.add_option('--add-metadata', action='store_true', dest='addmetadata', default=False,
394             help='add metadata to the files')
395
396
397     parser.add_option_group(general)
398     parser.add_option_group(selection)
399     parser.add_option_group(downloader)
400     parser.add_option_group(filesystem)
401     parser.add_option_group(verbosity)
402     parser.add_option_group(video_format)
403     parser.add_option_group(subtitles)
404     parser.add_option_group(authentication)
405     parser.add_option_group(postproc)
406
407     if overrideArguments is not None:
408         opts, args = parser.parse_args(overrideArguments)
409         if opts.verbose:
410             write_string(u'[debug] Override config: ' + repr(overrideArguments) + '\n')
411     else:
412         xdg_config_home = os.environ.get('XDG_CONFIG_HOME')
413         if xdg_config_home:
414             userConfFile = os.path.join(xdg_config_home, 'youtube-dl', 'config')
415             if not os.path.isfile(userConfFile):
416                 userConfFile = os.path.join(xdg_config_home, 'youtube-dl.conf')
417         else:
418             userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl', 'config')
419             if not os.path.isfile(userConfFile):
420                 userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf')
421         systemConf = _readOptions('/etc/youtube-dl.conf')
422         userConf = _readOptions(userConfFile)
423         commandLineConf = sys.argv[1:]
424         argv = systemConf + userConf + commandLineConf
425         opts, args = parser.parse_args(argv)
426         if opts.verbose:
427             write_string(u'[debug] System config: ' + repr(_hide_login_info(systemConf)) + '\n')
428             write_string(u'[debug] User config: ' + repr(_hide_login_info(userConf)) + '\n')
429             write_string(u'[debug] Command-line args: ' + repr(_hide_login_info(commandLineConf)) + '\n')
430
431     return parser, opts, args
432
433 def _real_main(argv=None):
434     # Compatibility fixes for Windows
435     if sys.platform == 'win32':
436         # https://github.com/rg3/youtube-dl/issues/820
437         codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)
438
439     parser, opts, args = parseOpts(argv)
440
441     # Open appropriate CookieJar
442     if opts.cookiefile is None:
443         jar = compat_cookiejar.CookieJar()
444     else:
445         try:
446             jar = compat_cookiejar.MozillaCookieJar(opts.cookiefile)
447             if os.access(opts.cookiefile, os.R_OK):
448                 jar.load()
449         except (IOError, OSError) as err:
450             if opts.verbose:
451                 traceback.print_exc()
452             write_string(u'ERROR: unable to open cookie file\n')
453             sys.exit(101)
454     # Set user agent
455     if opts.user_agent is not None:
456         std_headers['User-Agent'] = opts.user_agent
457
458     # Set referer
459     if opts.referer is not None:
460         std_headers['Referer'] = opts.referer
461
462     # Dump user agent
463     if opts.dump_user_agent:
464         compat_print(std_headers['User-Agent'])
465         sys.exit(0)
466
467     # Batch file verification
468     batchurls = []
469     if opts.batchfile is not None:
470         try:
471             if opts.batchfile == '-':
472                 batchfd = sys.stdin
473             else:
474                 batchfd = open(opts.batchfile, 'r')
475             batchurls = batchfd.readlines()
476             batchurls = [x.strip() for x in batchurls]
477             batchurls = [x for x in batchurls if len(x) > 0 and not re.search(r'^[#/;]', x)]
478             if opts.verbose:
479                 write_string(u'[debug] Batch file urls: ' + repr(batchurls) + u'\n')
480         except IOError:
481             sys.exit(u'ERROR: batch file could not be read')
482     all_urls = batchurls + args
483     all_urls = [url.strip() for url in all_urls]
484
485     opener = _setup_opener(jar=jar, opts=opts)
486
487     extractors = gen_extractors()
488
489     if opts.list_extractors:
490         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
491             compat_print(ie.IE_NAME + (' (CURRENTLY BROKEN)' if not ie._WORKING else ''))
492             matchedUrls = [url for url in all_urls if ie.suitable(url)]
493             all_urls = [url for url in all_urls if url not in matchedUrls]
494             for mu in matchedUrls:
495                 compat_print(u'  ' + mu)
496         sys.exit(0)
497     if opts.list_extractor_descriptions:
498         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
499             if not ie._WORKING:
500                 continue
501             desc = getattr(ie, 'IE_DESC', ie.IE_NAME)
502             if desc is False:
503                 continue
504             if hasattr(ie, 'SEARCH_KEY'):
505                 _SEARCHES = (u'cute kittens', u'slithering pythons', u'falling cat', u'angry poodle', u'purple fish', u'running tortoise')
506                 _COUNTS = (u'', u'5', u'10', u'all')
507                 desc += u' (Example: "%s%s:%s" )' % (ie.SEARCH_KEY, random.choice(_COUNTS), random.choice(_SEARCHES))
508             compat_print(desc)
509         sys.exit(0)
510
511
512     # Conflicting, missing and erroneous options
513     if opts.usenetrc and (opts.username is not None or opts.password is not None):
514         parser.error(u'using .netrc conflicts with giving username/password')
515     if opts.password is not None and opts.username is None:
516         parser.error(u' account username missing\n')
517     if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
518         parser.error(u'using output template conflicts with using title, video ID or auto number')
519     if opts.usetitle and opts.useid:
520         parser.error(u'using title conflicts with using video ID')
521     if opts.username is not None and opts.password is None:
522         opts.password = getpass.getpass(u'Type account password and press return:')
523     if opts.ratelimit is not None:
524         numeric_limit = FileDownloader.parse_bytes(opts.ratelimit)
525         if numeric_limit is None:
526             parser.error(u'invalid rate limit specified')
527         opts.ratelimit = numeric_limit
528     if opts.min_filesize is not None:
529         numeric_limit = FileDownloader.parse_bytes(opts.min_filesize)
530         if numeric_limit is None:
531             parser.error(u'invalid min_filesize specified')
532         opts.min_filesize = numeric_limit
533     if opts.max_filesize is not None:
534         numeric_limit = FileDownloader.parse_bytes(opts.max_filesize)
535         if numeric_limit is None:
536             parser.error(u'invalid max_filesize specified')
537         opts.max_filesize = numeric_limit
538     if opts.retries is not None:
539         try:
540             opts.retries = int(opts.retries)
541         except (TypeError, ValueError) as err:
542             parser.error(u'invalid retry count specified')
543     if opts.buffersize is not None:
544         numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize)
545         if numeric_buffersize is None:
546             parser.error(u'invalid buffer size specified')
547         opts.buffersize = numeric_buffersize
548     try:
549         opts.playliststart = int(opts.playliststart)
550         if opts.playliststart <= 0:
551             raise ValueError(u'Playlist start must be positive')
552     except (TypeError, ValueError) as err:
553         parser.error(u'invalid playlist start number specified')
554     try:
555         opts.playlistend = int(opts.playlistend)
556         if opts.playlistend != -1 and (opts.playlistend <= 0 or opts.playlistend < opts.playliststart):
557             raise ValueError(u'Playlist end must be greater than playlist start')
558     except (TypeError, ValueError) as err:
559         parser.error(u'invalid playlist end number specified')
560     if opts.extractaudio:
561         if opts.audioformat not in ['best', 'aac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav']:
562             parser.error(u'invalid audio format specified')
563     if opts.audioquality:
564         opts.audioquality = opts.audioquality.strip('k').strip('K')
565         if not opts.audioquality.isdigit():
566             parser.error(u'invalid audio quality specified')
567     if opts.recodevideo is not None:
568         if opts.recodevideo not in ['mp4', 'flv', 'webm', 'ogg']:
569             parser.error(u'invalid video recode format specified')
570     if opts.date is not None:
571         date = DateRange.day(opts.date)
572     else:
573         date = DateRange(opts.dateafter, opts.datebefore)
574
575     # --all-sub automatically sets --write-sub if --write-auto-sub is not given
576     # this was the old behaviour if only --all-sub was given.
577     if opts.allsubtitles and (opts.writeautomaticsub == False):
578         opts.writesubtitles = True
579
580     if sys.version_info < (3,):
581         # In Python 2, sys.argv is a bytestring (also note http://bugs.python.org/issue2128 for Windows systems)
582         if opts.outtmpl is not None:
583             opts.outtmpl = opts.outtmpl.decode(preferredencoding())
584     outtmpl =((opts.outtmpl is not None and opts.outtmpl)
585             or (opts.format == '-1' and opts.usetitle and u'%(title)s-%(id)s-%(format)s.%(ext)s')
586             or (opts.format == '-1' and u'%(id)s-%(format)s.%(ext)s')
587             or (opts.usetitle and opts.autonumber and u'%(autonumber)s-%(title)s-%(id)s.%(ext)s')
588             or (opts.usetitle and u'%(title)s-%(id)s.%(ext)s')
589             or (opts.useid and u'%(id)s.%(ext)s')
590             or (opts.autonumber and u'%(autonumber)s-%(id)s.%(ext)s')
591             or u'%(title)s-%(id)s.%(ext)s')
592     if '%(ext)s' not in outtmpl and opts.extractaudio:
593         parser.error(u'Cannot download a video and extract audio into the same'
594                      u' file! Use "%%(ext)s" instead of %r' %
595                      determine_ext(outtmpl, u''))
596
597     # YoutubeDL
598     ydl = YoutubeDL({
599         'usenetrc': opts.usenetrc,
600         'username': opts.username,
601         'password': opts.password,
602         'videopassword': opts.videopassword,
603         'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
604         'forceurl': opts.geturl,
605         'forcetitle': opts.gettitle,
606         'forceid': opts.getid,
607         'forcethumbnail': opts.getthumbnail,
608         'forcedescription': opts.getdescription,
609         'forcefilename': opts.getfilename,
610         'forceformat': opts.getformat,
611         'simulate': opts.simulate,
612         'skip_download': (opts.skip_download or opts.simulate or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
613         'format': opts.format,
614         'format_limit': opts.format_limit,
615         'listformats': opts.listformats,
616         'outtmpl': outtmpl,
617         'autonumber_size': opts.autonumber_size,
618         'restrictfilenames': opts.restrictfilenames,
619         'ignoreerrors': opts.ignoreerrors,
620         'ratelimit': opts.ratelimit,
621         'nooverwrites': opts.nooverwrites,
622         'retries': opts.retries,
623         'buffersize': opts.buffersize,
624         'noresizebuffer': opts.noresizebuffer,
625         'continuedl': opts.continue_dl,
626         'noprogress': opts.noprogress,
627         'progress_with_newline': opts.progress_with_newline,
628         'playliststart': opts.playliststart,
629         'playlistend': opts.playlistend,
630         'noplaylist': opts.noplaylist,
631         'logtostderr': opts.outtmpl == '-',
632         'consoletitle': opts.consoletitle,
633         'nopart': opts.nopart,
634         'updatetime': opts.updatetime,
635         'writedescription': opts.writedescription,
636         'writeannotations': opts.writeannotations,
637         'writeinfojson': opts.writeinfojson,
638         'writethumbnail': opts.writethumbnail,
639         'writesubtitles': opts.writesubtitles,
640         'writeautomaticsub': opts.writeautomaticsub,
641         'allsubtitles': opts.allsubtitles,
642         'listsubtitles': opts.listsubtitles,
643         'subtitlesformat': opts.subtitlesformat,
644         'subtitleslangs': opts.subtitleslangs,
645         'matchtitle': decodeOption(opts.matchtitle),
646         'rejecttitle': decodeOption(opts.rejecttitle),
647         'max_downloads': opts.max_downloads,
648         'prefer_free_formats': opts.prefer_free_formats,
649         'verbose': opts.verbose,
650         'dump_intermediate_pages': opts.dump_intermediate_pages,
651         'test': opts.test,
652         'keepvideo': opts.keepvideo,
653         'min_filesize': opts.min_filesize,
654         'max_filesize': opts.max_filesize,
655         'daterange': date,
656         'cachedir': opts.cachedir,
657         'youtube_print_sig_code': opts.youtube_print_sig_code,
658         'age_limit': opts.age_limit,
659         'download_archive': opts.download_archive,
660         })
661
662     if opts.verbose:
663         write_string(u'[debug] youtube-dl version ' + __version__ + u'\n')
664         try:
665             sp = subprocess.Popen(
666                 ['git', 'rev-parse', '--short', 'HEAD'],
667                 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
668                 cwd=os.path.dirname(os.path.abspath(__file__)))
669             out, err = sp.communicate()
670             out = out.decode().strip()
671             if re.match('[0-9a-f]+', out):
672                 write_string(u'[debug] Git HEAD: ' + out + u'\n')
673         except:
674             try:
675                 sys.exc_clear()
676             except:
677                 pass
678         write_string(u'[debug] Python version %s - %s' %(platform.python_version(), platform_name()) + u'\n')
679
680         proxy_map = {}
681         for handler in opener.handlers:
682             if hasattr(handler, 'proxies'):
683                 proxy_map.update(handler.proxies)
684         write_string(u'[debug] Proxy map: ' + compat_str(proxy_map) + u'\n')
685
686     ydl.add_default_info_extractors()
687
688     # PostProcessors
689     # Add the metadata pp first, the other pps will copy it
690     if opts.addmetadata:
691         ydl.add_post_processor(FFmpegMetadataPP())
692     if opts.extractaudio:
693         ydl.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, nopostoverwrites=opts.nopostoverwrites))
694     if opts.recodevideo:
695         ydl.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo))
696     if opts.embedsubtitles:
697         ydl.add_post_processor(FFmpegEmbedSubtitlePP(subtitlesformat=opts.subtitlesformat))
698
699     # Update version
700     if opts.update_self:
701         update_self(ydl.to_screen, opts.verbose)
702
703     # Maybe do nothing
704     if len(all_urls) < 1:
705         if not opts.update_self:
706             parser.error(u'you must provide at least one URL')
707         else:
708             sys.exit()
709
710     try:
711         retcode = ydl.download(all_urls)
712     except MaxDownloadsReached:
713         ydl.to_screen(u'--max-download limit reached, aborting.')
714         retcode = 101
715
716     # Dump cookie jar if requested
717     if opts.cookiefile is not None:
718         try:
719             jar.save()
720         except (IOError, OSError):
721             sys.exit(u'ERROR: unable to save cookie jar')
722
723     sys.exit(retcode)
724
725
726 def _setup_opener(jar=None, opts=None, timeout=300):
727     if opts is None:
728         FakeOptions = collections.namedtuple(
729             'FakeOptions', ['proxy', 'no_check_certificate'])
730         opts = FakeOptions(proxy=None, no_check_certificate=False)
731
732     cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
733     if opts.proxy is not None:
734         if opts.proxy == '':
735             proxies = {}
736         else:
737             proxies = {'http': opts.proxy, 'https': opts.proxy}
738     else:
739         proxies = compat_urllib_request.getproxies()
740         # Set HTTPS proxy to HTTP one if given (https://github.com/rg3/youtube-dl/issues/805)
741         if 'http' in proxies and 'https' not in proxies:
742             proxies['https'] = proxies['http']
743     proxy_handler = compat_urllib_request.ProxyHandler(proxies)
744     https_handler = make_HTTPS_handler(opts)
745     opener = compat_urllib_request.build_opener(
746         https_handler, proxy_handler, cookie_processor, YoutubeDLHandler())
747     # Delete the default user-agent header, which would otherwise apply in
748     # cases where our custom HTTP handler doesn't come into play
749     # (See https://github.com/rg3/youtube-dl/issues/1309 for details)
750     opener.addheaders = []
751     compat_urllib_request.install_opener(opener)
752     socket.setdefaulttimeout(timeout)
753     return opener
754
755
756 def main(argv=None):
757     try:
758         _real_main(argv)
759     except DownloadError:
760         sys.exit(1)
761     except SameFileError:
762         sys.exit(u'ERROR: fixed output name but more than one file to download')
763     except KeyboardInterrupt:
764         sys.exit(u'\nERROR: Interrupted by user')