dfslkjelf

This commit is contained in:
nose
2025-12-18 22:50:21 -08:00
parent 76691dbbf5
commit d637532237
16 changed files with 2587 additions and 299 deletions

View File

@@ -12,6 +12,8 @@ from __future__ import annotations
import sys
from SYS.logger import log, debug
try:
from Provider.openlibrary import OpenLibrary
_ol_scrape_isbn_metadata = OpenLibrary.scrape_isbn_metadata
@@ -94,7 +96,7 @@ def _emit_tags_as_table(
file_hash: Optional[str],
store: str = "hydrus",
service_name: Optional[str] = None,
config: Dict[str, Any] = None,
config: Optional[Dict[str, Any]] = None,
item_title: Optional[str] = None,
path: Optional[str] = None,
subject: Optional[Any] = None,
@@ -107,11 +109,10 @@ def _emit_tags_as_table(
from result_table import ResultTable
# Create ResultTable with just tag column (no title)
table_title = "Tag"
# Keep the title stable and avoid including hash fragments.
table_title = "tag"
if item_title:
table_title = f"Tag: {item_title}"
if file_hash:
table_title += f" [{file_hash[:8]}]"
table_title = f"tag: {item_title}"
table = ResultTable(table_title, max_columns=1)
table.set_source_command("get-tag", [])
@@ -140,6 +141,28 @@ def _emit_tags_as_table(
except AttributeError:
ctx.set_last_result_table(table, tag_items, subject)
# Note: CLI will handle displaying the table via ResultTable formatting
def _filter_scraped_tags(tags: List[str]) -> List[str]:
"""Filter out tags we don't want to import from scraping."""
blocked = {"title", "artist", "source"}
out: List[str] = []
seen: set[str] = set()
for t in tags:
if not t:
continue
s = str(t).strip()
if not s:
continue
ns = s.split(":", 1)[0].strip().lower() if ":" in s else ""
if ns in blocked:
continue
key = s.lower()
if key in seen:
continue
seen.add(key)
out.append(s)
return out
def _summarize_tags(tags_list: List[str], limit: int = 8) -> str:
"""Create a summary of tags for display."""
shown = [t for t in tags_list[:limit] if t]
@@ -865,14 +888,32 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
log(f"Unknown metadata provider: {scrape_url}", file=sys.stderr)
return 1
# Prefer identifier tags (ISBN/OLID/etc.) when available; fallback to title/filename
# Prefer identifier tags (ISBN/OLID/etc.) when available; fallback to title/filename.
# IMPORTANT: do not rely on `result.tag` for this because it can be stale (cached on
# the piped PipeObject). Always prefer the current store-backed tags when possible.
identifier_tags: List[str] = []
result_tags = get_field(result, "tag", None)
if isinstance(result_tags, list):
identifier_tags = [str(t) for t in result_tags if isinstance(t, (str, bytes))]
# Try local sidecar if no tags present on result
file_hash_for_scrape = normalize_hash(hash_override) or normalize_hash(get_field(result, "hash", None))
store_for_scrape = get_field(result, "store", None)
if file_hash_for_scrape and store_for_scrape:
try:
from Store import Store
storage = Store(config)
backend = storage[str(store_for_scrape)]
current_tags, _src = backend.get_tag(file_hash_for_scrape, config=config)
if isinstance(current_tags, (list, tuple, set)) and current_tags:
identifier_tags = [str(t) for t in current_tags if isinstance(t, (str, bytes))]
except Exception:
# Fall back to whatever is present on the piped result if store lookup fails.
pass
# Fall back to tags carried on the result (may be stale).
if not identifier_tags:
result_tags = get_field(result, "tag", None)
if isinstance(result_tags, list):
identifier_tags = [str(t) for t in result_tags if isinstance(t, (str, bytes))]
# As a last resort, try local sidecar only when the item is not store-backed.
if not identifier_tags and (not file_hash_for_scrape or not store_for_scrape):
file_path = get_field(result, "target", None) or get_field(result, "path", None) or get_field(result, "filename", None)
if isinstance(file_path, str) and file_path and not file_path.lower().startswith(("http://", "https://")):
try:
@@ -939,8 +980,11 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
selection_payload = []
hash_for_payload = normalize_hash(hash_override) or normalize_hash(get_field(result, "hash", None))
store_for_payload = get_field(result, "store", None)
# Preserve a consistent path field when present so selecting a metadata row
# keeps referring to the original file.
path_for_payload = get_field(result, "path", None) or get_field(result, "target", None) or get_field(result, "filename", None)
for idx, item in enumerate(items):
tags = provider.to_tags(item)
tags = _filter_scraped_tags(provider.to_tags(item))
row = table.add_row()
row.add_column("Title", item.get("title", ""))
row.add_column("Artist", item.get("artist", ""))
@@ -955,6 +999,7 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
"year": item.get("year"),
"hash": hash_for_payload,
"store": store_for_payload,
"path": path_for_payload,
"extra": {
"tag": tags,
"provider": provider.name,
@@ -967,7 +1012,6 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
ctx.set_current_stage_table(table)
# Preserve items for @ selection and downstream pipes without emitting duplicates
ctx.set_last_result_items_only(selection_payload)
print(table)
return 0
# If -scrape was requested but no URL, that's an error
@@ -978,6 +1022,70 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
# Handle @N selection which creates a list - extract the first item
if isinstance(result, list) and len(result) > 0:
result = result[0]
# If the current result already carries a tag list (e.g. a selected metadata
# row from get-tag -scrape itunes), APPLY those tags to the file in the store.
result_provider = get_field(result, "provider", None)
result_tags = get_field(result, "tag", None)
if result_provider and isinstance(result_tags, list) and result_tags:
file_hash = normalize_hash(hash_override) or normalize_hash(get_field(result, "hash", None))
store_name = get_field(result, "store", None)
subject_path = get_field(result, "path", None) or get_field(result, "target", None) or get_field(result, "filename", None)
if not file_hash or not store_name:
log("Selected metadata row is missing hash/store; cannot apply tags", file=sys.stderr)
_emit_tags_as_table(
tags_list=[str(t) for t in result_tags if t is not None],
file_hash=file_hash,
store=str(store_name or "local"),
service_name=None,
config=config,
item_title=str(get_field(result, "title", None) or result_provider),
path=str(subject_path) if subject_path else None,
subject=result,
)
return 0
# Apply tags to the store backend (no sidecar writing here).
apply_tags = _filter_scraped_tags([str(t) for t in result_tags if t is not None])
if not apply_tags:
log("No applicable scraped tags to apply (title:/artist:/source: are skipped)", file=sys.stderr)
return 0
try:
from Store import Store
storage = Store(config)
backend = storage[str(store_name)]
ok = bool(backend.add_tag(file_hash, apply_tags, config=config))
if not ok:
log(f"Failed to apply tags to store '{store_name}'", file=sys.stderr)
except Exception as exc:
log(f"Failed to apply tags: {exc}", file=sys.stderr)
return 1
# Show updated tags after applying.
try:
updated_tags, _src = backend.get_tag(file_hash, config=config)
except Exception:
updated_tags = apply_tags
if not updated_tags:
updated_tags = apply_tags
_emit_tags_as_table(
tags_list=list(updated_tags),
file_hash=file_hash,
store=str(store_name),
service_name=None,
config=config,
item_title=str(get_field(result, "title", None) or get_field(result, "name", None) or str(result_provider)),
path=str(subject_path) if subject_path else None,
subject={
"hash": file_hash,
"store": str(store_name),
"path": str(subject_path) if subject_path else None,
"title": get_field(result, "title", None) or get_field(result, "name", None),
"extra": {"applied_provider": str(result_provider)},
},
)
return 0
hash_from_result = normalize_hash(get_field(result, "hash", None))
file_hash = hash_override or hash_from_result
@@ -1022,6 +1130,7 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
# Build a subject payload representing the file whose tags are being shown
subject_store = get_field(result, "store", None) or store_name
subject_path = get_field(result, "path", None) or get_field(result, "target", None) or get_field(result, "filename", None)
subject_payload: Dict[str, Any] = {
"tag": list(current),
"title": item_title,
@@ -1034,12 +1143,9 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
}
if file_hash:
subject_payload["hash"] = file_hash
if local_path:
if subject_path:
try:
path_text = str(local_path)
subject_payload.update({
"path": path_text,
})
subject_payload["path"] = str(subject_path)
except Exception:
pass
@@ -1050,7 +1156,7 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
service_name=service_name if source == "hydrus" else None,
config=config,
item_title=item_title,
path=str(local_path) if local_path else None,
path=str(subject_path) if subject_path else None,
subject=subject_payload,
)
@@ -1116,55 +1222,7 @@ class Get_Tag(Cmdlet):
def run(self, result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
"""Execute get-tag cmdlet."""
# Parse arguments
parsed = parse_cmdlet_args(args, self)
# Get hash and store from parsed args or result
hash_override = parsed.get("hash")
file_hash = normalize_hash(hash_override) or normalize_hash(get_field(result, "hash"))
store_name = parsed.get("store") or get_field(result, "store")
if not file_hash:
log("No hash available in result", file=sys.stderr)
return 1
if not store_name:
log("No store specified in result", file=sys.stderr)
return 1
# Get tags using storage backend
try:
from Store import Store
storage_obj = Store(config)
backend = storage_obj[store_name]
current, source = backend.get_tag(file_hash, config=config)
if not current:
log("No tags found", file=sys.stderr)
return 1
# Build table and emit
item_title = get_field(result, "title") or file_hash[:16]
_emit_tags_as_table(
tags_list=current,
file_hash=file_hash,
store=store_name,
service_name="",
config=config,
item_title=item_title,
path=None,
subject=result,
)
return 0
except KeyError:
log(f"Store '{store_name}' not found", file=sys.stderr)
return 1
except Exception as exc:
log(f"Failed to get tags: {exc}", file=sys.stderr)
import traceback
traceback.print_exc(file=sys.stderr)
return 1
return _run(result, args, config)
# Create and register the cmdlet