df
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
"""Delete-file cmdlet: Delete files from local storage and/or Hydrus."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Sequence
|
||||
@@ -23,12 +24,16 @@ class Delete_File(sh.Cmdlet):
|
||||
super().__init__(
|
||||
name="delete-file",
|
||||
summary="Delete a file locally and/or from Hydrus, including database entries.",
|
||||
usage="delete-file [-query \"hash:<sha256>\"] [-conserve <local|hydrus>] [-lib-root <path>] [reason]",
|
||||
usage='delete-file [-query "hash:<sha256>"] [-conserve <local|hydrus>] [-lib-root <path>] [reason]',
|
||||
alias=["del-file"],
|
||||
arg=[
|
||||
sh.SharedArgs.QUERY,
|
||||
sh.CmdletArg("conserve", description="Choose which copy to keep: 'local' or 'hydrus'."),
|
||||
sh.CmdletArg("lib-root", description="Path to local library root for database cleanup."),
|
||||
sh.CmdletArg(
|
||||
"conserve", description="Choose which copy to keep: 'local' or 'hydrus'."
|
||||
),
|
||||
sh.CmdletArg(
|
||||
"lib-root", description="Path to local library root for database cleanup."
|
||||
),
|
||||
sh.CmdletArg("reason", description="Optional reason for deletion (free text)."),
|
||||
],
|
||||
detail=[
|
||||
@@ -62,7 +67,11 @@ class Delete_File(sh.Cmdlet):
|
||||
title_val = item.get("title") or item.get("name")
|
||||
else:
|
||||
hash_hex_raw = sh.get_field(item, "hash_hex") or sh.get_field(item, "hash")
|
||||
target = sh.get_field(item, "target") or sh.get_field(item, "file_path") or sh.get_field(item, "path")
|
||||
target = (
|
||||
sh.get_field(item, "target")
|
||||
or sh.get_field(item, "file_path")
|
||||
or sh.get_field(item, "path")
|
||||
)
|
||||
title_val = sh.get_field(item, "title") or sh.get_field(item, "name")
|
||||
|
||||
def _get_ext_from_item() -> str:
|
||||
@@ -102,7 +111,7 @@ class Delete_File(sh.Cmdlet):
|
||||
pass
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
store = None
|
||||
if isinstance(item, dict):
|
||||
store = item.get("store")
|
||||
@@ -133,19 +142,29 @@ class Delete_File(sh.Cmdlet):
|
||||
is_hydrus_store = False
|
||||
|
||||
# Backwards-compatible fallback heuristic (older items might only carry a name).
|
||||
if (not is_hydrus_store) and bool(store_lower) and ("hydrus" in store_lower or store_lower in {"home", "work"}):
|
||||
if (
|
||||
(not is_hydrus_store)
|
||||
and bool(store_lower)
|
||||
and ("hydrus" in store_lower or store_lower in {"home", "work"})
|
||||
):
|
||||
is_hydrus_store = True
|
||||
store_label = str(store) if store else "default"
|
||||
hydrus_prefix = f"[hydrusnetwork:{store_label}]"
|
||||
|
||||
|
||||
# For Hydrus files, the target IS the hash
|
||||
if is_hydrus_store and not hash_hex_raw:
|
||||
hash_hex_raw = target
|
||||
|
||||
hash_hex = sh.normalize_hash(override_hash) if override_hash else sh.normalize_hash(hash_hex_raw)
|
||||
hash_hex = (
|
||||
sh.normalize_hash(override_hash) if override_hash else sh.normalize_hash(hash_hex_raw)
|
||||
)
|
||||
|
||||
local_deleted = False
|
||||
local_target = isinstance(target, str) and target.strip() and not str(target).lower().startswith(("http://", "https://"))
|
||||
local_target = (
|
||||
isinstance(target, str)
|
||||
and target.strip()
|
||||
and not str(target).lower().startswith(("http://", "https://"))
|
||||
)
|
||||
deleted_rows: List[Dict[str, Any]] = []
|
||||
|
||||
# If this item references a configured non-Hydrus store backend, prefer deleting
|
||||
@@ -169,11 +188,15 @@ class Delete_File(sh.Cmdlet):
|
||||
try:
|
||||
if hash_candidate and hasattr(backend, "get_file"):
|
||||
candidate_path = backend.get_file(hash_candidate)
|
||||
resolved_path = candidate_path if isinstance(candidate_path, Path) else None
|
||||
resolved_path = (
|
||||
candidate_path if isinstance(candidate_path, Path) else None
|
||||
)
|
||||
except Exception:
|
||||
resolved_path = None
|
||||
|
||||
identifier = hash_candidate or (str(target).strip() if isinstance(target, str) else "")
|
||||
identifier = hash_candidate or (
|
||||
str(target).strip() if isinstance(target, str) else ""
|
||||
)
|
||||
if identifier:
|
||||
deleter = getattr(backend, "delete_file", None)
|
||||
if callable(deleter) and bool(deleter(identifier)):
|
||||
@@ -181,18 +204,27 @@ class Delete_File(sh.Cmdlet):
|
||||
|
||||
size_bytes: int | None = None
|
||||
try:
|
||||
if resolved_path is not None and isinstance(resolved_path, Path) and resolved_path.exists():
|
||||
if (
|
||||
resolved_path is not None
|
||||
and isinstance(resolved_path, Path)
|
||||
and resolved_path.exists()
|
||||
):
|
||||
size_bytes = int(resolved_path.stat().st_size)
|
||||
except Exception:
|
||||
size_bytes = None
|
||||
|
||||
deleted_rows.append(
|
||||
{
|
||||
"title": str(title_val).strip() if title_val else (resolved_path.name if resolved_path else identifier),
|
||||
"title": (
|
||||
str(title_val).strip()
|
||||
if title_val
|
||||
else (resolved_path.name if resolved_path else identifier)
|
||||
),
|
||||
"store": store_label,
|
||||
"hash": hash_candidate or (hash_hex or ""),
|
||||
"size_bytes": size_bytes,
|
||||
"ext": _get_ext_from_item() or (resolved_path.suffix.lstrip(".") if resolved_path else ""),
|
||||
"ext": _get_ext_from_item()
|
||||
or (resolved_path.suffix.lstrip(".") if resolved_path else ""),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -216,7 +248,7 @@ class Delete_File(sh.Cmdlet):
|
||||
local_target = False
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
if conserve != "local" and local_target:
|
||||
path = Path(str(target))
|
||||
size_bytes: int | None = None
|
||||
@@ -225,7 +257,7 @@ class Delete_File(sh.Cmdlet):
|
||||
size_bytes = int(path.stat().st_size)
|
||||
except Exception:
|
||||
size_bytes = None
|
||||
|
||||
|
||||
# If lib_root is provided and this is from a folder store, use the Folder class
|
||||
if lib_root:
|
||||
try:
|
||||
@@ -276,7 +308,7 @@ class Delete_File(sh.Cmdlet):
|
||||
)
|
||||
except Exception as exc:
|
||||
log(f"Local delete failed: {exc}", file=sys.stderr)
|
||||
|
||||
|
||||
# Remove common sidecars regardless of file removal success
|
||||
for sidecar in (
|
||||
path.with_suffix(".tag"),
|
||||
@@ -291,11 +323,11 @@ class Delete_File(sh.Cmdlet):
|
||||
|
||||
hydrus_deleted = False
|
||||
should_try_hydrus = is_hydrus_store
|
||||
|
||||
|
||||
# If conserve is set to hydrus, definitely don't delete
|
||||
if conserve == "hydrus":
|
||||
should_try_hydrus = False
|
||||
|
||||
|
||||
if should_try_hydrus and hash_hex:
|
||||
# Prefer deleting via the resolved store backend when it is a HydrusNetwork store.
|
||||
# This ensures store-specific post-delete hooks run (e.g., clearing Hydrus deletion records).
|
||||
@@ -312,7 +344,10 @@ class Delete_File(sh.Cmdlet):
|
||||
hydrus_deleted = True
|
||||
title_str = str(title_val).strip() if title_val else ""
|
||||
if title_str:
|
||||
debug(f"{hydrus_prefix} Deleted title:{title_str} hash:{hash_hex}", file=sys.stderr)
|
||||
debug(
|
||||
f"{hydrus_prefix} Deleted title:{title_str} hash:{hash_hex}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
else:
|
||||
debug(f"{hydrus_prefix} Deleted hash:{hash_hex}", file=sys.stderr)
|
||||
else:
|
||||
@@ -328,7 +363,10 @@ class Delete_File(sh.Cmdlet):
|
||||
client = candidate
|
||||
except Exception as exc:
|
||||
if not local_deleted:
|
||||
log(f"Hydrus client unavailable for store '{store}': {exc}", file=sys.stderr)
|
||||
log(
|
||||
f"Hydrus client unavailable for store '{store}': {exc}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return False
|
||||
if client is None:
|
||||
if not local_deleted:
|
||||
@@ -365,7 +403,10 @@ class Delete_File(sh.Cmdlet):
|
||||
hydrus_deleted = True
|
||||
title_str = str(title_val).strip() if title_val else ""
|
||||
if title_str:
|
||||
debug(f"{hydrus_prefix} Deleted title:{title_str} hash:{hash_hex}", file=sys.stderr)
|
||||
debug(
|
||||
f"{hydrus_prefix} Deleted title:{title_str} hash:{hash_hex}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
else:
|
||||
debug(f"{hydrus_prefix} Deleted hash:{hash_hex}", file=sys.stderr)
|
||||
except Exception:
|
||||
@@ -411,7 +452,7 @@ class Delete_File(sh.Cmdlet):
|
||||
lib_root: str | None = None
|
||||
reason_tokens: list[str] = []
|
||||
i = 0
|
||||
|
||||
|
||||
while i < len(args):
|
||||
token = args[i]
|
||||
low = str(token).lower()
|
||||
@@ -460,7 +501,7 @@ class Delete_File(sh.Cmdlet):
|
||||
items = result
|
||||
elif result:
|
||||
items = [result]
|
||||
|
||||
|
||||
if not items:
|
||||
log("No items to delete", file=sys.stderr)
|
||||
return 1
|
||||
@@ -468,7 +509,9 @@ class Delete_File(sh.Cmdlet):
|
||||
success_count = 0
|
||||
deleted_rows: List[Dict[str, Any]] = []
|
||||
for item in items:
|
||||
rows = self._process_single_item(item, override_hash, conserve, lib_root, reason, config)
|
||||
rows = self._process_single_item(
|
||||
item, override_hash, conserve, lib_root, reason, config
|
||||
)
|
||||
if rows:
|
||||
success_count += 1
|
||||
deleted_rows.extend(rows)
|
||||
@@ -481,7 +524,9 @@ class Delete_File(sh.Cmdlet):
|
||||
result_row.add_column("Title", row.get("title", ""))
|
||||
result_row.add_column("Store", row.get("store", ""))
|
||||
result_row.add_column("Hash", row.get("hash", ""))
|
||||
result_row.add_column("Size", _format_size(row.get("size_bytes"), integer_only=False))
|
||||
result_row.add_column(
|
||||
"Size", _format_size(row.get("size_bytes"), integer_only=False)
|
||||
)
|
||||
result_row.add_column("Ext", row.get("ext", ""))
|
||||
|
||||
# Display-only: print directly and do not affect selection/history.
|
||||
@@ -504,5 +549,3 @@ class Delete_File(sh.Cmdlet):
|
||||
|
||||
# Instantiate and register the cmdlet
|
||||
Delete_File()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user