huge refactor of the entire codebase, with the goal of improving maintainability, readability, and extensibility. This commit includes changes to almost every file in the project, including:

This commit is contained in:
2026-04-19 00:41:09 -07:00
parent d9e736172a
commit bafd37fdfb
50 changed files with 3258 additions and 4177 deletions
+18 -21
View File
@@ -199,10 +199,10 @@ class SharedArgs:
type="string",
description="http parser",
)
PROVIDER = CmdletArg(
name="provider",
PLUGIN = CmdletArg(
name="plugin",
type="string",
description="selects provider",
description="selects plugin",
)
@staticmethod
@@ -538,10 +538,13 @@ class Cmdlet:
elif low in flags.get('tag', set()):
# handle tag
"""
return {
arg.name: self.get_flags(arg.name)
for arg in self.arg
}
registry: Dict[str, set[str]] = {}
for arg in self.arg:
try:
registry[arg.name] = {str(flag).lower() for flag in arg.to_flags()}
except Exception:
registry[arg.name] = {flag.lower() for flag in self.get_flags(arg.name)}
return registry
# Tag groups cache (loaded from JSON config file)
@@ -642,10 +645,14 @@ def parse_cmdlet_args(args: Sequence[str],
else:
flagged_args.append(spec)
# Register all prefix variants for flagged lookup
arg_spec_map[canonical_name.lower()] = canonical_name # bare name
arg_spec_map[f"-{canonical_name}".lower()] = canonical_name # single dash
arg_spec_map[f"--{canonical_name}".lower()] = canonical_name # double dash
# Register all supported flag variants, including legacy aliases.
arg_spec_map[canonical_name.lower()] = canonical_name # bare canonical name
try:
for flag in spec.to_flags():
arg_spec_map[str(flag).lower()] = canonical_name
except Exception:
arg_spec_map[f"-{canonical_name}".lower()] = canonical_name
arg_spec_map[f"--{canonical_name}".lower()] = canonical_name
# Parse arguments
i = 0
@@ -3143,16 +3150,6 @@ def register_url_with_local_library(
"""
# Folder store removed; local library URL registration is disabled.
return False
try:
# Provider-specific implementation lives with the provider code.
from Provider.tidal_manifest import resolve_tidal_manifest_path
except Exception: # pragma: no cover
def resolve_tidal_manifest_path(item: Any) -> Optional[str]:
_ = item
return None
def check_url_exists_in_storage(
urls: Sequence[str],
storage: Any,
+79 -62
View File
@@ -176,14 +176,14 @@ class Add_File(Cmdlet):
super().__init__(
name="add-file",
summary=
"Ingest a local media file to a store backend, file provider, or local directory.",
"Ingest a local media file to a store backend, upload plugin, or local directory.",
usage=
"add-file (-path <filepath> | <piped>) (-storage <location> | -provider <fileprovider>) [-delete]",
"add-file (-path <filepath> | <piped>) (-storage <location> | -plugin <upload-plugin>) [-delete]",
arg=[
SharedArgs.PATH,
SharedArgs.STORE,
SharedArgs.URL,
SharedArgs.PROVIDER,
SharedArgs.PLUGIN,
CmdletArg(
name="delete",
type="flag",
@@ -198,7 +198,7 @@ class Add_File(Cmdlet):
" hydrus: Upload to Hydrus database with metadata tagging",
" local: Copy file to local directory",
" <path>: Copy file to specified directory",
"- File provider options (use -provider):",
"- Upload plugin options (use -plugin):",
" 0x0: Upload to 0x0.st for temporary hosting",
" file.io: Upload to file.io for temporary hosting",
" internetarchive: Upload to archive.org (optional tag: ia:<identifier> to upload into an existing item)",
@@ -224,13 +224,13 @@ class Add_File(Cmdlet):
path_arg = parsed.get("path")
location = parsed.get("store")
source_url_arg = parsed.get("url")
provider_name = parsed.get("provider")
plugin_name = parsed.get("plugin")
delete_after = parsed.get("delete", False)
# Convenience: when piping a file into add-file, allow `-path <existing dir>`
# to act as the destination export directory.
# Example: screen-shot "https://..." | add-file -path "C:\Users\Admin\Desktop"
if path_arg and not location and not provider_name:
if path_arg and not location and not plugin_name:
try:
candidate_dir = Path(str(path_arg))
if candidate_dir.exists() and candidate_dir.is_dir():
@@ -263,7 +263,7 @@ class Add_File(Cmdlet):
dir_scan_results: Optional[List[Dict[str, Any]]] = None
explicit_path_list_results: Optional[List[Dict[str, Any]]] = None
if path_arg and location and not provider_name:
if path_arg and location and not plugin_name:
# Support comma-separated path lists: -path "file1,file2,file3"
# This is the mechanism used by @N expansion for directory tables.
try:
@@ -403,7 +403,7 @@ class Add_File(Cmdlet):
("result_type", type(result).__name__),
("items", total_items),
("location", location),
("provider", provider_name),
("plugin", plugin_name),
("delete", delete_after),
],
border_style="cyan",
@@ -599,8 +599,8 @@ class Add_File(Cmdlet):
export_destination=(Path(location) if location and not is_storage_backend_location else None),
store_instance=storage_registry,
)
if not media_path and provider_name:
media_path, file_hash, temp_dir_to_cleanup = Add_File._download_provider_source(
if not media_path and plugin_name:
media_path, file_hash, temp_dir_to_cleanup = Add_File._download_piped_source(
pipe_obj, config, storage_registry
)
if media_path:
@@ -610,7 +610,7 @@ class Add_File(Cmdlet):
[
("path", media_path),
("hash", file_hash or "N/A"),
("provider", provider_name or "local"),
("plugin", plugin_name or "local"),
],
border_style="green",
)
@@ -635,10 +635,10 @@ class Add_File(Cmdlet):
progress.step("hashing file")
progress.step("ingesting file")
if provider_name:
code = self._handle_provider_upload(
if plugin_name:
code = self._handle_plugin_upload(
media_path,
provider_name,
plugin_name,
pipe_obj,
config,
delete_after_item
@@ -1365,7 +1365,7 @@ class Add_File(Cmdlet):
hash_hint = get_field(result, "hash") or get_field(result, "file_hash") or getattr(pipe_obj, "hash", None)
return candidate, hash_hint, None
downloaded_path, hash_hint, tmp_dir = Add_File._maybe_download_provider_result(
downloaded_path, hash_hint, tmp_dir = Add_File._maybe_download_plugin_result(
result,
pipe_obj,
config,
@@ -1393,45 +1393,41 @@ class Add_File(Cmdlet):
return normalized
@staticmethod
def _maybe_download_provider_result(
def _maybe_download_plugin_result(
result: Any,
pipe_obj: models.PipeObject,
config: Dict[str, Any],
) -> Tuple[Optional[Path], Optional[str], Optional[Path]]:
provider_key = None
plugin_key = None
for source in (
pipe_obj.provider,
get_field(result, "plugin"),
get_field(result, "provider"),
get_field(result, "table"),
):
candidate = Add_File._normalize_provider_key(source)
if candidate:
provider_key = candidate
plugin_key = candidate
break
if not provider_key:
if not plugin_key:
return None, None, None
provider = get_search_provider(provider_key, config)
if provider is None:
from ProviderCore.registry import get_search_plugin
plugin = get_search_plugin(plugin_key, config)
if plugin is None:
return None, None, None
# Check for specialized download helper (used by AllDebrid and potentially others)
handler = getattr(provider, "download_for_pipe_result", None)
if not callable(handler):
# Fallback: check class if it's a classmethod and instance didn't have it (unlikely but safe)
handler = getattr(type(provider), "download_for_pipe_result", None)
if callable(handler):
try:
return handler(result, pipe_obj, config)
except Exception as exc:
debug(f"[add-file] Provider '{provider_key}' download helper failed: {exc}")
try:
return plugin.resolve_pipe_result_download(result, pipe_obj)
except Exception as exc:
debug(f"[add-file] Plugin '{plugin_key}' download helper failed: {exc}")
return None, None, None
@staticmethod
def _download_provider_source(
def _download_piped_source(
pipe_obj: models.PipeObject,
config: Dict[str, Any],
store_instance: Optional[Any],
@@ -2152,23 +2148,23 @@ class Add_File(Cmdlet):
return 0
@staticmethod
def _handle_provider_upload(
def _handle_plugin_upload(
media_path: Path,
provider_name: str,
plugin_name: str,
pipe_obj: models.PipeObject,
config: Dict[str,
Any],
delete_after: bool,
) -> int:
"""Handle uploading to a file provider (e.g. 0x0)."""
from ProviderCore.registry import get_file_provider
"""Handle uploading via an upload plugin (e.g. 0x0)."""
from ProviderCore.registry import get_upload_plugin
log(f"Uploading via {provider_name}: {media_path.name}", file=sys.stderr)
log(f"Uploading via {plugin_name}: {media_path.name}", file=sys.stderr)
try:
file_provider = get_file_provider(provider_name, config)
file_provider = get_upload_plugin(plugin_name, config)
if not file_provider:
log(f"File provider '{provider_name}' not available", file=sys.stderr)
log(f"Upload plugin '{plugin_name}' not available", file=sys.stderr)
return 1
hoster_url = file_provider.upload(str(media_path), pipe_obj=pipe_obj)
@@ -2183,8 +2179,8 @@ class Add_File(Cmdlet):
# Update PipeObject and emit
extra_updates: Dict[str,
Any] = {
"provider": provider_name,
"provider_url": hoster_url,
"plugin": plugin_name,
"plugin_url": hoster_url,
}
if isinstance(pipe_obj.extra, dict):
# Also track hoster URL as a url for downstream steps
@@ -2197,7 +2193,7 @@ class Add_File(Cmdlet):
Add_File._update_pipe_object_destination(
pipe_obj,
hash_value=f_hash or "unknown",
store=provider_name or "provider",
store=plugin_name or "plugin",
path=file_path,
tag=pipe_obj.tag,
title=pipe_obj.title or (media_path.name if media_path else None),
@@ -2445,9 +2441,6 @@ class Add_File(Cmdlet):
try:
adder = getattr(backend, "add_tag", None)
if callable(adder):
debug(
f"[add-file] Applying {len(tags)} tag(s) post-upload to {backend_name}"
)
adder(resolved_hash, list(tags))
except Exception as exc:
log(f"[add-file] Post-upload tagging failed for {backend_name}: {exc}", file=sys.stderr)
@@ -2479,48 +2472,72 @@ class Add_File(Cmdlet):
try:
setter = getattr(backend, "set_note", None)
if callable(setter):
debug(
f"[add-file] Writing sub note (len={len(str(sub_note))}) to {backend_name}:{resolved_hash}"
)
setter(resolved_hash, "sub", sub_note)
except Exception as exc:
debug(f"[add-file] sub note write failed: {exc}")
debug_panel(
"add-file note write failed",
[
("store", backend_name),
("hash", resolved_hash),
("note", "sub"),
("error", exc),
],
border_style="yellow",
)
lyric_note = Add_File._get_note_text(result, pipe_obj, "lyric")
if lyric_note:
try:
setter = getattr(backend, "set_note", None)
if callable(setter):
debug(
f"[add-file] Writing lyric note (len={len(str(lyric_note))}) to {backend_name}:{resolved_hash}"
)
setter(resolved_hash, "lyric", lyric_note)
except Exception as exc:
debug(f"[add-file] lyric note write failed: {exc}")
debug_panel(
"add-file note write failed",
[
("store", backend_name),
("hash", resolved_hash),
("note", "lyric"),
("error", exc),
],
border_style="yellow",
)
chapters_note = Add_File._get_note_text(result, pipe_obj, "chapters")
if chapters_note:
try:
setter = getattr(backend, "set_note", None)
if callable(setter):
debug(
f"[add-file] Writing chapters note (len={len(str(chapters_note))}) to {backend_name}:{resolved_hash}"
)
setter(resolved_hash, "chapters", chapters_note)
except Exception as exc:
debug(f"[add-file] chapters note write failed: {exc}")
debug_panel(
"add-file note write failed",
[
("store", backend_name),
("hash", resolved_hash),
("note", "chapters"),
("error", exc),
],
border_style="yellow",
)
caption_note = Add_File._get_note_text(result, pipe_obj, "caption")
if caption_note:
try:
setter = getattr(backend, "set_note", None)
if callable(setter):
debug(
f"[add-file] Writing caption note (len={len(str(caption_note))}) to {backend_name}:{resolved_hash}"
)
setter(resolved_hash, "caption", caption_note)
except Exception as exc:
debug(f"[add-file] caption note write failed: {exc}")
debug_panel(
"add-file note write failed",
[
("store", backend_name),
("hash", resolved_hash),
("note", "caption"),
("error", exc),
],
border_style="yellow",
)
meta: Dict[str,
Any] = {}
+205 -2023
View File
File diff suppressed because it is too large Load Diff
+141 -869
View File
File diff suppressed because it is too large Load Diff
+13 -47
View File
@@ -154,47 +154,7 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
if urls_to_download and len(urls_to_download) >= 2:
try:
# Compute a batch hint (audio vs video + single-format id) once.
mode_hint: Optional[str] = None
forced_format: Optional[str] = None
try:
from tool.ytdlp import YtDlpTool, list_formats
sample_url = urls_to_download[0]
cookiefile = None
try:
cookie_path = YtDlpTool(config).resolve_cookiefile()
if cookie_path is not None and cookie_path.is_file():
cookiefile = str(cookie_path)
except Exception:
cookiefile = None
fmts = list_formats(
sample_url,
no_playlist=False,
playlist_items=None,
cookiefile=cookiefile
)
if isinstance(fmts, list) and fmts:
has_video = False
for f in fmts:
if not isinstance(f, dict):
continue
vcodec = str(f.get("vcodec", "none") or "none").strip().lower()
if vcodec and vcodec != "none":
has_video = True
break
mode_hint = "video" if has_video else "audio"
if len(fmts) == 1 and isinstance(fmts[0], dict):
fid = str(fmts[0].get("format_id") or "").strip()
if fid:
forced_format = fid
except Exception:
mode_hint = None
forced_format = None
from cmdlet.download_file import Download_File
from ProviderCore.registry import get_plugin_for_url
expanded: List[Dict[str, Any]] = []
downloaded_any = False
@@ -207,12 +167,18 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
expanded.append(it)
continue
downloaded = Download_File.download_streaming_url_as_pipe_objects(
u,
config,
mode_hint=mode_hint,
ytdl_format_hint=forced_format,
)
downloaded = []
try:
plugin = get_plugin_for_url(u, config)
except Exception:
plugin = None
if plugin is not None and hasattr(plugin, "download_url_as_pipe_objects"):
try:
downloaded = plugin.download_url_as_pipe_objects(u)
except TypeError:
downloaded = plugin.download_url_as_pipe_objects(u, output_dir=None)
except Exception:
downloaded = []
if downloaded:
expanded.extend(downloaded)
downloaded_any = True
+15 -15
View File
@@ -7,7 +7,7 @@ from . import _shared as sh
from SYS.logger import log
from SYS import pipeline as ctx
from SYS.result_table_adapters import get_provider
from SYS.result_table_adapters import get_plugin
from SYS.result_table_renderers import RichRenderer
Cmdlet = sh.Cmdlet
@@ -16,19 +16,19 @@ parse_cmdlet_args = sh.parse_cmdlet_args
CMDLET = Cmdlet(
name="provider-table",
summary="Render a provider's result set and optionally run a follow-up cmdlet using the selected row.",
usage="provider-table -provider <name> [-sample] [-select <n>] [-run-cmd <name>]",
name="plugin-table",
summary="Render a plugin's result set and optionally run a follow-up cmdlet using the selected row.",
usage="plugin-table -plugin <name> [-sample] [-select <n>] [-run-cmd <name>]",
arg=[
CmdletArg("provider", type="string", description="Provider name to render (default: example)"),
CmdletArg("sample", type="flag", description="Use provider sample/demo items when available."),
CmdletArg("plugin", type="string", description="Plugin name to render (default: example)"),
CmdletArg("sample", type="flag", description="Use plugin sample/demo items when available."),
CmdletArg("select", type="int", description="1-based row index to select and use for follow-up command."),
CmdletArg("run-cmd", type="string", description="Cmdlet to invoke with the selected row's selector args."),
],
detail=[
"Use a registered provider to build a table and optionally run another cmdlet with selection args.",
"Use a registered plugin to build a table and optionally run another cmdlet with selection args.",
"Emits pipeline-friendly dicts enriched with `_selection_args` so you can use @N syntax to select and chain.",
"Example: provider-table -provider example -sample | @1 | add-file -store my_store",
"Example: plugin-table -plugin example -sample | @1 | add-file -store my_store",
],
)
@@ -36,15 +36,15 @@ CMDLET = Cmdlet(
def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
parsed = parse_cmdlet_args(args, CMDLET)
provider_name = parsed.get("provider") or "example"
plugin_name = parsed.get("plugin") or "example"
use_sample = bool(parsed.get("sample", False))
run_cmd = parsed.get("run-cmd")
select_raw = parsed.get("select")
try:
provider = get_provider(provider_name)
provider = get_plugin(plugin_name)
except Exception:
log(f"Unknown provider: {provider_name}", file=sys.stderr)
log(f"Unknown plugin: {plugin_name}", file=sys.stderr)
return 1
# Obtain items to feed to the adapter
@@ -55,23 +55,23 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
mod = __import__(provider.adapter.__module__, fromlist=["*"])
items = getattr(mod, "SAMPLE_ITEMS", None)
if items is None:
log("Provider does not expose SAMPLE_ITEMS; no sample available", file=sys.stderr)
log("Plugin does not expose SAMPLE_ITEMS; no sample available", file=sys.stderr)
return 1
except Exception:
log("Failed to load provider sample", file=sys.stderr)
log("Failed to load plugin sample", file=sys.stderr)
return 1
else:
# Require input for non-sample runs
inputs = list(result) if isinstance(result, Iterable) else []
if not inputs:
log("No input provided. Use -sample for demo or pipe provider items in.", file=sys.stderr)
log("No input provided. Use -sample for demo or pipe plugin items in.", file=sys.stderr)
return 1
items = inputs
try:
table = provider.build_table(items)
except Exception as exc:
log(f"Provider '{provider.name}' failed: {exc}", file=sys.stderr)
log(f"Plugin '{provider.name}' failed: {exc}", file=sys.stderr)
return 1
# Emit rows for downstream pipeline consumption (pipable behavior).
+31 -85
View File
@@ -15,7 +15,7 @@ from urllib.parse import urlparse, parse_qs, unquote, urljoin
from SYS.logger import log, debug
from SYS.payload_builders import build_file_result_payload, normalize_file_extension
from ProviderCore.registry import get_search_provider, list_search_providers
from ProviderCore.registry import get_search_plugin, list_search_plugins
from SYS.rich_display import (
show_provider_config_panel,
show_store_config_panel,
@@ -169,8 +169,8 @@ class search_file(Cmdlet):
def __init__(self) -> None:
super().__init__(
name="search-file",
summary="Search storage backends (Hydrus) or external providers (via -provider).",
usage="search-file [-query <query>] [-store BACKEND] [-limit N] [-provider NAME]",
summary="Search storage backends (Hydrus) or external plugins (via -plugin).",
usage="search-file [-query <query>] [-store BACKEND] [-limit N] [-plugin NAME]",
arg=[
CmdletArg(
"limit",
@@ -179,11 +179,7 @@ class search_file(Cmdlet):
),
SharedArgs.STORE,
SharedArgs.QUERY,
CmdletArg(
"provider",
type="string",
description="External provider name (e.g., tidal, youtube, soulseek, etc)",
),
SharedArgs.PLUGIN,
CmdletArg(
"open",
type="integer",
@@ -209,10 +205,10 @@ class search_file(Cmdlet):
"search-file 'example.com/path' -query 'ext:pdf' # Web: site:example.com filetype:pdf",
"search-file -query 'site:example.com filetype:epub history' # Web: site-scoped search",
"",
"Provider search (-provider):",
"search-file -provider youtube 'tutorial' # Search YouTube provider",
"search-file -provider alldebrid '*' # List AllDebrid magnets",
"search-file -provider alldebrid -open 123 '*' # Show files for a magnet",
"Plugin search (-plugin):",
"search-file -plugin youtube 'tutorial' # Search YouTube plugin",
"search-file -plugin alldebrid '*' # List AllDebrid magnets",
"search-file -plugin alldebrid -open 123 '*' # Show files for a magnet",
],
exec=self.run,
)
@@ -1451,10 +1447,10 @@ class search_file(Cmdlet):
self._set_storage_display_columns(payload)
return payload
def _run_provider_search(
def _run_plugin_search(
self,
*,
provider_name: str,
plugin_name: str,
query: str,
limit: int,
limit_set: bool,
@@ -1463,9 +1459,9 @@ class search_file(Cmdlet):
refresh_mode: bool,
config: Dict[str, Any],
) -> int:
"""Execute external provider search."""
"""Execute external plugin search."""
if not provider_name or not query:
if not plugin_name or not query:
from SYS import pipeline as ctx_mod
progress = None
if hasattr(ctx_mod, "get_pipeline_state"):
@@ -1476,10 +1472,10 @@ class search_file(Cmdlet):
except Exception:
pass
log("Error: search-file -provider requires both provider and query", file=sys.stderr)
log("Error: search-file -plugin requires both plugin and query", file=sys.stderr)
log(f"Usage: {self.usage}", file=sys.stderr)
providers_map = list_search_providers(config)
providers_map = list_search_plugins(config)
available = [n for n, a in providers_map.items() if a]
unconfigured = [n for n, a in providers_map.items() if not a]
@@ -1500,7 +1496,7 @@ class search_file(Cmdlet):
if hasattr(ctx_mod, "get_pipeline_state"):
progress = ctx_mod.get_pipeline_state().live_progress
provider = get_search_provider(provider_name, config)
provider = get_search_plugin(plugin_name, config)
if not provider:
if progress:
try:
@@ -1508,9 +1504,9 @@ class search_file(Cmdlet):
except Exception:
pass
show_provider_config_panel([provider_name])
show_provider_config_panel([plugin_name])
providers_map = list_search_providers(config)
providers_map = list_search_plugins(config)
available = [n for n, a in providers_map.items() if a]
if available:
show_available_providers_panel(available)
@@ -1522,7 +1518,7 @@ class search_file(Cmdlet):
worker_id,
"search-file",
title=f"Search: {query}",
description=f"Provider: {provider_name}, Query: {query}",
description=f"Plugin: {plugin_name}, Query: {query}",
)
except Exception:
pass
@@ -1532,7 +1528,7 @@ class search_file(Cmdlet):
from SYS.result_table import Table
provider_text = str(provider_name or "").strip()
provider_text = str(plugin_name or "").strip()
provider_lower = provider_text.lower()
# Dynamic query/filter extraction via provider
@@ -1564,9 +1560,9 @@ class search_file(Cmdlet):
source_cmd, source_args = provider.get_source_command(args_list)
table.set_source_command(source_cmd, source_args)
debug(f"[search-file] Calling {provider_name}.search(filters={search_filters})")
debug(f"[search-file] Calling {plugin_name}.search(filters={search_filters})")
results = provider.search(query, limit=limit, filters=search_filters or None)
debug(f"[search-file] {provider_name} -> {len(results or [])} result(s)")
debug(f"[search-file] {plugin_name} -> {len(results or [])} result(s)")
# Allow providers to apply provider-specific UX transforms (e.g. auto-expansion)
try:
@@ -1615,7 +1611,7 @@ class search_file(Cmdlet):
# Ensure provider source is present so downstream cmdlets (select) can resolve provider
if "source" not in item_dict:
item_dict["source"] = provider_name
item_dict["source"] = plugin_name
row_index = len(table.rows)
table.add_result(search_result)
@@ -1636,7 +1632,7 @@ class search_file(Cmdlet):
return 0
except Exception as exc:
log(f"Error searching provider '{provider_name}': {exc}", file=sys.stderr)
log(f"Error searching plugin '{plugin_name}': {exc}", file=sys.stderr)
import traceback
debug(traceback.format_exc())
@@ -1728,9 +1724,9 @@ class search_file(Cmdlet):
f.lower()
for f in (flag_registry.get("limit") or {"-limit", "--limit"})
}
provider_flags = {
plugin_flags = {
f.lower()
for f in (flag_registry.get("provider") or {"-provider", "--provider"})
for f in (flag_registry.get("plugin") or {"-plugin", "--plugin"})
}
open_flags = {
f.lower()
@@ -1740,7 +1736,7 @@ class search_file(Cmdlet):
# Parse arguments
query = ""
storage_backend: Optional[str] = None
provider_name: Optional[str] = None
plugin_name: Optional[str] = None
open_id: Optional[int] = None
limit = 100
limit_set = False
@@ -1756,8 +1752,8 @@ class search_file(Cmdlet):
query = f"{query} {chunk}".strip() if query else chunk
i += 2
continue
if low in provider_flags and i + 1 < len(args_list):
provider_name = args_list[i + 1]
if low in plugin_flags and i + 1 < len(args_list):
plugin_name = args_list[i + 1]
i += 2
continue
if low in open_flags and i + 1 < len(args_list):
@@ -1790,9 +1786,9 @@ class search_file(Cmdlet):
query = query.strip()
if provider_name:
return self._run_provider_search(
provider_name=provider_name,
if plugin_name:
return self._run_plugin_search(
plugin_name=plugin_name,
query=query,
limit=limit,
limit_set=limit_set,
@@ -1814,56 +1810,6 @@ class search_file(Cmdlet):
if store_filter and not storage_backend:
storage_backend = store_filter
# If the user accidentally used `-store <provider>` or `store:<provider>`,
# prefer to treat it as a provider search (providers like 'alldebrid' are not store backends).
try:
from Store.registry import list_configured_backend_names
providers_map = list_search_providers(config)
configured = list_configured_backend_names(config or {})
if storage_backend:
matched = None
storage_hint = self._normalize_lookup_target(storage_backend)
if storage_hint:
for p in (providers_map or {}):
if self._normalize_lookup_target(p) == storage_hint:
matched = p
break
if matched and str(storage_backend) not in configured:
log(f"Note: Treating '-store {storage_backend}' as provider search for '{matched}'", file=sys.stderr)
return self._run_provider_search(
provider_name=matched,
query=query,
limit=limit,
limit_set=limit_set,
open_id=open_id,
args_list=args_list,
refresh_mode=refresh_mode,
config=config,
)
elif store_filter:
matched = None
store_hint = self._normalize_lookup_target(store_filter)
if store_hint:
for p in (providers_map or {}):
if self._normalize_lookup_target(p) == store_hint:
matched = p
break
if matched and str(store_filter) not in configured:
log(f"Note: Treating 'store:{store_filter}' as provider search for '{matched}'", file=sys.stderr)
return self._run_provider_search(
provider_name=matched,
query=query,
limit=limit,
limit_set=limit_set,
open_id=open_id,
args_list=args_list,
refresh_mode=refresh_mode,
config=config,
)
except Exception:
# Be conservative: if provider detection fails, fall back to store behaviour
pass
hash_query = parse_hash_query(query)
web_plan = self._build_web_search_plan(