dfdsf
This commit is contained in:
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
||||
import sys
|
||||
import re
|
||||
|
||||
from SYS.logger import log
|
||||
|
||||
@@ -25,13 +26,12 @@ class Add_Note(Cmdlet):
|
||||
super().__init__(
|
||||
name="add-note",
|
||||
summary="Add file store note",
|
||||
usage="add-note -store <store> [-query \"hash:<sha256>\"] <name> <text...>",
|
||||
usage="add-note (-query \"title:<title>,text:<text>\") [ -store <store> -hash <sha256> | <piped> ]",
|
||||
alias=[""],
|
||||
arg=[
|
||||
SharedArgs.STORE,
|
||||
CmdletArg("hash", type="string", required=False, description="Target file hash (sha256). When omitted, uses piped item hash."),
|
||||
SharedArgs.QUERY,
|
||||
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=[
|
||||
"""
|
||||
@@ -47,6 +47,68 @@ class Add_Note(Cmdlet):
|
||||
pass
|
||||
self.register()
|
||||
|
||||
@staticmethod
|
||||
def _commas_to_spaces_outside_quotes(text: str) -> str:
|
||||
buf: List[str] = []
|
||||
quote: Optional[str] = None
|
||||
escaped = False
|
||||
for ch in str(text or ""):
|
||||
if escaped:
|
||||
buf.append(ch)
|
||||
escaped = False
|
||||
continue
|
||||
if ch == "\\" and quote is not None:
|
||||
buf.append(ch)
|
||||
escaped = True
|
||||
continue
|
||||
if ch in ('"', "'"):
|
||||
if quote is None:
|
||||
quote = ch
|
||||
elif quote == ch:
|
||||
quote = None
|
||||
buf.append(ch)
|
||||
continue
|
||||
if ch == "," and quote is None:
|
||||
buf.append(" ")
|
||||
continue
|
||||
buf.append(ch)
|
||||
return "".join(buf)
|
||||
|
||||
@staticmethod
|
||||
def _parse_note_query(query: str) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""Parse note payload from -query.
|
||||
|
||||
Expected:
|
||||
title:<title>,text:<text>
|
||||
Commas are treated as separators when not inside quotes.
|
||||
"""
|
||||
raw = str(query or "").strip()
|
||||
if not raw:
|
||||
return None, None
|
||||
|
||||
try:
|
||||
from cli_syntax import parse_query, get_field
|
||||
except Exception:
|
||||
parse_query = None # type: ignore
|
||||
get_field = None # type: ignore
|
||||
|
||||
normalized = Add_Note._commas_to_spaces_outside_quotes(raw)
|
||||
|
||||
if callable(parse_query) and callable(get_field):
|
||||
parsed = parse_query(normalized)
|
||||
name = get_field(parsed, "title")
|
||||
text = get_field(parsed, "text")
|
||||
name_s = str(name or "").strip() if name is not None else ""
|
||||
text_s = str(text or "").strip() if text is not None else ""
|
||||
return (name_s or None, text_s or None)
|
||||
|
||||
# Fallback: best-effort regex.
|
||||
name_match = re.search(r"\btitle\s*:\s*([^,\s]+)", normalized, flags=re.IGNORECASE)
|
||||
text_match = re.search(r"\btext\s*:\s*(.+)$", normalized, flags=re.IGNORECASE)
|
||||
note_name = (name_match.group(1).strip() if name_match else "")
|
||||
note_text = (text_match.group(1).strip() if text_match else "")
|
||||
return (note_name or None, note_text or None)
|
||||
|
||||
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:
|
||||
@@ -72,32 +134,42 @@ class Add_Note(Cmdlet):
|
||||
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("[add_note] Error: -query must be of the form hash:<sha256>", file=sys.stderr)
|
||||
return 1
|
||||
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)
|
||||
hash_override = normalize_hash(parsed.get("hash"))
|
||||
note_name, note_text = self._parse_note_query(str(parsed.get("query") or ""))
|
||||
if not note_name or not note_text:
|
||||
log("[add_note] Error: -query must include title:<title> and text:<text>", 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 hash_override and not store_override:
|
||||
log("[add_note] Error: -hash requires -store <store>", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
# Note text can be omitted when upstream stages provide it (e.g. download-media --write-sub
|
||||
# attaches notes.sub). In that case we resolve per-item below.
|
||||
user_provided_text = bool(note_text)
|
||||
explicit_target = bool(hash_override and store_override)
|
||||
|
||||
results = normalize_result_input(result)
|
||||
if results and explicit_target:
|
||||
# Direct targeting mode: apply note once to the explicit target and
|
||||
# pass through any piped items unchanged.
|
||||
try:
|
||||
store_registry = Store(config)
|
||||
backend = store_registry[str(store_override)]
|
||||
ok = bool(backend.set_note(str(hash_override), note_name, note_text, config=config))
|
||||
if ok:
|
||||
ctx.print_if_visible(f"✓ add-note: 1 item in '{store_override}'", file=sys.stderr)
|
||||
except Exception as exc:
|
||||
log(f"[add_note] Error: Failed to set note: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
for res in results:
|
||||
ctx.emit(res)
|
||||
return 0
|
||||
|
||||
if not results:
|
||||
if store_override and query_hash:
|
||||
results = [{"store": str(store_override), "hash": query_hash}]
|
||||
if explicit_target:
|
||||
# Allow standalone use (no piped input) and enable piping the target forward.
|
||||
results = [{"store": str(store_override), "hash": hash_override}]
|
||||
else:
|
||||
log("[add_note] Error: Requires piped item(s) or -store and -query \"hash:<sha256>\"", file=sys.stderr)
|
||||
log("[add_note] Error: Requires piped item(s) from add-file, or explicit -store <store> and -hash <sha256>", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
store_registry = Store(config)
|
||||
@@ -106,55 +178,12 @@ class Add_Note(Cmdlet):
|
||||
# Batch write plan: store -> [(hash, name, text), ...]
|
||||
note_ops: Dict[str, List[Tuple[str, str, str]]] = {}
|
||||
|
||||
# Optional global fallback for note text from pipeline values.
|
||||
# Allows patterns like: ... | add-note sub
|
||||
pipeline_default_text = None
|
||||
if not user_provided_text:
|
||||
try:
|
||||
pipeline_default_text = ctx.load_value(note_name)
|
||||
except Exception:
|
||||
pipeline_default_text = None
|
||||
if isinstance(pipeline_default_text, list):
|
||||
pipeline_default_text = " ".join([str(x) for x in pipeline_default_text]).strip()
|
||||
elif pipeline_default_text is not None:
|
||||
pipeline_default_text = str(pipeline_default_text).strip()
|
||||
|
||||
for res in results:
|
||||
if not isinstance(res, dict):
|
||||
ctx.emit(res)
|
||||
continue
|
||||
|
||||
# Resolve note text for this item when not provided explicitly.
|
||||
item_note_text = note_text
|
||||
if not user_provided_text:
|
||||
# Prefer item-scoped notes dict.
|
||||
candidate = None
|
||||
try:
|
||||
notes = res.get("notes")
|
||||
if isinstance(notes, dict):
|
||||
candidate = notes.get(note_name)
|
||||
except Exception:
|
||||
candidate = None
|
||||
|
||||
# Also allow direct field fallback: res["sub"], etc.
|
||||
if candidate is None:
|
||||
try:
|
||||
candidate = res.get(note_name)
|
||||
except Exception:
|
||||
candidate = None
|
||||
|
||||
if candidate is None:
|
||||
candidate = pipeline_default_text
|
||||
|
||||
if isinstance(candidate, list):
|
||||
item_note_text = " ".join([str(x) for x in candidate]).strip()
|
||||
else:
|
||||
item_note_text = str(candidate or "").strip()
|
||||
|
||||
if not item_note_text:
|
||||
log(f"[add_note] Warning: No note text found for '{note_name}'; skipping", file=sys.stderr)
|
||||
ctx.emit(res)
|
||||
continue
|
||||
|
||||
store_name = str(store_override or res.get("store") or "").strip()
|
||||
raw_hash = res.get("hash")
|
||||
@@ -167,7 +196,7 @@ class Add_Note(Cmdlet):
|
||||
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,
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user