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