dfd
This commit is contained in:
@@ -12,8 +12,8 @@ from __future__ import annotations
|
||||
|
||||
import sys
|
||||
|
||||
from helper.logger import log
|
||||
from helper.metadata_search import get_metadata_provider
|
||||
from helper.logger import log, debug
|
||||
from helper.metadata_search import get_metadata_provider, list_metadata_providers
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
||||
@@ -475,6 +475,21 @@ def _extract_scrapable_identifiers(tags_list: List[str]) -> Dict[str, str]:
|
||||
return identifiers
|
||||
|
||||
|
||||
def _extract_tag_value(tags_list: List[str], namespace: str) -> Optional[str]:
|
||||
"""Get first tag value for a namespace (e.g., artist:, title:)."""
|
||||
ns = namespace.lower()
|
||||
for tag in tags_list:
|
||||
if not isinstance(tag, str) or ':' not in tag:
|
||||
continue
|
||||
prefix, _, value = tag.partition(':')
|
||||
if prefix.strip().lower() != ns:
|
||||
continue
|
||||
candidate = value.strip()
|
||||
if candidate:
|
||||
return candidate
|
||||
return None
|
||||
|
||||
|
||||
def _scrape_url_metadata(url: str) -> Tuple[Optional[str], List[str], List[Tuple[str, str]], List[Dict[str, Any]]]:
|
||||
"""Scrape metadata from a URL using yt-dlp.
|
||||
|
||||
@@ -1012,6 +1027,25 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
--emit: Emit result without interactive prompt (quiet mode)
|
||||
-scrape <url|provider>: Scrape metadata from URL or provider name (itunes, openlibrary, googlebooks)
|
||||
"""
|
||||
args_list = [str(arg) for arg in (args or [])]
|
||||
raw_args = list(args_list)
|
||||
|
||||
# Support numeric selection tokens (e.g., "@1" leading to argument "1") without treating
|
||||
# them as hash overrides. This lets users pick from the most recent table overlay/results.
|
||||
if len(args_list) == 1:
|
||||
token = args_list[0]
|
||||
if not token.startswith("-") and token.isdigit():
|
||||
try:
|
||||
idx = int(token) - 1
|
||||
items_pool = ctx.get_last_result_items()
|
||||
if 0 <= idx < len(items_pool):
|
||||
result = items_pool[idx]
|
||||
args_list = []
|
||||
debug(f"[get_tag] Resolved numeric selection arg {token} -> last_result_items[{idx}]")
|
||||
else:
|
||||
debug(f"[get_tag] Numeric selection arg {token} out of range (items={len(items_pool)})")
|
||||
except Exception as exc:
|
||||
debug(f"[get_tag] Failed to resolve numeric selection arg {token}: {exc}")
|
||||
# Helper to get field from both dict and object
|
||||
def get_field(obj: Any, field: str, default: Any = None) -> Any:
|
||||
if isinstance(obj, dict):
|
||||
@@ -1020,10 +1054,10 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
return getattr(obj, field, default)
|
||||
|
||||
# Parse arguments using shared parser
|
||||
parsed_args = parse_cmdlet_args(args, CMDLET)
|
||||
parsed_args = parse_cmdlet_args(args_list, CMDLET)
|
||||
|
||||
# Detect if -scrape flag was provided without a value (parse_cmdlet_args skips missing values)
|
||||
scrape_flag_present = any(str(arg).lower() in {"-scrape", "--scrape"} for arg in args)
|
||||
scrape_flag_present = any(str(arg).lower() in {"-scrape", "--scrape"} for arg in args_list)
|
||||
|
||||
# Extract values
|
||||
hash_override_raw = parsed_args.get("hash")
|
||||
@@ -1033,10 +1067,14 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
scrape_url = parsed_args.get("scrape")
|
||||
scrape_requested = scrape_flag_present or scrape_url is not None
|
||||
|
||||
explicit_hash_flag = any(str(arg).lower() in {"-hash", "--hash"} for arg in raw_args)
|
||||
if hash_override_raw is not None:
|
||||
if not hash_override or not looks_like_hash(hash_override):
|
||||
log("Invalid hash format: expected 64 hex characters", file=sys.stderr)
|
||||
return 1
|
||||
debug(f"[get_tag] Ignoring invalid hash override '{hash_override_raw}' (explicit_flag={explicit_hash_flag})")
|
||||
if explicit_hash_flag:
|
||||
log("Invalid hash format: expected 64 hex characters", file=sys.stderr)
|
||||
return 1
|
||||
hash_override = None
|
||||
|
||||
if scrape_requested and (not scrape_url or str(scrape_url).strip() == ""):
|
||||
log("-scrape requires a URL or provider name", file=sys.stderr)
|
||||
@@ -1085,6 +1123,9 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
identifier_tags = [str(t) for t in tags_from_sidecar if isinstance(t, (str, bytes))]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
title_from_tags = _extract_tag_value(identifier_tags, "title")
|
||||
artist_from_tags = _extract_tag_value(identifier_tags, "artist")
|
||||
|
||||
identifiers = _extract_scrapable_identifiers(identifier_tags)
|
||||
identifier_query: Optional[str] = None
|
||||
@@ -1095,19 +1136,35 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
identifier_query = identifiers.get("musicbrainz") or identifiers.get("musicbrainzalbum")
|
||||
|
||||
# Determine query from identifier first, else title on the result or filename
|
||||
title_hint = get_field(result, "title", None) or get_field(result, "name", None)
|
||||
title_hint = title_from_tags or get_field(result, "title", None) or get_field(result, "name", None)
|
||||
if not title_hint:
|
||||
file_path = get_field(result, "path", None) or get_field(result, "filename", None)
|
||||
if file_path:
|
||||
title_hint = Path(str(file_path)).stem
|
||||
artist_hint = artist_from_tags or get_field(result, "artist", None) or get_field(result, "uploader", None)
|
||||
if not artist_hint:
|
||||
meta_field = get_field(result, "metadata", None)
|
||||
if isinstance(meta_field, dict):
|
||||
meta_artist = meta_field.get("artist") or meta_field.get("uploader")
|
||||
if meta_artist:
|
||||
artist_hint = str(meta_artist)
|
||||
|
||||
combined_query: Optional[str] = None
|
||||
if not identifier_query and title_hint and artist_hint and provider.name in {"itunes", "musicbrainz"}:
|
||||
if provider.name == "musicbrainz":
|
||||
combined_query = f'recording:"{title_hint}" AND artist:"{artist_hint}"'
|
||||
else:
|
||||
combined_query = f"{title_hint} {artist_hint}"
|
||||
|
||||
query_hint = identifier_query or title_hint
|
||||
query_hint = identifier_query or combined_query or title_hint
|
||||
if not query_hint:
|
||||
log("No title or identifier available to search for metadata", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
if identifier_query:
|
||||
log(f"Using identifier for metadata search: {identifier_query}")
|
||||
elif combined_query:
|
||||
log(f"Using title+artist for metadata search: {title_hint} - {artist_hint}")
|
||||
else:
|
||||
log(f"Using title for metadata search: {query_hint}")
|
||||
|
||||
@@ -1319,6 +1376,13 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
_SCRAPE_CHOICES = []
|
||||
try:
|
||||
_SCRAPE_CHOICES = sorted(list_metadata_providers().keys())
|
||||
except Exception:
|
||||
_SCRAPE_CHOICES = ["itunes", "openlibrary", "googlebooks", "google", "musicbrainz"]
|
||||
|
||||
|
||||
CMDLET = Cmdlet(
|
||||
name="get-tag",
|
||||
summary="Get tags from Hydrus or local sidecar metadata",
|
||||
@@ -1341,8 +1405,9 @@ CMDLET = Cmdlet(
|
||||
CmdletArg(
|
||||
name="-scrape",
|
||||
type="string",
|
||||
description="Scrape metadata from URL or provider name (returns tags as JSON or table)",
|
||||
required=False
|
||||
description="Scrape metadata from URL or provider name (returns tags as JSON or table)",
|
||||
required=False,
|
||||
choices=_SCRAPE_CHOICES,
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user