aboutsummaryrefslogtreecommitdiffstats
path: root/yt_dlp/options.py
diff options
context:
space:
mode:
Diffstat (limited to 'yt_dlp/options.py')
-rw-r--r--yt_dlp/options.py158
1 files changed, 74 insertions, 84 deletions
diff --git a/yt_dlp/options.py b/yt_dlp/options.py
index 2e8d384c0..91095f7f1 100644
--- a/yt_dlp/options.py
+++ b/yt_dlp/options.py
@@ -1,11 +1,29 @@
-import optparse
+from __future__ import unicode_literals
+
import os.path
+import optparse
import re
-import shlex
import sys
-from .compat import compat_expanduser, compat_get_terminal_size, compat_getenv
+from .compat import (
+ compat_expanduser,
+ compat_get_terminal_size,
+ compat_getenv,
+ compat_kwargs,
+ compat_shlex_split,
+)
+from .utils import (
+ Config,
+ expand_path,
+ get_executable_path,
+ OUTTMPL_TYPES,
+ POSTPROCESS_WHEN,
+ remove_end,
+ write_string,
+)
from .cookies import SUPPORTED_BROWSERS, SUPPORTED_KEYRINGS
+from .version import __version__
+
from .downloader.external import list_external_downloaders
from .postprocessor import (
FFmpegExtractAudioPP,
@@ -15,17 +33,6 @@ from .postprocessor import (
SponsorBlockPP,
)
from .postprocessor.modify_chapters import DEFAULT_SPONSORBLOCK_CHAPTER_TITLE
-from .utils import (
- OUTTMPL_TYPES,
- POSTPROCESS_WHEN,
- Config,
- expand_path,
- get_executable_path,
- join_nonempty,
- remove_end,
- write_string,
-)
-from .version import __version__
def parseOpts(overrideArguments=None, ignore_config_files='if_override'):
@@ -110,54 +117,37 @@ def parseOpts(overrideArguments=None, ignore_config_files='if_override'):
return parser, opts, args
-class _YoutubeDLHelpFormatter(optparse.IndentedHelpFormatter):
- def __init__(self):
- # No need to wrap help messages if we're on a wide console
- max_width = compat_get_terminal_size().columns or 80
- # 47% is chosen because that is how README.md is currently formatted
- # and moving help text even further to the right is undesirable.
- # This can be reduced in the future to get a prettier output
- super().__init__(width=max_width, max_help_position=int(0.47 * max_width))
-
- @staticmethod
- def format_option_strings(option):
- """ ('-o', '--option') -> -o, --format METAVAR """
- opts = join_nonempty(
- option._short_opts and option._short_opts[0],
- option._long_opts and option._long_opts[0],
- delim=', ')
- if option.takes_value():
- opts += f' {option.metavar}'
- return opts
-
-
class _YoutubeDLOptionParser(optparse.OptionParser):
# optparse is deprecated since python 3.2. So assume a stable interface even for private methods
- def __init__(self):
- super().__init__(
- prog='yt-dlp',
- version=__version__,
- usage='%prog [OPTIONS] URL [URL...]',
- epilog='See full documentation at https://github.com/yt-dlp/yt-dlp#readme',
- formatter=_YoutubeDLHelpFormatter(),
- conflict_handler='resolve',
- )
-
- def _get_args(self, args):
- return sys.argv[1:] if args is None else list(args)
-
def _match_long_opt(self, opt):
"""Improve ambigious argument resolution by comparing option objects instead of argument strings"""
try:
return super()._match_long_opt(opt)
except optparse.AmbiguousOptionError as e:
- if len({self._long_opt[p] for p in e.possibilities}) == 1:
+ if len(set(self._long_opt[p] for p in e.possibilities)) == 1:
return e.possibilities[0]
raise
def create_parser():
+ def _format_option_string(option):
+ ''' ('-o', '--option') -> -o, --format METAVAR'''
+
+ opts = []
+
+ if option._short_opts:
+ opts.append(option._short_opts[0])
+ if option._long_opts:
+ opts.append(option._long_opts[0])
+ if len(opts) > 1:
+ opts.insert(1, ', ')
+
+ if option.takes_value():
+ opts.append(' %s' % option.metavar)
+
+ return ''.join(opts)
+
def _list_from_options_callback(option, opt_str, value, parser, append=True, delim=',', process=str.strip):
# append can be True, False or -1 (prepend)
current = list(getattr(parser.values, option.dest)) if append else []
@@ -200,9 +190,9 @@ def create_parser():
out_dict = dict(getattr(parser.values, option.dest))
multiple_args = not isinstance(value, str)
if multiple_keys:
- allowed_keys = fr'({allowed_keys})(,({allowed_keys}))*'
+ allowed_keys = r'(%s)(,(%s))*' % (allowed_keys, allowed_keys)
mobj = re.match(
- fr'(?i)(?P<keys>{allowed_keys}){delimiter}(?P<val>.*)$',
+ r'(?i)(?P<keys>%s)%s(?P<val>.*)$' % (allowed_keys, delimiter),
value[0] if multiple_args else value)
if mobj is not None:
keys, val = mobj.group('keys').split(','), mobj.group('val')
@@ -212,7 +202,7 @@ def create_parser():
keys, val = [default_key], value
else:
raise optparse.OptionValueError(
- f'wrong {opt_str} formatting; it should be {option.metavar}, not "{value}"')
+ 'wrong %s formatting; it should be %s, not "%s"' % (opt_str, option.metavar, value))
try:
keys = map(process_key, keys) if process_key else keys
val = process(val) if process else val
@@ -222,7 +212,25 @@ def create_parser():
out_dict[key] = out_dict.get(key, []) + [val] if append else val
setattr(parser.values, option.dest, out_dict)
- parser = _YoutubeDLOptionParser()
+ # No need to wrap help messages if we're on a wide console
+ columns = compat_get_terminal_size().columns
+ max_width = columns if columns else 80
+ # 47% is chosen because that is how README.md is currently formatted
+ # and moving help text even further to the right is undesirable.
+ # This can be reduced in the future to get a prettier output
+ max_help_position = int(0.47 * max_width)
+
+ fmt = optparse.IndentedHelpFormatter(width=max_width, max_help_position=max_help_position)
+ fmt.format_option_strings = _format_option_string
+
+ kw = {
+ 'version': __version__,
+ 'formatter': fmt,
+ 'usage': '%prog [OPTIONS] URL [URL...]',
+ 'conflict_handler': 'resolve',
+ }
+
+ parser = _YoutubeDLOptionParser(**compat_kwargs(kw))
general = optparse.OptionGroup(parser, 'General Options')
general.add_option(
@@ -234,10 +242,6 @@ def create_parser():
action='version',
help='Print program version and exit')
general.add_option(
- '-U', '--update',
- action='store_true', dest='update_self',
- help='Update this program to latest version')
- general.add_option(
'-i', '--ignore-errors',
action='store_true', dest='ignoreerrors',
help='Ignore download and postprocessing errors. The download will be considered successful even if the postprocessing fails')
@@ -435,8 +439,9 @@ def create_parser():
'--date',
metavar='DATE', dest='date', default=None,
help=(
- 'Download only videos uploaded on this date. The date can be "YYYYMMDD" or in the format '
- '[now|today|yesterday][-N[day|week|month|year]]. Eg: --date today-2weeks'))
+ 'Download only videos uploaded on this date. '
+ 'The date can be "YYYYMMDD" or in the format '
+ '"(now|today)[+-][0-9](day|week|month|year)(s)?"'))
selection.add_option(
'--datebefore',
metavar='DATE', dest='datebefore', default=None,
@@ -470,8 +475,7 @@ def create_parser():
'!is_live --match-filter "like_count>?100 & description~=\'(?i)\\bcats \\& dogs\\b\'" '
'matches only videos that are not live OR those that have a like count more than 100 '
'(or the like field is not available) and also has a description '
- 'that contains the phrase "cats & dogs" (ignoring case). '
- 'Use "--match-filter -" to interactively ask whether to download each video'))
+ 'that contains the phrase "cats & dogs" (ignoring case)'))
selection.add_option(
'--no-match-filter',
metavar='FILTER', dest='match_filter', action='store_const', const=None,
@@ -511,11 +515,11 @@ def create_parser():
selection.add_option(
'--break-per-input',
action='store_true', dest='break_per_url', default=False,
- help='Make --break-on-existing, --break-on-reject and --max-downloads act only on the current input URL')
+ help='Make --break-on-existing and --break-on-reject act only on the current input URL')
selection.add_option(
'--no-break-per-input',
action='store_false', dest='break_per_url',
- help='--break-on-existing and similar options terminates the entire download queue')
+ help='--break-on-existing and --break-on-reject terminates the entire download queue')
selection.add_option(
'--skip-playlist-after-errors', metavar='N',
dest='skip_playlist_after_errors', default=None, type=int,
@@ -570,19 +574,6 @@ def create_parser():
'--ap-list-mso',
action='store_true', dest='ap_list_mso', default=False,
help='List all supported multiple-system operators')
- authentication.add_option(
- '--client-certificate',
- dest='client_certificate', metavar='CERTFILE',
- help='Path to client certificate file in PEM format. May include the private key')
- authentication.add_option(
- '--client-certificate-key',
- dest='client_certificate_key', metavar='KEYFILE',
- help='Path to private key file for client certificate')
- authentication.add_option(
- '--client-certificate-password',
- dest='client_certificate_password', metavar='PASSWORD',
- help='Password for client certificate private key, if encrypted. '
- 'If not provided and the key is encrypted, yt-dlp will ask interactively')
video_format = optparse.OptionGroup(parser, 'Video Format Options')
video_format.add_option(
@@ -826,11 +817,11 @@ def create_parser():
}, help=(
'Name or path of the external downloader to use (optionally) prefixed by '
'the protocols (http, ftp, m3u8, dash, rstp, rtmp, mms) to use it for. '
- f'Currently supports native, {", ".join(list_external_downloaders())}. '
+ 'Currently supports native, %s (Recommended: aria2c). '
'You can use this option multiple times to set different downloaders for different protocols. '
'For example, --downloader aria2c --downloader "dash,m3u8:native" will use '
'aria2c for http/ftp downloads, and the native downloader for dash/m3u8 downloads '
- '(Alias: --external-downloader)'))
+ '(Alias: --external-downloader)' % ', '.join(list_external_downloaders())))
downloader.add_option(
'--downloader-args', '--external-downloader-args',
metavar='NAME:ARGS', dest='external_downloader_args', default={}, type='str',
@@ -838,7 +829,7 @@ def create_parser():
callback_kwargs={
'allowed_keys': r'ffmpeg_[io]\d*|%s' % '|'.join(map(re.escape, list_external_downloaders())),
'default_key': 'default',
- 'process': shlex.split
+ 'process': compat_shlex_split
}, help=(
'Give these arguments to the external downloader. '
'Specify the downloader name and the arguments separated by a colon ":". '
@@ -945,8 +936,7 @@ def create_parser():
}, help=(
'Field name or output template to print to screen, optionally prefixed with when to print it, separated by a ":". '
'Supported values of "WHEN" are the same as that of --use-postprocessor, and "video" (default). '
- 'Implies --quiet. Implies --simulate unless --no-simulate or later stages of WHEN are used. '
- 'This option can be used multiple times'))
+ 'Implies --quiet and --simulate (unless --no-simulate is used). This option can be used multiple times'))
verbosity.add_option(
'--print-to-file',
metavar='[WHEN:]TEMPLATE FILE', dest='print_to_file', default={}, type='str', nargs=2,
@@ -1064,7 +1054,7 @@ def create_parser():
verbosity.add_option(
'-C', '--call-home',
dest='call_home', action='store_true', default=False,
- # help='Contact the yt-dlp server for debugging')
+ # help='[Broken] Contact the yt-dlp server for debugging')
help=optparse.SUPPRESS_HELP)
verbosity.add_option(
'--no-call-home',
@@ -1345,7 +1335,7 @@ def create_parser():
callback_kwargs={
'allowed_keys': r'\w+(?:\+\w+)?',
'default_key': 'default-compat',
- 'process': shlex.split,
+ 'process': compat_shlex_split,
'multiple_keys': False
}, help=(
'Give these arguments to the postprocessors. '
@@ -1434,7 +1424,7 @@ def create_parser():
dest='parse_metadata', metavar='FIELDS REGEX REPLACE', action='append', nargs=3,
help='Replace text in a metadata field using the given regex. This option can be used multiple times')
postproc.add_option(
- '--xattrs', '--xattr',
+ '--xattrs',
action='store_true', dest='xattrs', default=False,
help='Write metadata to the video file\'s xattrs (using dublin core and xdg standards)')
postproc.add_option(