update refactoring and add new features
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -1081,6 +1081,8 @@ class Add_Tag(Cmdlet):
|
||||
ok_add = True
|
||||
|
||||
if not queued_bulk:
|
||||
ok_add = False
|
||||
ok_remove = True
|
||||
try:
|
||||
ok_add = backend.add_tag(
|
||||
resolved_hash,
|
||||
@@ -1088,11 +1090,19 @@ class Add_Tag(Cmdlet):
|
||||
config=config,
|
||||
existing_tags=existing_tag_list,
|
||||
)
|
||||
if not ok_add:
|
||||
log("[add_tag] Warning: Store rejected tag update", file=sys.stderr)
|
||||
except Exception as exc:
|
||||
log(f"[add_tag] Warning: Failed adding tag: {exc}", file=sys.stderr)
|
||||
ok_add = False
|
||||
if tags_to_remove:
|
||||
try:
|
||||
delete_fn = getattr(backend, "delete_tag", None)
|
||||
if callable(delete_fn):
|
||||
ok_remove = delete_fn(resolved_hash, list(tags_to_remove), config=config)
|
||||
except Exception as exc:
|
||||
log(f"[add_tag] Warning: Failed removing tag: {exc}", file=sys.stderr)
|
||||
ok_remove = False
|
||||
if not ok_add and not ok_remove:
|
||||
log("[add_tag] Warning: Store rejected tag update", file=sys.stderr)
|
||||
|
||||
if ok_add and merged_tags:
|
||||
refreshed_list = list(merged_tags)
|
||||
|
||||
Reference in New Issue
Block a user