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