Files
romm/backend/handler/metadata/launchbox_handler/utils.py
Georges-Antoine Assi 40689d7e39 Fix LaunchBox local-media file:// paths resolving under library root
LaunchBox produced file:// URIs relative to /romm/launchbox, but the
resources handler resolved them under /romm/library via fs_rom_handler,
so local images/manuals/screenshots were never found. Switch LaunchBox
to a distinct launchbox-file:// scheme and add FSLaunchboxHandler +
_resolve_local_file_uri to route each scheme to the correct root.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 22:59:00 -04:00

99 lines
2.3 KiB
Python

import re
from datetime import datetime
from pathlib import Path
from .types import LAUNCHBOX_LOCAL_DIR
def sanitize_filename(stem: str) -> str:
s = (stem or "").strip()
s = s.replace("\u2019", "'")
s = re.sub(r"[:']", "_", s)
s = re.sub(r"[\\/|<>\"?*]", "_", s)
s = re.sub(r"\s+", " ", s)
s = re.sub(r"_+", "_", s)
return s.strip(" .")
def file_uri_for_local_path(path: Path) -> str | None:
try:
relative = path.resolve().relative_to(LAUNCHBOX_LOCAL_DIR.resolve())
except ValueError:
return None
return f"launchbox-file://{relative.as_posix()}"
def coalesce(*values: object | None) -> str | None:
for v in values:
if v is None:
continue
s = str(v).strip()
if s:
return s
return None
def parse_list(value: str | None) -> list[str]:
if not value:
return []
parts = re.split(r"[;,]", value)
return [p.strip() for p in parts if p and p.strip()]
def dedupe_words(values: list[str | None]) -> list[str]:
seen = {}
out: list[str] = []
for v in values:
if v is None or not v.strip():
continue
v = v.strip()
key = v.lower()
if key not in seen:
seen[key] = len(out)
out.append(v)
else:
idx = seen[key]
if out[idx].islower() and not v.islower():
out[idx] = v
return out
def parse_release_date(value: str | None) -> int | None:
if not value:
return None
try:
iso = value.replace("Z", "+00:00")
return int(datetime.fromisoformat(iso).timestamp())
except ValueError:
pass
for fmt in ("%Y-%m-%dT%H:%M:%S%z", "%Y-%m-%d"):
try:
return int(datetime.strptime(value, fmt).timestamp())
except ValueError:
continue
return None
def parse_playmode(play_mode: str | None) -> bool:
if not play_mode:
return False
pm = play_mode.lower()
return bool(re.search(r"\b(cooperative|coop|co-op)\b", pm))
def parse_videourl(url: str | None) -> str:
if not url:
return ""
if "youtube.com/watch?v=" in url:
return url.split("v=")[-1].split("&")[0]
elif "youtu.be/" in url:
return url.split("/")[-1].split("?")[0]
return ""