update refactoring and add new features

This commit is contained in:
2026-07-24 20:55:58 -07:00
parent 7f12bc7f40
commit 03fbbbcf28
69 changed files with 16332 additions and 17600 deletions
+5 -152
View File
@@ -60,8 +60,7 @@ CMDLET = Cmdlet(
"- Mode 2: Use -king to explicitly set which item/hash is the king: @1-3 | add-relationship -king @4",
"- Mode 2b: Use -king and -alt to select both sides from the last table: add-relationship -king @1 -alt @3-5",
"- Mode 3: Read relationships from sidecar tags:",
" - New format: 'relationship: <KING_HASH>,<ALT_HASH>,<ALT_HASH>' (first hash is king)",
" - Legacy: 'relationship: hash(king)<HASH>,hash(alt)<HASH>...'",
" - Format: 'relationship: <KING_HASH>,<ALT_HASH>,<ALT_HASH>' (first hash is king)",
"- Supports three relationship types: king (primary), alt (alternative), related (other versions)",
"- When using -king, all piped items become the specified relationship type to the king",
],
@@ -71,13 +70,10 @@ CMDLET = Cmdlet(
_normalize_hash_hex = sh.normalize_hash
def _extract_relationships_from_tag(tag_value: str) -> Dict[str, list[str]]:
def _extract_relationships_from_tag(tag_value: str) -> Dict[str, list[str]]:
"""Parse relationship tags.
Supported formats:
- New: relationship: <KING_HASH>,<ALT_HASH>,<ALT_HASH>
- Old: relationship: hash(king)<HASH>,hash(alt)<HASH>...
Format: relationship: <KING_HASH>,<ALT_HASH>,<ALT_HASH>
Returns a dict like {"king": ["HASH1"], "alt": ["HASH2"], ...}
"""
result: Dict[str,
@@ -85,20 +81,7 @@ def _extract_relationships_from_tag(tag_value: str) -> Dict[str, list[str]]:
if not isinstance(tag_value, str):
return result
# Match patterns like hash(king)HASH or hash(type)<HASH>
pattern = r"hash\((\w+)\)<?([a-fA-F0-9]{64})>?"
matches = re.findall(pattern, tag_value)
if matches:
for rel_type, hash_value in matches:
normalized = _normalize_hash_hex(hash_value)
if normalized:
if rel_type not in result:
result[rel_type] = []
result[rel_type].append(normalized)
return result
# New format: extract hashes, first is king
# Extract hashes; first is king
hashes = re.findall(r"\b[a-fA-F0-9]{64}\b", tag_value)
hashes = [h.strip().lower() for h in hashes if isinstance(h, str)]
if not hashes:
@@ -833,7 +816,7 @@ def _run(result: Any, _args: Sequence[str], config: Dict[str, Any]) -> int:
hydrus_client.set_relationship(h, king_hash, str(rel_type))
return 0
# Process each item in the list (legacy path-based mode)
# Process each item in the list (path-based mode)
for item in items_to_process:
# Extract hash and path from current item
file_hash = None
@@ -1033,136 +1016,6 @@ def _run(result: Any, _args: Sequence[str], config: Dict[str, Any]) -> int:
return 0
# FILE MODE: Read relationships from sidecar (legacy mode - for -path arg only)
log(
"Note: Use piping mode for easier relationships. Example: 1,2,3 | add-relationship",
file=sys.stderr,
)
# Resolve media path from -path arg or result target
target = getattr(result, "target", None) or getattr(result, "path", None)
media_path = (
arg_path
if arg_path is not None else Path(str(target)) if isinstance(target,
str) else None
)
if media_path is None:
log("Provide -path <file> or pipe a local file result", file=sys.stderr)
return 1
# Validate local file
if str(media_path).lower().startswith(("http://", "https://")):
log("This cmdlet requires a local file path, not a URL", file=sys.stderr)
return 1
if not media_path.exists() or not media_path.is_file():
log(f"File not found: {media_path}", file=sys.stderr)
return 1
# Build Hydrus client
hydrus_provider = get_plugin("hydrusnetwork", config)
try:
hydrus_client = hydrus_provider.get_client() if hydrus_provider is not None else None
except Exception as exc:
log(f"Hydrus client unavailable: {exc}", file=sys.stderr)
return 1
if hydrus_client is None:
log("Hydrus client unavailable", file=sys.stderr)
return 1
# Read sidecar to find relationship tags
sidecar_path = find_sidecar(media_path)
if sidecar_path is None:
log(f"No sidecar found for {media_path.name}", file=sys.stderr)
return 1
try:
_, tags, _ = read_sidecar(sidecar_path)
except Exception as exc:
log(f"Failed to read sidecar: {exc}", file=sys.stderr)
return 1
# Find relationship tags (format: "relationship: hash(king)<HASH>,hash(alt)<HASH>,hash(related)<HASH>")
relationship_tags = [
t for t in tags if isinstance(t, str) and t.lower().startswith("relationship:")
]
if not relationship_tags:
log("No relationship tags found in sidecar", file=sys.stderr)
return 0 # Not an error, just nothing to do
# Get the file hash from result (should have been set by add-file)
file_hash = getattr(result, "hash_hex", None)
if not file_hash:
log("File hash not available (run add-file first)", file=sys.stderr)
return 1
file_hash = _normalize_hash_hex(file_hash)
if not file_hash:
log("Invalid file hash format", file=sys.stderr)
return 1
# Parse relationships from tags and apply them
success_count = 0
error_count = 0
for rel_tag in relationship_tags:
try:
# Parse: "relationship: hash(king)<HASH>,hash(alt)<HASH>,hash(related)<HASH>"
rel_str = rel_tag.split(":", 1)[1].strip() # Get part after "relationship:"
# Parse relationships
rels = _extract_relationships_from_tag(f"relationship: {rel_str}")
# Set the relationships in Hydrus
for rel_type, related_hashes in rels.items():
if not related_hashes:
continue
for related_hash in related_hashes:
# Don't set relationship between hash and itself
if file_hash == related_hash:
continue
try:
hydrus_client.set_relationship(
file_hash,
related_hash,
rel_type
)
log(
f"[add-relationship] Set {rel_type} relationship: "
f"{file_hash} <-> {related_hash}",
file=sys.stderr,
)
success_count += 1
except Exception as exc:
log(
f"Failed to set {rel_type} relationship: {exc}",
file=sys.stderr
)
error_count += 1
except Exception as exc:
log(f"Failed to parse relationship tag: {exc}", file=sys.stderr)
error_count += 1
if success_count > 0:
log(
f"Successfully set {success_count} relationship(s) for {media_path.name}",
file=sys.stderr,
)
ctx.emit(
f"add-relationship: {media_path.name} ({success_count} relationships set)"
)
return 0
elif error_count == 0:
log("No relationships to set", file=sys.stderr)
return 0 # Success with nothing to do
else:
log(f"Failed with {error_count} error(s)", file=sys.stderr)
return 1
# Register cmdlet (no legacy decorator)
CMDLET.exec = _run