177 lines
6.6 KiB
Python
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()
|