This commit is contained in:
2026-05-04 15:58:24 -07:00
parent bca85defa4
commit 3ce339b3c1
6 changed files with 322 additions and 18 deletions
+31 -3
View File
@@ -98,7 +98,7 @@ DEBUG_PIPE_NOTE_PREVIEW_LENGTH = 256
# Used by multiple methods in this file to guard against URL strings being
# treated as local file paths.
_REMOTE_URL_PREFIXES: tuple[str, ...] = (
"http://", "https://", "magnet:", "torrent:", "tidal:", "hydrus:",
"http://", "https://", "ftp://", "ftps://", "magnet:", "torrent:", "tidal:", "hydrus:",
)
@@ -1241,6 +1241,11 @@ class Add_File(Cmdlet):
return None, None
if not url_text.lower().startswith(_REMOTE_URL_PREFIXES):
return None, None
# This helper performs generic HTTP downloads only.
# Non-HTTP schemes (e.g. hydrus://, tidal:) should be handled by
# plugin-specific resolvers via _maybe_download_plugin_result.
if not url_text.lower().startswith(("http://", "https://")):
return None, None
tmp_dir: Optional[Path] = None
try:
@@ -1480,8 +1485,31 @@ class Add_File(Cmdlet):
if candidate:
s = str(candidate).lower()
if s.startswith(_REMOTE_URL_PREFIXES):
log("add-file ingests local files only. Use download-file first.", file=sys.stderr)
return None, None, None
# For remote sources, prefer plugin-specific resolvers first
# (e.g. hydrus://), then generic HTTP fallback.
downloaded_path, hash_hint, tmp_dir = Add_File._maybe_download_plugin_result(
result,
pipe_obj,
config,
deps=deps,
)
if downloaded_path:
pipe_obj.path = str(downloaded_path)
return downloaded_path, hash_hint, tmp_dir
dl_path, tmp_dir = Add_File._download_remote_backend_url(
str(candidate),
pipe_obj,
file_hash=get_field(result, "hash") or get_field(result, "file_hash"),
output_dir=export_destination,
)
if dl_path:
pipe_obj.path = str(dl_path)
hash_hint = get_field(result, "hash") or get_field(result, "file_hash")
return dl_path, hash_hint, tmp_dir
log("add-file could not auto-fetch remote source. Use download-file first.", file=sys.stderr)
return None, None, None
pipe_obj.path = str(candidate)
# Retain hash from input if available to avoid re-hashing
+63 -4
View File
@@ -3,6 +3,7 @@
from __future__ import annotations
from typing import Any, Dict, List, Sequence
import posixpath
import sys
from pathlib import Path
@@ -128,6 +129,27 @@ class Delete_File(sh.Cmdlet):
else:
store = sh.get_field(item, "store")
# Extract plugin/provider identity and full metadata for plugin-level dispatch
provider_name = None
full_metadata: Dict[str, Any] = {}
if isinstance(item, dict):
provider_name = item.get("provider") or item.get("table")
raw_meta = item.get("full_metadata") or item.get("metadata")
if isinstance(raw_meta, dict):
full_metadata = raw_meta
else:
try:
provider_name = sh.get_field(item, "provider") or sh.get_field(item, "table")
except Exception:
pass
try:
raw_meta = sh.get_field(item, "full_metadata") or sh.get_field(item, "metadata")
if isinstance(raw_meta, dict):
full_metadata = raw_meta
except Exception:
pass
provider_name = str(provider_name or "").strip().lower() or None
store_lower = str(store).lower() if store else ""
hydrus_provider = get_plugin("hydrusnetwork", config)
@@ -169,14 +191,51 @@ class Delete_File(sh.Cmdlet):
)
local_deleted = False
_target_str = str(target).strip().lower() if isinstance(target, str) else ""
local_target = (
isinstance(target,
str) and target.strip()
and not str(target).lower().startswith(("http://",
"https://"))
isinstance(target, str) and target.strip()
and not _target_str.startswith(("http://", "https://", "ftp://", "ftps://"))
)
deleted_rows: List[Dict[str, Any]] = []
# --- Plugin-level delete dispatch ---
# When the item originates from a plugin (e.g. FTP), and that plugin exposes
# a delete_file() method, delegate to it instead of attempting a local unlink.
if conserve != "local" and provider_name and not is_hydrus_store:
try:
candidate_plugin = get_plugin(provider_name, config)
plugin_deleter = getattr(candidate_plugin, "delete_file", None) if candidate_plugin else None
if callable(plugin_deleter):
# Prefer ftp_path from full_metadata; fall back to the path/url field
remote = (
full_metadata.get("ftp_path")
or full_metadata.get("selection_url")
or full_metadata.get("ftp_url")
or (str(target).strip() if isinstance(target, str) else "")
)
instance_hint = full_metadata.get("instance") or None
if remote:
plugin_ok = bool(plugin_deleter(remote, instance=instance_hint))
if plugin_ok:
local_deleted = True
size_hint = (
full_metadata.get("size")
or (item.get("size_bytes") if isinstance(item, dict) else None)
or sh.get_field(item, "size_bytes")
)
deleted_rows.append(
{
"title": str(title_val).strip() if title_val else posixpath.basename(str(remote).rstrip("/")),
"store": instance_hint or provider_name,
"hash": hash_hex or "",
"size_bytes": size_hint,
"ext": _get_ext_from_item(),
}
)
return deleted_rows
except Exception:
pass
# If this item references a configured non-Hydrus store backend, prefer deleting
# via the backend API. This supports store items where `path`/`target` is the hash.
if conserve != "local" and store and (not is_hydrus_store):