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