Files
2026-05-04 18:41:01 -07:00

177 lines
6.6 KiB
Python

from __future__ import annotations
from typing import Any, Dict, List, Sequence
import sys
from SYS.logger import log
from . import _shared as sh
Cmdlet = sh.Cmdlet
CmdletArg = sh.CmdletArg
SharedArgs = sh.SharedArgs
class Metadata(Cmdlet):
"""Unified metadata command with domain + action routing."""
_ACTION_FLAGS = {
"add": {"-add", "--add"},
"delete": {"-delete", "--delete", "-del", "--del"},
"get": {"-get", "--get"},
"inspect": {"-inspect", "--inspect", "-info", "--info", "-file", "--file"},
}
_DOMAIN_FLAGS = {
"tag": {"-tag", "--tag", "-tags", "--tags"},
"url": {"-url", "--url", "-urls", "--urls"},
"relationship": {
"-relationship",
"--relationship",
"-relationships",
"--relationships",
"-rel",
"--rel",
},
"note": {"-note", "--note", "-notes", "--notes"},
"file": {"-file", "--file"},
}
def __init__(self) -> None:
super().__init__(
name="metadata",
summary="Manage metadata domains with one command",
usage='metadata [-tag|-url|-relationship|-note] (-add|-delete|-get) [args] OR metadata -inspect [args]',
alias=["meta"],
arg=[
SharedArgs.QUERY,
SharedArgs.INSTANCE,
CmdletArg("-tag", type="flag", required=False, description="Metadata tag domain (default if omitted)"),
CmdletArg("-url", type="flag", required=False, description="URL metadata domain"),
CmdletArg("-relationship", type="flag", required=False, description="Relationship metadata domain", alias="rel"),
CmdletArg("-note", type="flag", required=False, description="Note metadata domain"),
CmdletArg("-add", type="flag", required=False, description="Add metadata tag value(s)"),
CmdletArg("-delete", type="flag", required=False, description="Delete metadata tag value(s)", alias="del"),
CmdletArg("-get", type="flag", required=False, description="Read metadata values for selected domain"),
CmdletArg("-inspect", type="flag", required=False, description="Inspect file metadata details", alias="info"),
CmdletArg(
"<tag>[,<tag>...]",
type="string",
required=False,
variadic=True,
description="Tag values for -add/-delete",
),
],
detail=[
"- Use one action flag: -add, -delete, -get, or -inspect.",
"- Domain flags: -tag (default), -url, -relationship, -note.",
"- Examples:",
" metadata -url -add <url>",
" metadata -url -delete <url>",
" metadata -relationship -get",
" metadata -note -add -query \"title:lyric text:...\"",
"- -inspect maps to get-metadata and prints rich metadata details.",
],
exec=self.run,
)
self.register()
@classmethod
def _extract_parts(
cls,
args: Sequence[str],
) -> tuple[str | None, str, List[str], List[str], List[str]]:
matched_actions: List[str] = []
matched_domains: List[str] = []
passthrough: List[str] = []
for token in args or []:
text = str(token or "")
lower = text.strip().lower()
matched = None
for action_name, variants in cls._ACTION_FLAGS.items():
if lower in variants:
matched = action_name
break
if matched:
matched_actions.append(matched)
continue
matched_domain = None
for domain_name, variants in cls._DOMAIN_FLAGS.items():
if lower in variants:
matched_domain = domain_name
break
if matched_domain:
matched_domains.append(matched_domain)
continue
passthrough.append(text)
unique_actions: List[str] = []
for action in matched_actions:
if action not in unique_actions:
unique_actions.append(action)
unique_domains: List[str] = []
for domain in matched_domains:
if domain not in unique_domains:
unique_domains.append(domain)
action = unique_actions[0] if len(unique_actions) == 1 else None
domain = unique_domains[0] if len(unique_domains) == 1 else "tag"
return action, domain, passthrough, unique_actions, unique_domains
def run(self, result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
action, domain, passthrough_args, seen_actions, seen_domains = self._extract_parts(args)
if action is None:
if not seen_actions:
log(
"metadata: missing action flag; choose exactly one of -add, -delete, -get, -inspect",
file=sys.stderr,
)
else:
rendered = ", ".join(f"-{name}" for name in seen_actions)
log(f"metadata: conflicting actions ({rendered}); choose exactly one", file=sys.stderr)
return 1
if len(seen_domains) > 1:
rendered_domains = ", ".join(f"-{name}" for name in seen_domains)
log(f"metadata: conflicting domains ({rendered_domains}); choose one domain", file=sys.stderr)
return 1
if action == "inspect":
from cmdlet.metadata.inspect import run_inspect_action
return run_inspect_action(result, passthrough_args, config)
if domain == "file":
log("metadata: -file only supports -inspect; use metadata -inspect", file=sys.stderr)
return 1
if domain == "tag":
from cmdlet.metadata.tag import run_tag_action
return run_tag_action(action, result, passthrough_args, config)
if domain == "url":
from cmdlet.metadata.url import run_url_action
return run_url_action(action, result, passthrough_args, config)
if domain == "relationship":
from cmdlet.metadata.relationship import run_relationship_action
return run_relationship_action(action, result, passthrough_args, config)
if domain == "note":
from cmdlet.metadata.note import run_note_action
return run_note_action(action, result, passthrough_args, config)
log(f"metadata: unsupported domain '{domain}'", file=sys.stderr)
return 1
CMDLET = Metadata()