updating and refining plugin system refactor

This commit is contained in:
2026-04-28 22:20:54 -07:00
parent 8685fbb723
commit 323c24f4f4
33 changed files with 4287 additions and 3312 deletions
+115 -26
View File
@@ -506,32 +506,28 @@ class CmdletIntrospection:
if normalized_arg == "plugin":
canonical_cmd = (cmd_name or "").replace("_", "-").lower()
try:
from ProviderCore.registry import (
list_search_plugin_names,
list_upload_plugin_names,
)
from ProviderCore.registry import list_plugin_names_with_capability
except Exception:
list_search_plugin_names = None # type: ignore
list_upload_plugin_names = None # type: ignore
list_plugin_names_with_capability = None # type: ignore
provider_choices: List[str] = []
plugin_choices: List[str] = []
if canonical_cmd in {"add-file"} and list_upload_plugin_names is not None:
return list_upload_plugin_names() or []
if canonical_cmd in {"add-file"} and list_plugin_names_with_capability is not None:
return list_plugin_names_with_capability("upload") or []
if list_search_plugin_names is not None:
provider_choices = list_search_plugin_names() or []
if list_plugin_names_with_capability is not None:
plugin_choices = list_plugin_names_with_capability("search") or []
if provider_choices:
return provider_choices
if plugin_choices:
return plugin_choices
if normalized_arg == "scrape":
try:
from plugins.metadata_provider import list_metadata_providers
from plugins.metadata_provider import list_metadata_plugins
meta_providers = list_metadata_providers(config) or {}
if meta_providers:
return sorted(meta_providers.keys())
metadata_plugins = list_metadata_plugins(config) or {}
if metadata_plugins:
return sorted(metadata_plugins.keys())
except Exception:
pass
@@ -704,6 +700,77 @@ class CmdletCompleter(Completer):
return tokens[idx + 1]
return None
@staticmethod
def _selected_plugin_name(cmd_name: str, stage_tokens: Sequence[str]) -> Optional[str]:
canonical_cmd = str(cmd_name or "").replace("_", "-").strip().lower()
if canonical_cmd not in {"search-file", "add-file", "download-file"}:
return None
return CmdletCompleter._flag_value(stage_tokens, "-plugin", "--plugin")
@staticmethod
def _plugin_instance_choices(plugin_name: Optional[str], config: Dict[str, Any]) -> List[str]:
plugin_key = str(plugin_name or "").strip().lower()
if not plugin_key:
return []
try:
from ProviderCore.registry import get_plugin_class
except Exception:
return []
plugin_class = get_plugin_class(plugin_key)
if plugin_class is None:
return []
try:
plugin = plugin_class(config)
except Exception:
return []
try:
instances = plugin.configured_instances()
except Exception:
return []
out: List[str] = []
seen: Set[str] = set()
for value in instances or []:
text = str(value or "").strip()
lowered = text.lower()
if not text or lowered in seen:
continue
seen.add(lowered)
out.append(text)
return out
def _filter_stage_arg_names(
self,
*,
cmd_name: str,
stage_tokens: Sequence[str],
config: Dict[str, Any],
arg_names: List[str],
) -> List[str]:
if not arg_names:
return []
canonical_cmd = str(cmd_name or "").replace("_", "-").strip().lower()
plugin_name = self._selected_plugin_name(canonical_cmd, stage_tokens)
instance_choices = self._plugin_instance_choices(plugin_name, config)
has_named_instances = bool(instance_choices)
filtered: List[str] = []
for arg in arg_names:
logical = str(arg or "").lstrip("-").strip().lower()
if logical == "instance":
if not plugin_name or not has_named_instances:
continue
if canonical_cmd == "search-file" and logical == "open":
if str(plugin_name or "").strip().lower() != "alldebrid":
continue
filtered.append(arg)
return filtered
def get_completions(
self,
document: Document,
@@ -742,7 +809,12 @@ class CmdletCompleter(Completer):
if cmd_name not in self.cmdlet_names:
return
arg_names = self._cmdlet_args(cmd_name, config)
arg_names = self._filter_stage_arg_names(
cmd_name=cmd_name,
stage_tokens=stage_tokens,
config=config,
arg_names=self._cmdlet_args(cmd_name, config),
)
seen_logicals: Set[str] = set()
for arg in arg_names:
arg_low = arg.lower()
@@ -779,6 +851,8 @@ class CmdletCompleter(Completer):
if cmd_name == "search-file":
provider_name = self._flag_value(stage_tokens, "-plugin", "--plugin")
selected_plugin = self._selected_plugin_name(cmd_name, stage_tokens)
query_specs = self._query_args(cmd_name, config)
query_flag_index = -1
for idx, tok in enumerate(stage_tokens):
@@ -886,28 +960,43 @@ class CmdletCompleter(Completer):
yield Completion(suggestion, start_position=start_pos)
return
choices = self._arg_choices(
cmd_name=cmd_name,
arg_name=prev_token,
config=config,
force=False,
)
normalized_prev = prev_token.lstrip("-").strip().lower()
choices: List[str] = []
if normalized_prev == "instance" and selected_plugin:
choices = self._plugin_instance_choices(selected_plugin, config)
if not choices:
choices = self._arg_choices(
cmd_name=cmd_name,
arg_name=prev_token,
config=config,
force=False,
)
if choices:
choice_list = choices
normalized_prev = prev_token.lstrip("-").strip().lower()
if normalized_prev in {"plugin", "provider"} and current_token:
current_lower = current_token.lower()
filtered = [c for c in choices if current_lower in c.lower()]
if filtered:
choice_list = filtered
if normalized_prev == "instance" and current_token:
current_lower = current_token.lower()
filtered = [c for c in choice_list if current_lower in c.lower()]
if filtered:
choice_list = filtered
for choice in choice_list:
yield Completion(choice, start_position=-len(current_token))
# Example: if the user has typed `download-file -url ...`, then `url`
# is considered used and should not be suggested again (even as `--url`).
return
arg_names = self._cmdlet_args(cmd_name, config)
arg_names = self._filter_stage_arg_names(
cmd_name=cmd_name,
stage_tokens=stage_tokens,
config=config,
arg_names=self._cmdlet_args(cmd_name, config),
)
used_logicals = self._used_arg_logicals(cmd_name, stage_tokens, config)
logical_seen: Set[str] = set()
for arg in arg_names: