Add YAPF style + ignore, and format tracked Python files

This commit is contained in:
2025-12-29 18:42:02 -08:00
parent c019c00aed
commit 507946a3e4
108 changed files with 11664 additions and 6494 deletions

View File

@@ -39,8 +39,7 @@ CmdletArg = sh.CmdletArg
SharedArgs = sh.SharedArgs
parse_cmdlet_args = sh.parse_cmdlet_args
get_field = sh.get_field
from config import get_local_storage_path
from SYS.config import get_local_storage_path
try:
from metadata import extract_title
@@ -148,7 +147,8 @@ def _resolve_candidate_urls_for_item(
result: Any,
backend: Any,
file_hash: str,
config: Dict[str, Any],
config: Dict[str,
Any],
) -> List[str]:
"""Get candidate URLs from backend and/or piped result."""
try:
@@ -165,7 +165,10 @@ def _resolve_candidate_urls_for_item(
urls.extend(normalize_urls(backend_urls))
else:
urls.extend(
[str(u).strip() for u in backend_urls if isinstance(u, str) and str(u).strip()]
[
str(u).strip() for u in backend_urls
if isinstance(u, str) and str(u).strip()
]
)
except Exception:
pass
@@ -180,7 +183,10 @@ def _resolve_candidate_urls_for_item(
raw = meta.get("url")
if isinstance(raw, list):
urls.extend(
[str(u).strip() for u in raw if isinstance(u, str) and str(u).strip()]
[
str(u).strip() for u in raw
if isinstance(u, str) and str(u).strip()
]
)
elif isinstance(raw, str) and raw.strip():
urls.append(raw.strip())
@@ -203,7 +209,9 @@ def _resolve_candidate_urls_for_item(
if isinstance(val, str) and val.strip():
urls.append(val.strip())
elif isinstance(val, list):
urls.extend([str(u).strip() for u in val if isinstance(u, str) and str(u).strip()])
urls.extend(
[str(u).strip() for u in val if isinstance(u, str) and str(u).strip()]
)
meta_field = _get(result, "metadata", None)
if isinstance(meta_field, dict) and meta_field.get("url"):
@@ -211,7 +219,9 @@ def _resolve_candidate_urls_for_item(
if normalize_urls:
urls.extend(normalize_urls(val))
elif isinstance(val, list):
urls.extend([str(u).strip() for u in val if isinstance(u, str) and str(u).strip()])
urls.extend(
[str(u).strip() for u in val if isinstance(u, str) and str(u).strip()]
)
elif isinstance(val, str) and val.strip():
urls.append(val.strip())
@@ -263,7 +273,6 @@ def _pick_supported_ytdlp_url(urls: List[str]) -> Optional[str]:
_scrape_isbn_metadata = _ol_scrape_isbn_metadata # type: ignore[assignment]
_scrape_openlibrary_metadata = _ol_scrape_openlibrary_metadata # type: ignore[assignment]
# Tag item for ResultTable display and piping
from dataclasses import dataclass
@@ -308,7 +317,8 @@ def _emit_tags_as_table(
file_hash: Optional[str],
store: str = "hydrus",
service_name: Optional[str] = None,
config: Optional[Dict[str, Any]] = None,
config: Optional[Dict[str,
Any]] = None,
item_title: Optional[str] = None,
path: Optional[str] = None,
subject: Optional[Any] = None,
@@ -357,7 +367,9 @@ def _emit_tags_as_table(
def _filter_scraped_tags(tags: List[str]) -> List[str]:
"""Filter out tags we don't want to import from scraping."""
blocked = {"title", "artist", "source"}
blocked = {"title",
"artist",
"source"}
out: List[str] = []
seen: set[str] = set()
for t in tags:
@@ -529,12 +541,14 @@ def _handle_title_rename(old_path: Path, tags_list: List[str]) -> Optional[Path]
new_tags_path = old_path.parent / (new_name + ".tag")
if new_tags_path.exists():
log(
f"Warning: Target sidecar already exists: {new_tags_path.name}", file=sys.stderr
f"Warning: Target sidecar already exists: {new_tags_path.name}",
file=sys.stderr
)
else:
old_tags_path.rename(new_tags_path)
log(
f"Renamed sidecar: {old_tags_path.name}{new_tags_path.name}", file=sys.stderr
f"Renamed sidecar: {old_tags_path.name}{new_tags_path.name}",
file=sys.stderr
)
return new_path
@@ -564,7 +578,10 @@ def _read_sidecar_fallback(p: Path) -> tuple[Optional[str], List[str], List[str]
h: Optional[str] = None
# Namespaces to exclude from tags
excluded_namespaces = {"hash", "url", "url", "relationship"}
excluded_namespaces = {"hash",
"url",
"url",
"relationship"}
for line in raw.splitlines():
s = line.strip()
@@ -594,7 +611,11 @@ def _read_sidecar_fallback(p: Path) -> tuple[Optional[str], List[str], List[str]
def _write_sidecar(
p: Path, media: Path, tag_list: List[str], url: List[str], hash_in_sidecar: Optional[str]
p: Path,
media: Path,
tag_list: List[str],
url: List[str],
hash_in_sidecar: Optional[str]
) -> Path:
"""Write tags to sidecar file and handle title-based renaming.
@@ -634,15 +655,17 @@ def _emit_tag_payload(
tags_list: List[str],
*,
hash_value: Optional[str],
extra: Optional[Dict[str, Any]] = None,
extra: Optional[Dict[str,
Any]] = None,
store_label: Optional[str] = None,
) -> int:
"""Emit tag values as structured payload to pipeline."""
payload: Dict[str, Any] = {
"source": source,
"tag": list(tags_list),
"count": len(tags_list),
}
payload: Dict[str,
Any] = {
"source": source,
"tag": list(tags_list),
"count": len(tags_list),
}
if hash_value:
payload["hash"] = hash_value
if extra:
@@ -662,7 +685,11 @@ def _emit_tag_payload(
if ctx.get_stage_context() is not None:
for idx, tag_name in enumerate(tags_list, start=1):
tag_item = TagItem(
tag_name=tag_name, tag_index=idx, hash=hash_value, store=source, service_name=None
tag_name=tag_name,
tag_index=idx,
hash=hash_value,
store=source,
service_name=None
)
ctx.emit(tag_item)
else:
@@ -730,7 +757,12 @@ def _extract_tag_value(tags_list: List[str], namespace: str) -> Optional[str]:
def _scrape_url_metadata(
url: str,
) -> Tuple[Optional[str], List[str], List[Tuple[str, str]], List[Dict[str, Any]]]:
) -> Tuple[Optional[str],
List[str],
List[Tuple[str,
str]],
List[Dict[str,
Any]]]:
"""Scrape metadata from a URL using yt-dlp.
Returns:
@@ -810,10 +842,12 @@ def _scrape_url_metadata(
playlist_items.append(
{
"index": idx,
"id": entry.get("id", f"track_{idx}"),
"id": entry.get("id",
f"track_{idx}"),
"title": item_title,
"duration": item_duration,
"url": entry.get("url") or entry.get("webpage_url", ""),
"url": entry.get("url") or entry.get("webpage_url",
""),
}
)
@@ -837,14 +871,16 @@ def _scrape_url_metadata(
for tag in entry_tags:
# Extract the namespace (part before the colon)
tag_namespace = tag.split(":", 1)[0].lower() if ":" in tag else None
tag_namespace = tag.split(":",
1)[0].lower(
) if ":" in tag else None
# Skip if this namespace already exists in tags (from album level)
if tag_namespace and tag_namespace in single_value_namespaces:
# Check if any tag with this namespace already exists in tags
already_has_namespace = any(
t.split(":", 1)[0].lower() == tag_namespace
for t in tags
t.split(":",
1)[0].lower() == tag_namespace for t in tags
if ":" in t
)
if already_has_namespace:
@@ -858,8 +894,21 @@ def _scrape_url_metadata(
elif (data.get("playlist_count") or 0) > 0 and "entries" not in data:
try:
# Make a second call with --flat-playlist to get the actual tracks
flat_cmd = ["yt-dlp", "-j", "--no-warnings", "--flat-playlist", "-f", "best", url]
flat_result = subprocess.run(flat_cmd, capture_output=True, text=True, timeout=30)
flat_cmd = [
"yt-dlp",
"-j",
"--no-warnings",
"--flat-playlist",
"-f",
"best",
url
]
flat_result = subprocess.run(
flat_cmd,
capture_output=True,
text=True,
timeout=30
)
if flat_result.returncode == 0:
flat_lines = flat_result.stdout.strip().split("\n")
# With --flat-playlist, each line is a separate track JSON object
@@ -868,15 +917,27 @@ def _scrape_url_metadata(
if line.strip().startswith("{"):
try:
entry = json_module.loads(line)
item_title = entry.get("title", entry.get("id", f"Track {idx}"))
item_title = entry.get(
"title",
entry.get("id",
f"Track {idx}")
)
item_duration = entry.get("duration", 0)
playlist_items.append(
{
"index": idx,
"id": entry.get("id", f"track_{idx}"),
"title": item_title,
"duration": item_duration,
"url": entry.get("url") or entry.get("webpage_url", ""),
"index":
idx,
"id":
entry.get("id",
f"track_{idx}"),
"title":
item_title,
"duration":
item_duration,
"url":
entry.get("url")
or entry.get("webpage_url",
""),
}
)
except json_module.JSONDecodeError:
@@ -935,7 +996,9 @@ def _extract_url_formats(formats: list) -> List[Tuple[str, str]]:
if height < 480:
continue
res_key = f"{height}p"
if res_key not in video_formats or tbr > video_formats[res_key].get("tbr", 0):
if res_key not in video_formats or tbr > video_formats[res_key].get(
"tbr",
0):
video_formats[res_key] = {
"label": f"{height}p ({ext})",
"format_id": format_id,
@@ -945,7 +1008,9 @@ def _extract_url_formats(formats: list) -> List[Tuple[str, str]]:
# Audio-only format
elif acodec and acodec != "none" and (not vcodec or vcodec == "none"):
audio_key = f"audio_{abr}"
if audio_key not in audio_formats or abr > audio_formats[audio_key].get("abr", 0):
if audio_key not in audio_formats or abr > audio_formats[audio_key].get(
"abr",
0):
audio_formats[audio_key] = {
"label": f"audio ({ext})",
"format_id": format_id,
@@ -955,9 +1020,9 @@ def _extract_url_formats(formats: list) -> List[Tuple[str, str]]:
result = []
# Add video formats in descending resolution order
for res in sorted(
video_formats.keys(), key=lambda x: int(x.replace("p", "")), reverse=True
):
for res in sorted(video_formats.keys(),
key=lambda x: int(x.replace("p", "")),
reverse=True):
fmt = video_formats[res]
result.append((fmt["label"], fmt["format_id"]))
@@ -1019,12 +1084,15 @@ def _perform_scraping(tags_list: List[str]) -> List[str]:
log(f"Scraping OpenLibrary: {olid}")
new_tags.extend(_scrape_openlibrary_metadata(olid))
elif "isbn_13" in identifiers or "isbn_10" in identifiers or "isbn" in identifiers:
isbn = identifiers.get("isbn_13") or identifiers.get("isbn_10") or identifiers.get("isbn")
isbn = identifiers.get("isbn_13") or identifiers.get(
"isbn_10"
) or identifiers.get("isbn")
if isbn:
log(f"Scraping ISBN: {isbn}")
new_tags.extend(_scrape_isbn_metadata(isbn))
existing_tags_lower = {tag.lower() for tag in tags_list}
existing_tags_lower = {tag.lower()
for tag in tags_list}
scraped_unique = []
seen = set()
for tag in new_tags:
@@ -1074,7 +1142,9 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
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}")
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:
@@ -1087,7 +1157,10 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
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_list)
scrape_flag_present = any(
str(arg).lower() in {"-scrape",
"--scrape"} for arg in args_list
)
# Extract values
query_raw = parsed_args.get("query")
@@ -1122,39 +1195,54 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
# NOTE: We intentionally do not reuse _scrape_url_metadata() here because it
# performs namespace deduplication that would collapse multi-valued tags.
file_hash = normalize_hash(hash_override) or normalize_hash(
get_field(result, "hash", None)
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)
get_field(result,
"path",
None) or get_field(result,
"target",
None)
or get_field(result,
"filename",
None)
)
item_title = (
get_field(result, "title", None)
or get_field(result, "name", None)
or get_field(result, "filename", None)
get_field(result,
"title",
None) or get_field(result,
"name",
None)
or get_field(result,
"filename",
None)
)
# Only run overwrite-apply when the item is store-backed.
# If this is a URL-only PipeObject, fall through to provider mode below.
if (
file_hash
and store_name
and str(file_hash).strip().lower() != "unknown"
and str(store_name).strip().upper() not in {"PATH", "URL"}
):
if (file_hash and store_name and str(file_hash).strip().lower() != "unknown"
and str(store_name).strip().upper() not in {"PATH",
"URL"}):
try:
from Store import Store
storage = Store(config)
backend = storage[str(store_name)]
except Exception as exc:
log(f"Failed to resolve store backend '{store_name}': {exc}", file=sys.stderr)
log(
f"Failed to resolve store backend '{store_name}': {exc}",
file=sys.stderr
)
return 1
candidate_urls = _resolve_candidate_urls_for_item(
result, backend, file_hash, config
result,
backend,
file_hash,
config
)
scrape_target = _pick_supported_ytdlp_url(candidate_urls)
if not scrape_target:
@@ -1201,7 +1289,8 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
try:
tags.extend(
_extract_subtitle_tags(
info_for_subs if isinstance(info_for_subs, dict) else {}
info_for_subs if isinstance(info_for_subs,
dict) else {}
)
)
except Exception:
@@ -1220,7 +1309,11 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
existing_tags = []
try:
if existing_tags:
backend.delete_tag(file_hash, list(existing_tags), config=config)
backend.delete_tag(
file_hash,
list(existing_tags),
config=config
)
except Exception as exc:
debug(f"[get_tag] ytdlp overwrite: delete_tag failed: {exc}")
try:
@@ -1250,7 +1343,10 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
"store": str(store_name),
"path": str(subject_path) if subject_path else None,
"title": item_title,
"extra": {"applied_provider": "ytdlp", "scrape_url": scrape_target},
"extra": {
"applied_provider": "ytdlp",
"scrape_url": scrape_target
},
},
)
return 0
@@ -1264,7 +1360,8 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
output = {
"title": title,
"tag": tags,
"formats": [(label, fmt_id) for label, fmt_id in formats],
"formats": [(label,
fmt_id) for label, fmt_id in formats],
"playlist_items": playlist_items,
}
print(json_module.dumps(output, ensure_ascii=False))
@@ -1281,7 +1378,9 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
# the piped PipeObject). Always prefer the current store-backed tags when possible.
identifier_tags: List[str] = []
file_hash_for_scrape = normalize_hash(hash_override) or normalize_hash(
get_field(result, "hash", None)
get_field(result,
"hash",
None)
)
store_for_scrape = get_field(result, "store", None)
if file_hash_for_scrape and store_for_scrape:
@@ -1292,7 +1391,9 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
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))]
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
@@ -1301,27 +1402,34 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
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))]
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)
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://"))
):
if (isinstance(file_path,
str) and file_path and not file_path.lower().startswith(
("http://",
"https://"))):
try:
media_path = Path(str(file_path))
if media_path.exists():
tags_from_sidecar = read_sidecar(media_path)
if isinstance(tags_from_sidecar, list):
identifier_tags = [
str(t) for t in tags_from_sidecar if isinstance(t, (str, bytes))
str(t) for t in tags_from_sidecar
if isinstance(t, (str, bytes))
]
except Exception:
pass
@@ -1332,12 +1440,12 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
identifiers = _extract_scrapable_identifiers(identifier_tags)
identifier_query: Optional[str] = None
if identifiers:
if provider.name in {"openlibrary", "googlebooks", "google"}:
if provider.name in {"openlibrary",
"googlebooks",
"google"}:
identifier_query = (
identifiers.get("isbn_13")
or identifiers.get("isbn_10")
or identifiers.get("isbn")
or identifiers.get("openlibrary")
identifiers.get("isbn_13") or identifiers.get("isbn_10")
or identifiers.get("isbn") or identifiers.get("openlibrary")
)
elif provider.name == "itunes":
identifier_query = identifiers.get("musicbrainz") or identifiers.get(
@@ -1346,16 +1454,26 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
# Determine query from identifier first, else title on the result or filename
title_hint = (
title_from_tags or get_field(result, "title", None) or get_field(result, "name", None)
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)
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)
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)
@@ -1365,12 +1483,9 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
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 (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:
@@ -1380,18 +1495,27 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
url_hint: Optional[str] = None
if provider.name == "ytdlp":
raw_url = (
get_field(result, "url", None)
or get_field(result, "source_url", None)
or get_field(result, "target", None)
get_field(result,
"url",
None) or get_field(result,
"source_url",
None) or get_field(result,
"target",
None)
)
if isinstance(raw_url, list) and raw_url:
raw_url = raw_url[0]
if isinstance(raw_url, str) and raw_url.strip().startswith(("http://", "https://")):
if isinstance(raw_url,
str) and raw_url.strip().startswith(("http://",
"https://")):
url_hint = raw_url.strip()
query_hint = url_hint or 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)
log(
"No title or identifier available to search for metadata",
file=sys.stderr
)
return 1
if identifier_query:
@@ -1423,7 +1547,10 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
config=config,
item_title=str(items[0].get("title") or "ytdlp"),
path=None,
subject={"provider": "ytdlp", "url": str(query_hint)},
subject={
"provider": "ytdlp",
"url": str(query_hint)
},
)
return 0
@@ -1433,15 +1560,21 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
table.set_source_command("get-tag", [])
selection_payload = []
hash_for_payload = normalize_hash(hash_override) or normalize_hash(
get_field(result, "hash", None)
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)
get_field(result,
"path",
None) or get_field(result,
"target",
None) or get_field(result,
"filename",
None)
)
for idx, item in enumerate(items):
tags = _filter_scraped_tags(provider.to_tags(item))
@@ -1488,22 +1621,35 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
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))
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)
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)
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),
item_title=str(get_field(result,
"title",
None) or result_provider),
path=str(subject_path) if subject_path else None,
subject=result,
)
@@ -1513,7 +1659,9 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
if str(result_provider).strip().lower() == "ytdlp":
apply_tags = [str(t) for t in result_tags if t is not None]
else:
apply_tags = _filter_scraped_tags([str(t) for t in result_tags if t is not None])
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)",
@@ -1547,17 +1695,25 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
service_name=None,
config=config,
item_title=str(
get_field(result, "title", None)
or get_field(result, "name", None)
or str(result_provider)
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)},
"title": get_field(result,
"title",
None) or get_field(result,
"name",
None),
"extra": {
"applied_provider": str(result_provider)
},
},
)
return 0
@@ -1603,28 +1759,37 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
# Always output to ResultTable (pipeline mode only)
# Extract title for table header
item_title = (
get_field(result, "title", None)
or get_field(result, "name", None)
or get_field(result, "filename", None)
get_field(result,
"title",
None) or get_field(result,
"name",
None) or get_field(result,
"filename",
None)
)
# 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)
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,
"name": item_title,
"store": subject_store,
"service_name": service_name,
"extra": {
"tag": list(current),
},
}
subject_payload: Dict[str,
Any] = {
"tag": list(current),
"title": item_title,
"name": item_title,
"store": subject_store,
"service_name": service_name,
"extra": {
"tag": list(current),
},
}
if file_hash:
subject_payload["hash"] = file_hash
if subject_path:
@@ -1646,7 +1811,12 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
# If emit requested or store key provided, emit payload
if emit_mode:
_emit_tag_payload(source, current, hash_value=file_hash, store_label=store_label)
_emit_tag_payload(
source,
current,
hash_value=file_hash,
store_label=store_label
)
return 0
@@ -1671,7 +1841,8 @@ class Get_Tag(Cmdlet):
super().__init__(
name="get-tag",
summary="Get tag values from Hydrus or local sidecar metadata",
usage='get-tag [-query "hash:<sha256>"] [--store <key>] [--emit] [-scrape <url|provider>]',
usage=
'get-tag [-query "hash:<sha256>"] [--store <key>] [--emit] [-scrape <url|provider>]',
alias=[],
arg=[
SharedArgs.QUERY,
@@ -1690,7 +1861,8 @@ class Get_Tag(Cmdlet):
CmdletArg(
name="-scrape",
type="string",
description="Scrape metadata from URL/provider, or use 'ytdlp' to scrape from the item's URL and overwrite tags",
description=
"Scrape metadata from URL/provider, or use 'ytdlp' to scrape from the item's URL and overwrite tags",
required=False,
choices=_SCRAPE_CHOICES,
),