213 lines
8.9 KiB
Python
213 lines
8.9 KiB
Python
"""Delete file relationships."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Dict, Optional, Sequence
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
from helper.logger import log
|
|
|
|
import pipeline as ctx
|
|
from ._shared import Cmdlet, CmdletArg, parse_cmdlet_args, normalize_result_input
|
|
from helper.local_library import LocalLibrarySearchOptimizer
|
|
from config import get_local_storage_path
|
|
|
|
|
|
def _refresh_relationship_view_if_current(target_hash: Optional[str], target_path: Optional[str], other: Optional[str], config: Dict[str, Any]) -> None:
|
|
"""If the current subject matches the target, refresh relationships via get-relationship."""
|
|
try:
|
|
from cmdlets import get_relationship as get_rel_cmd # type: ignore
|
|
except Exception:
|
|
return
|
|
|
|
try:
|
|
subject = ctx.get_last_result_subject()
|
|
if subject is None:
|
|
return
|
|
|
|
def norm(val: Any) -> str:
|
|
return str(val).lower()
|
|
|
|
target_hashes = [norm(v) for v in [target_hash, other] if v]
|
|
target_paths = [norm(v) for v in [target_path, other] if v]
|
|
|
|
subj_hashes: list[str] = []
|
|
subj_paths: list[str] = []
|
|
if isinstance(subject, dict):
|
|
subj_hashes = [norm(v) for v in [subject.get("hydrus_hash"), subject.get("hash"), subject.get("hash_hex"), subject.get("file_hash")] if v]
|
|
subj_paths = [norm(v) for v in [subject.get("file_path"), subject.get("path"), subject.get("target")] if v]
|
|
else:
|
|
subj_hashes = [norm(getattr(subject, f, None)) for f in ("hydrus_hash", "hash", "hash_hex", "file_hash") if getattr(subject, f, None)]
|
|
subj_paths = [norm(getattr(subject, f, None)) for f in ("file_path", "path", "target") if getattr(subject, f, None)]
|
|
|
|
is_match = False
|
|
if target_hashes and any(h in subj_hashes for h in target_hashes):
|
|
is_match = True
|
|
if target_paths and any(p in subj_paths for p in target_paths):
|
|
is_match = True
|
|
if not is_match:
|
|
return
|
|
|
|
refresh_args: list[str] = []
|
|
if target_hash:
|
|
refresh_args.extend(["-hash", target_hash])
|
|
get_rel_cmd._run(subject, refresh_args, config)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
|
"""Delete relationships from files.
|
|
|
|
Args:
|
|
result: Input result(s) from previous cmdlet
|
|
args: Command arguments
|
|
config: CLI configuration
|
|
|
|
Returns:
|
|
Exit code (0 = success)
|
|
"""
|
|
try:
|
|
# Parse arguments
|
|
parsed_args = parse_cmdlet_args(args, CMDLET)
|
|
delete_all_flag = parsed_args.get("all", False)
|
|
rel_type_filter = parsed_args.get("type")
|
|
|
|
# Get storage path
|
|
local_storage_path = get_local_storage_path(config)
|
|
if not local_storage_path:
|
|
log("Local storage path not configured", file=sys.stderr)
|
|
return 1
|
|
|
|
# Normalize input
|
|
results = normalize_result_input(result)
|
|
|
|
if not results:
|
|
log("No results to process", file=sys.stderr)
|
|
return 1
|
|
|
|
deleted_count = 0
|
|
|
|
for single_result in results:
|
|
try:
|
|
# Get file path from result
|
|
file_path_from_result = None
|
|
|
|
if isinstance(single_result, dict):
|
|
file_path_from_result = (
|
|
single_result.get("file_path") or
|
|
single_result.get("path") or
|
|
single_result.get("target")
|
|
)
|
|
else:
|
|
file_path_from_result = (
|
|
getattr(single_result, "file_path", None) or
|
|
getattr(single_result, "path", None) or
|
|
getattr(single_result, "target", None) or
|
|
str(single_result)
|
|
)
|
|
|
|
if not file_path_from_result:
|
|
log("Could not extract file path from result", file=sys.stderr)
|
|
return 1
|
|
|
|
file_path_obj = Path(str(file_path_from_result))
|
|
|
|
if not file_path_obj.exists():
|
|
log(f"File not found: {file_path_obj}", file=sys.stderr)
|
|
return 1
|
|
|
|
with LocalLibrarySearchOptimizer(local_storage_path) as db:
|
|
file_id = db.db.get_file_id(file_path_obj)
|
|
|
|
if not file_id:
|
|
log(f"File not in database: {file_path_obj.name}", file=sys.stderr)
|
|
continue
|
|
|
|
# Get current relationships
|
|
cursor = db.db.connection.cursor()
|
|
cursor.execute("""
|
|
SELECT relationships FROM metadata WHERE file_id = ?
|
|
""", (file_id,))
|
|
|
|
row = cursor.fetchone()
|
|
if not row:
|
|
log(f"No relationships found for: {file_path_obj.name}", file=sys.stderr)
|
|
continue
|
|
|
|
relationships_str = row[0]
|
|
if not relationships_str:
|
|
log(f"No relationships found for: {file_path_obj.name}", file=sys.stderr)
|
|
continue
|
|
|
|
try:
|
|
relationships = json.loads(relationships_str)
|
|
except json.JSONDecodeError:
|
|
log(f"Invalid relationship data for: {file_path_obj.name}", file=sys.stderr)
|
|
continue
|
|
|
|
if not isinstance(relationships, dict):
|
|
relationships = {}
|
|
|
|
# Determine what to delete
|
|
if delete_all_flag:
|
|
# Delete all relationships
|
|
deleted_types = list(relationships.keys())
|
|
relationships = {}
|
|
log(f"Deleted all relationships ({len(deleted_types)} types) from: {file_path_obj.name}", file=sys.stderr)
|
|
elif rel_type_filter:
|
|
# Delete specific type
|
|
if rel_type_filter in relationships:
|
|
deleted_count_for_type = len(relationships[rel_type_filter])
|
|
del relationships[rel_type_filter]
|
|
log(f"Deleted {deleted_count_for_type} {rel_type_filter} relationship(s) from: {file_path_obj.name}", file=sys.stderr)
|
|
else:
|
|
log(f"No {rel_type_filter} relationships found for: {file_path_obj.name}", file=sys.stderr)
|
|
continue
|
|
else:
|
|
log("Specify --all to delete all relationships or -type <type> to delete specific type", file=sys.stderr)
|
|
return 1
|
|
|
|
# Save updated relationships
|
|
cursor.execute("""
|
|
INSERT INTO metadata (file_id, relationships)
|
|
VALUES (?, ?)
|
|
ON CONFLICT(file_id) DO UPDATE SET
|
|
relationships = excluded.relationships,
|
|
time_modified = CURRENT_TIMESTAMP
|
|
""", (file_id, json.dumps(relationships) if relationships else None))
|
|
|
|
db.db.connection.commit()
|
|
_refresh_relationship_view_if_current(None, str(file_path_obj), None, config)
|
|
deleted_count += 1
|
|
|
|
except Exception as exc:
|
|
log(f"Error deleting relationship: {exc}", file=sys.stderr)
|
|
return 1
|
|
|
|
log(f"Successfully deleted relationships from {deleted_count} file(s)", file=sys.stderr)
|
|
return 0
|
|
|
|
except Exception as exc:
|
|
log(f"Error in delete-relationship: {exc}", file=sys.stderr)
|
|
return 1
|
|
|
|
|
|
CMDLET = Cmdlet(
|
|
name="delete-relationship",
|
|
summary="Remove relationships from files.",
|
|
usage="@1 | delete-relationship --all OR delete-relationship -path <file> --all OR @1-3 | delete-relationship -type alt",
|
|
args=[
|
|
CmdletArg("path", type="string", description="Specify the local file path (if not piping a result)."),
|
|
CmdletArg("all", type="flag", description="Delete all relationships for the file(s)."),
|
|
CmdletArg("type", type="string", description="Delete specific relationship type ('alt', 'king', 'related'). Default: delete all types."),
|
|
],
|
|
details=[
|
|
"- Delete all relationships: pipe files | delete-relationship --all",
|
|
"- Delete specific type: pipe files | delete-relationship -type alt",
|
|
"- Delete all from file: delete-relationship -path <file> --all",
|
|
],
|
|
)
|