149 lines
5.0 KiB
Python
149 lines
5.0 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import Any, Dict, Optional, Sequence
|
||
|
|
import sys
|
||
|
|
|
||
|
|
from SYS.logger import log
|
||
|
|
|
||
|
|
import pipeline as ctx
|
||
|
|
from ._shared import (
|
||
|
|
Cmdlet,
|
||
|
|
CmdletArg,
|
||
|
|
SharedArgs,
|
||
|
|
normalize_hash,
|
||
|
|
parse_cmdlet_args,
|
||
|
|
normalize_result_input,
|
||
|
|
should_show_help,
|
||
|
|
)
|
||
|
|
from Store import Store
|
||
|
|
from SYS.utils import sha256_file
|
||
|
|
|
||
|
|
|
||
|
|
class Add_Note(Cmdlet):
|
||
|
|
def __init__(self) -> None:
|
||
|
|
super().__init__(
|
||
|
|
name="add-note",
|
||
|
|
summary="Add or set a named note on a file in a store.",
|
||
|
|
usage="add-note -store <store> [-hash <sha256>] <name> <text...>",
|
||
|
|
alias=["set-note", "add_note"],
|
||
|
|
arg=[
|
||
|
|
SharedArgs.STORE,
|
||
|
|
SharedArgs.HASH,
|
||
|
|
CmdletArg("name", type="string", required=True, description="The note name/key to set (e.g. 'comment', 'lyric')."),
|
||
|
|
CmdletArg("text", type="string", required=True, description="Note text/content to store.", variadic=True),
|
||
|
|
],
|
||
|
|
detail=[
|
||
|
|
"- Notes are stored via the selected store backend.",
|
||
|
|
"- For lyrics: store LRC text in a note named 'lyric'.",
|
||
|
|
],
|
||
|
|
exec=self.run,
|
||
|
|
)
|
||
|
|
# Populate dynamic store choices for autocomplete
|
||
|
|
try:
|
||
|
|
SharedArgs.STORE.choices = SharedArgs.get_store_choices(None)
|
||
|
|
except Exception:
|
||
|
|
pass
|
||
|
|
self.register()
|
||
|
|
|
||
|
|
def _resolve_hash(self, raw_hash: Optional[str], raw_path: Optional[str], override_hash: Optional[str]) -> Optional[str]:
|
||
|
|
resolved = normalize_hash(override_hash) if override_hash else normalize_hash(raw_hash)
|
||
|
|
if resolved:
|
||
|
|
return resolved
|
||
|
|
|
||
|
|
if raw_path:
|
||
|
|
try:
|
||
|
|
p = Path(str(raw_path))
|
||
|
|
stem = p.stem
|
||
|
|
if len(stem) == 64 and all(c in "0123456789abcdef" for c in stem.lower()):
|
||
|
|
return stem.lower()
|
||
|
|
if p.exists() and p.is_file():
|
||
|
|
return sha256_file(p)
|
||
|
|
except Exception:
|
||
|
|
return None
|
||
|
|
return None
|
||
|
|
|
||
|
|
def run(self, result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||
|
|
if should_show_help(args):
|
||
|
|
log(f"Cmdlet: {self.name}\nSummary: {self.summary}\nUsage: {self.usage}")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
parsed = parse_cmdlet_args(args, self)
|
||
|
|
|
||
|
|
store_override = parsed.get("store")
|
||
|
|
hash_override = parsed.get("hash")
|
||
|
|
note_name = str(parsed.get("name") or "").strip()
|
||
|
|
text_parts = parsed.get("text")
|
||
|
|
|
||
|
|
if not note_name:
|
||
|
|
log("[add_note] Error: Requires <name>", file=sys.stderr)
|
||
|
|
return 1
|
||
|
|
|
||
|
|
if isinstance(text_parts, list):
|
||
|
|
note_text = " ".join([str(p) for p in text_parts]).strip()
|
||
|
|
else:
|
||
|
|
note_text = str(text_parts or "").strip()
|
||
|
|
|
||
|
|
if not note_text:
|
||
|
|
log("[add_note] Error: Empty note text", file=sys.stderr)
|
||
|
|
return 1
|
||
|
|
|
||
|
|
results = normalize_result_input(result)
|
||
|
|
if not results:
|
||
|
|
if store_override and normalize_hash(hash_override):
|
||
|
|
results = [{"store": str(store_override), "hash": normalize_hash(hash_override)}]
|
||
|
|
else:
|
||
|
|
log("[add_note] Error: Requires piped item(s) or -store and -hash", file=sys.stderr)
|
||
|
|
return 1
|
||
|
|
|
||
|
|
store_registry = Store(config)
|
||
|
|
updated = 0
|
||
|
|
|
||
|
|
for res in results:
|
||
|
|
if not isinstance(res, dict):
|
||
|
|
ctx.emit(res)
|
||
|
|
continue
|
||
|
|
|
||
|
|
store_name = str(store_override or res.get("store") or "").strip()
|
||
|
|
raw_hash = res.get("hash")
|
||
|
|
raw_path = res.get("path")
|
||
|
|
|
||
|
|
if not store_name:
|
||
|
|
log("[add_note] Error: Missing -store and item has no store field", file=sys.stderr)
|
||
|
|
return 1
|
||
|
|
|
||
|
|
resolved_hash = self._resolve_hash(
|
||
|
|
raw_hash=str(raw_hash) if raw_hash else None,
|
||
|
|
raw_path=str(raw_path) if raw_path else None,
|
||
|
|
override_hash=str(hash_override) if hash_override else None,
|
||
|
|
)
|
||
|
|
if not resolved_hash:
|
||
|
|
log("[add_note] Warning: Item missing usable hash; skipping", file=sys.stderr)
|
||
|
|
ctx.emit(res)
|
||
|
|
continue
|
||
|
|
|
||
|
|
try:
|
||
|
|
backend = store_registry[store_name]
|
||
|
|
except Exception as exc:
|
||
|
|
log(f"[add_note] Error: Unknown store '{store_name}': {exc}", file=sys.stderr)
|
||
|
|
return 1
|
||
|
|
|
||
|
|
ok = False
|
||
|
|
try:
|
||
|
|
ok = bool(backend.set_note(resolved_hash, note_name, note_text, config=config))
|
||
|
|
except Exception as exc:
|
||
|
|
log(f"[add_note] Error: Failed to set note: {exc}", file=sys.stderr)
|
||
|
|
ok = False
|
||
|
|
|
||
|
|
if ok:
|
||
|
|
updated += 1
|
||
|
|
|
||
|
|
ctx.emit(res)
|
||
|
|
|
||
|
|
log(f"[add_note] Updated {updated} item(s)", file=sys.stderr)
|
||
|
|
return 0 if updated > 0 else 1
|
||
|
|
|
||
|
|
|
||
|
|
CMDLET = Add_Note()
|
||
|
|
|