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 . import _shared as sh Cmdlet = sh.Cmdlet CmdletArg = sh.CmdletArg SharedArgs = sh.SharedArgs normalize_hash = sh.normalize_hash parse_cmdlet_args = sh.parse_cmdlet_args normalize_result_input = sh.normalize_result_input should_show_help = sh.should_show_help from Store import Store from SYS.utils import sha256_file class Get_Note(Cmdlet): def __init__(self) -> None: super().__init__( name="get-note", summary="List notes on a file in a store.", usage="get-note -store [-query \"hash:\"]", alias=["get-notes", "get_note"], arg=[ SharedArgs.STORE, SharedArgs.QUERY, ], detail=[ "- Notes are retrieved via the selected store backend.", "- Lyrics are stored in a note named 'lyric'.", ], exec=self.run, ) 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") query_hash = sh.parse_single_hash_query(parsed.get("query")) if parsed.get("query") and not query_hash: log("[get_note] Error: -query must be of the form hash:", file=sys.stderr) return 1 results = normalize_result_input(result) if not results: if store_override and query_hash: results = [{"store": str(store_override), "hash": query_hash}] else: log("[get_note] Error: Requires piped item(s) or -store and -query \"hash:\"", file=sys.stderr) return 1 store_registry = Store(config) any_notes = False for res in results: if not isinstance(res, dict): 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("[get_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(query_hash) if query_hash else None, ) if not resolved_hash: continue try: backend = store_registry[store_name] except Exception as exc: log(f"[get_note] Error: Unknown store '{store_name}': {exc}", file=sys.stderr) return 1 notes = {} try: notes = backend.get_note(resolved_hash, config=config) or {} except Exception: notes = {} if not notes: continue any_notes = True # Emit each note as its own row so CLI renders a proper note table for k in sorted(notes.keys(), key=lambda x: str(x).lower()): v = notes.get(k) raw_text = str(v or "") # Keep payload small for IPC/pipes. raw_text = raw_text[:999] preview = " ".join(raw_text.replace("\r", "").split("\n")) ctx.emit( { "store": store_name, "hash": resolved_hash, "note_name": str(k), "note_text": raw_text, "columns": [ ("Name", str(k)), ("Text", preview.strip()), ], } ) if not any_notes: ctx.emit("No notes found.") return 0 CMDLET = Get_Note()