df
This commit is contained in:
177
CLI.py
177
CLI.py
@@ -808,14 +808,6 @@ class CmdletIntrospection:
|
||||
|
||||
provider_choices: List[str] = []
|
||||
|
||||
if canonical_cmd in {"search-provider"
|
||||
} and list_search_providers is not None:
|
||||
providers = list_search_providers(config) or {}
|
||||
available = [
|
||||
name for name, is_ready in providers.items() if is_ready
|
||||
]
|
||||
return sorted(available) if available else sorted(providers.keys())
|
||||
|
||||
if canonical_cmd in {"add-file"} and list_file_providers is not None:
|
||||
providers = list_file_providers(config) or {}
|
||||
available = [
|
||||
@@ -1298,48 +1290,6 @@ class CmdletExecutor:
|
||||
emitted_items: Optional[List[Any]] = None,
|
||||
cmd_args: Optional[List[str]] = None,
|
||||
) -> str:
|
||||
if cmd_name in ("search-provider", "search_provider") and cmd_args:
|
||||
provider: str = ""
|
||||
query: str = ""
|
||||
tokens = [str(a) for a in (cmd_args or [])]
|
||||
pos: List[str] = []
|
||||
i = 0
|
||||
while i < len(tokens):
|
||||
low = tokens[i].lower()
|
||||
if low in {"-provider",
|
||||
"--provider"} and i + 1 < len(tokens):
|
||||
provider = str(tokens[i + 1]).strip()
|
||||
i += 2
|
||||
continue
|
||||
if low in {"-query",
|
||||
"--query"} and i + 1 < len(tokens):
|
||||
query = str(tokens[i + 1]).strip()
|
||||
i += 2
|
||||
continue
|
||||
if low in {"-limit",
|
||||
"--limit"} and i + 1 < len(tokens):
|
||||
i += 2
|
||||
continue
|
||||
if not str(tokens[i]).startswith("-"):
|
||||
pos.append(str(tokens[i]))
|
||||
i += 1
|
||||
|
||||
if not provider and pos:
|
||||
provider = str(pos[0]).strip()
|
||||
pos = pos[1:]
|
||||
if not query and pos:
|
||||
query = " ".join(pos).strip()
|
||||
|
||||
if provider and query:
|
||||
provider_lower = provider.lower()
|
||||
if provider_lower == "youtube":
|
||||
provider_label = "Youtube"
|
||||
elif provider_lower == "openlibrary":
|
||||
provider_label = "OpenLibrary"
|
||||
else:
|
||||
provider_label = provider[:1].upper() + provider[1:]
|
||||
return f"{provider_label}: {query}".strip().rstrip(":")
|
||||
|
||||
title_map = {
|
||||
"search-file": "Results",
|
||||
"search_file": "Results",
|
||||
@@ -1807,10 +1757,6 @@ class CmdletExecutor:
|
||||
"tags",
|
||||
"search-file",
|
||||
"search_file",
|
||||
"search-provider",
|
||||
"search_provider",
|
||||
"search-store",
|
||||
"search_store",
|
||||
}
|
||||
|
||||
if cmd_name in self_managing_commands:
|
||||
@@ -3029,8 +2975,6 @@ class PipelineExecutor:
|
||||
stage_is_last = (stage_index + 1 >= len(stages))
|
||||
if filter_spec is not None and stage_is_last:
|
||||
try:
|
||||
from SYS.result_table import ResultTable
|
||||
|
||||
base_table = stage_table
|
||||
if base_table is None:
|
||||
base_table = ctx.get_last_result_table()
|
||||
@@ -3801,6 +3745,15 @@ class MedeiaCLI:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._config_loader = ConfigLoader(root=self.ROOT)
|
||||
|
||||
# Optional dependency auto-install for configured tools (best-effort).
|
||||
try:
|
||||
from SYS.optional_deps import maybe_auto_install_configured_tools
|
||||
|
||||
maybe_auto_install_configured_tools(self._config_loader.load())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self._cmdlet_executor = CmdletExecutor(config_loader=self._config_loader)
|
||||
self._pipeline_executor = PipelineExecutor(config_loader=self._config_loader)
|
||||
|
||||
@@ -3833,54 +3786,6 @@ class MedeiaCLI:
|
||||
pass
|
||||
return value
|
||||
|
||||
def _complete_search_provider(ctx, param, incomplete: str): # pragma: no cover
|
||||
try:
|
||||
from click.shell_completion import CompletionItem
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
try:
|
||||
from ProviderCore.registry import list_search_providers
|
||||
|
||||
providers = list_search_providers(self._config_loader.load()) or {}
|
||||
available = [n for n, ok in providers.items() if ok]
|
||||
choices = sorted(available) if available else sorted(providers.keys())
|
||||
except Exception:
|
||||
choices = []
|
||||
|
||||
inc = (incomplete or "").lower()
|
||||
return [
|
||||
CompletionItem(name) for name in choices
|
||||
if name and name.lower().startswith(inc)
|
||||
]
|
||||
|
||||
@app.command("search-provider")
|
||||
def search_provider(
|
||||
provider: str = typer.Option(
|
||||
...,
|
||||
"--provider",
|
||||
"-p",
|
||||
help="Provider name (bandcamp, libgen, soulseek, youtube)",
|
||||
shell_complete=_complete_search_provider,
|
||||
),
|
||||
query: str = typer.Argument(...,
|
||||
help="Search query (quote for spaces)"),
|
||||
limit: int = typer.Option(
|
||||
36,
|
||||
"--limit",
|
||||
"-l",
|
||||
help="Maximum results to return"
|
||||
),
|
||||
) -> None:
|
||||
self._cmdlet_executor.execute(
|
||||
"search-provider",
|
||||
["-provider",
|
||||
provider,
|
||||
query,
|
||||
"-limit",
|
||||
str(limit)]
|
||||
)
|
||||
|
||||
@app.command("pipeline")
|
||||
def pipeline(
|
||||
command: str = typer.Option(
|
||||
@@ -3942,7 +3847,7 @@ class MedeiaCLI:
|
||||
if ctx.invoked_subcommand is None:
|
||||
self.run_repl()
|
||||
|
||||
_ = (search_provider, pipeline, repl, main_callback)
|
||||
_ = (pipeline, repl, main_callback)
|
||||
|
||||
# Dynamically register all cmdlets as top-level Typer commands so users can
|
||||
# invoke `mm <cmdlet> [args]` directly from the shell. We use Click/Typer
|
||||
@@ -3950,9 +3855,7 @@ class MedeiaCLI:
|
||||
# the cmdlet system without Typer trying to parse them.
|
||||
try:
|
||||
names = list_cmdlet_names()
|
||||
skip = {"search-provider",
|
||||
"pipeline",
|
||||
"repl"}
|
||||
skip = {"pipeline", "repl"}
|
||||
for nm in names:
|
||||
if not nm or nm in skip:
|
||||
continue
|
||||
@@ -3964,7 +3867,7 @@ class MedeiaCLI:
|
||||
cmd_name,
|
||||
context_settings={
|
||||
"ignore_unknown_options": True,
|
||||
"allow_extra_args": True
|
||||
"allow_extra_args": True,
|
||||
},
|
||||
)
|
||||
def _handler(ctx: typer.Context):
|
||||
@@ -4119,6 +4022,13 @@ Come to love it when others take what you share, as there is no greater joy
|
||||
block = provider_cfg.get(str(name).strip().lower())
|
||||
return isinstance(block, dict) and bool(block)
|
||||
|
||||
def _has_tool(cfg: dict, name: str) -> bool:
|
||||
tool_cfg = cfg.get("tool")
|
||||
if not isinstance(tool_cfg, dict):
|
||||
return False
|
||||
block = tool_cfg.get(str(name).strip().lower())
|
||||
return isinstance(block, dict) and bool(block)
|
||||
|
||||
def _ping_url(url: str, timeout: float = 3.0) -> tuple[bool, str]:
|
||||
try:
|
||||
from API.HTTP import HTTPClient
|
||||
@@ -4542,6 +4452,45 @@ Come to love it when others take what you share, as there is no greater joy
|
||||
except Exception as exc:
|
||||
_add_startup_check("ERROR", "Cookies", detail=str(exc))
|
||||
|
||||
# Tool checks (configured via [tool=...])
|
||||
if _has_tool(config, "florencevision"):
|
||||
try:
|
||||
tool_cfg = config.get("tool")
|
||||
fv_cfg = tool_cfg.get("florencevision") if isinstance(tool_cfg, dict) else None
|
||||
enabled = bool(fv_cfg.get("enabled")) if isinstance(fv_cfg, dict) else False
|
||||
if not enabled:
|
||||
_add_startup_check(
|
||||
"DISABLED",
|
||||
"FlorenceVision",
|
||||
provider="tool",
|
||||
detail="Not enabled",
|
||||
)
|
||||
else:
|
||||
from SYS.optional_deps import florencevision_missing_modules
|
||||
|
||||
missing = florencevision_missing_modules()
|
||||
if missing:
|
||||
_add_startup_check(
|
||||
"DISABLED",
|
||||
"FlorenceVision",
|
||||
provider="tool",
|
||||
detail="Missing: " + ", ".join(missing),
|
||||
)
|
||||
else:
|
||||
_add_startup_check(
|
||||
"ENABLED",
|
||||
"FlorenceVision",
|
||||
provider="tool",
|
||||
detail="Ready",
|
||||
)
|
||||
except Exception as exc:
|
||||
_add_startup_check(
|
||||
"DISABLED",
|
||||
"FlorenceVision",
|
||||
provider="tool",
|
||||
detail=str(exc),
|
||||
)
|
||||
|
||||
if startup_table.rows:
|
||||
stdout_console().print()
|
||||
stdout_console().print(startup_table)
|
||||
@@ -4709,7 +4658,7 @@ Come to love it when others take what you share, as there is no greater joy
|
||||
if last_table is None:
|
||||
last_table = ctx.get_last_result_table()
|
||||
|
||||
# Auto-refresh search-store tables when navigating back,
|
||||
# Auto-refresh search-file tables when navigating back,
|
||||
# so row payloads (titles/tags) reflect latest store state.
|
||||
try:
|
||||
src_cmd = (
|
||||
@@ -4720,7 +4669,7 @@ Come to love it when others take what you share, as there is no greater joy
|
||||
if (isinstance(src_cmd,
|
||||
str)
|
||||
and src_cmd.lower().replace("_",
|
||||
"-") == "search-store"):
|
||||
"-") == "search-file"):
|
||||
src_args = (
|
||||
getattr(last_table,
|
||||
"source_args",
|
||||
@@ -4748,7 +4697,7 @@ Come to love it when others take what you share, as there is no greater joy
|
||||
else:
|
||||
ctx.set_current_command_text(
|
||||
" ".join(
|
||||
["search-store",
|
||||
["search-file",
|
||||
*cleaned_args]
|
||||
).strip()
|
||||
)
|
||||
@@ -4756,7 +4705,7 @@ Come to love it when others take what you share, as there is no greater joy
|
||||
pass
|
||||
try:
|
||||
self._cmdlet_executor.execute(
|
||||
"search-store",
|
||||
"search-file",
|
||||
cleaned_args + ["--refresh"]
|
||||
)
|
||||
finally:
|
||||
@@ -4768,7 +4717,7 @@ Come to love it when others take what you share, as there is no greater joy
|
||||
continue
|
||||
except Exception as exc:
|
||||
print(
|
||||
f"Error refreshing search-store table: {exc}",
|
||||
f"Error refreshing search-file table: {exc}",
|
||||
file=sys.stderr
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user