diff options
Diffstat (limited to 'yt_dlp/options.py')
-rw-r--r-- | yt_dlp/options.py | 158 |
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( |