df
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled
This commit is contained in:
77
MPV/lyric.py
77
MPV/lyric.py
@@ -150,7 +150,9 @@ def _osd_overlay_set_ass(client: MPVIPCClient, ass_text: str) -> Optional[dict]:
|
||||
|
||||
|
||||
def _osd_overlay_clear(client: MPVIPCClient) -> None:
|
||||
client.send_command({"command": {"name": "osd-overlay", "id": _LYRIC_OSD_OVERLAY_ID, "format": "none"}})
|
||||
client.send_command(
|
||||
{"command": {"name": "osd-overlay", "id": _LYRIC_OSD_OVERLAY_ID, "format": "none"}}
|
||||
)
|
||||
|
||||
|
||||
def _log(msg: str) -> None:
|
||||
@@ -181,6 +183,8 @@ def _ipc_get_property(
|
||||
if resp and resp.get("error") == "success":
|
||||
return resp.get("data", default)
|
||||
return default
|
||||
|
||||
|
||||
def _http_get_json(url: str, *, timeout_s: float = 10.0) -> Optional[dict]:
|
||||
try:
|
||||
req = Request(
|
||||
@@ -262,7 +266,9 @@ def _wrap_plain_lyrics_as_lrc(text: str) -> str:
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
def _fetch_lrclib(*, artist: Optional[str], title: Optional[str], duration_s: Optional[float] = None) -> Optional[str]:
|
||||
def _fetch_lrclib(
|
||||
*, artist: Optional[str], title: Optional[str], duration_s: Optional[float] = None
|
||||
) -> Optional[str]:
|
||||
base = "https://lrclib.net/api"
|
||||
|
||||
# Require both artist and title; title-only lookups cause frequent mismatches.
|
||||
@@ -506,7 +512,9 @@ def _write_temp_sub_file(*, key: str, text: str) -> Path:
|
||||
tmp_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
ext = _infer_sub_extension(text)
|
||||
digest = hashlib.sha1((key + "\n" + (text or "")).encode("utf-8", errors="ignore")).hexdigest()[:16]
|
||||
digest = hashlib.sha1((key + "\n" + (text or "")).encode("utf-8", errors="ignore")).hexdigest()[
|
||||
:16
|
||||
]
|
||||
safe_key = hashlib.sha1((key or "").encode("utf-8", errors="ignore")).hexdigest()[:12]
|
||||
path = (tmp_dir / f"sub-{safe_key}-{digest}{ext}").resolve()
|
||||
path.write_text(text or "", encoding="utf-8", errors="replace")
|
||||
@@ -747,7 +755,9 @@ def _infer_store_for_target(*, target: str, config: dict) -> Optional[str]:
|
||||
continue
|
||||
root = None
|
||||
try:
|
||||
root = getattr(backend, "_location", None) or getattr(backend, "location", lambda: None)()
|
||||
root = (
|
||||
getattr(backend, "_location", None) or getattr(backend, "location", lambda: None)()
|
||||
)
|
||||
except Exception:
|
||||
root = None
|
||||
if not root:
|
||||
@@ -816,7 +826,9 @@ def run_auto_overlay(*, mpv: MPV, poll_s: float = 0.15, config: Optional[dict] =
|
||||
while True:
|
||||
try:
|
||||
# Toggle support (mpv Lua script sets this property; default to visible).
|
||||
visible_raw = _ipc_get_property(client, _LYRIC_VISIBLE_PROP, True, raise_on_disconnect=True)
|
||||
visible_raw = _ipc_get_property(
|
||||
client, _LYRIC_VISIBLE_PROP, True, raise_on_disconnect=True
|
||||
)
|
||||
raw_path = _ipc_get_property(client, "path", None, raise_on_disconnect=True)
|
||||
except ConnectionError:
|
||||
try:
|
||||
@@ -915,7 +927,9 @@ def run_auto_overlay(*, mpv: MPV, poll_s: float = 0.15, config: Optional[dict] =
|
||||
if is_http:
|
||||
# HTTP/HTTPS targets are only valid if they map to a store backend.
|
||||
store_from_url = _extract_store_from_url_target(target)
|
||||
store_name = store_from_url or _infer_hydrus_store_from_url_target(target=target, config=cfg)
|
||||
store_name = store_from_url or _infer_hydrus_store_from_url_target(
|
||||
target=target, config=cfg
|
||||
)
|
||||
if not store_name:
|
||||
_log("HTTP target has no store mapping; lyrics disabled")
|
||||
current_store_name = None
|
||||
@@ -962,7 +976,9 @@ def run_auto_overlay(*, mpv: MPV, poll_s: float = 0.15, config: Optional[dict] =
|
||||
except Exception:
|
||||
meta = None
|
||||
if meta is None:
|
||||
_log(f"HTTP target not found in store DB (store={store_name!r} hash={current_file_hash}); lyrics disabled")
|
||||
_log(
|
||||
f"HTTP target not found in store DB (store={store_name!r} hash={current_file_hash}); lyrics disabled"
|
||||
)
|
||||
current_store_name = None
|
||||
current_backend = None
|
||||
current_key = None
|
||||
@@ -988,9 +1004,15 @@ def run_auto_overlay(*, mpv: MPV, poll_s: float = 0.15, config: Optional[dict] =
|
||||
file_hash=current_file_hash,
|
||||
config=cfg,
|
||||
)
|
||||
current_key = f"{current_store_name}:{current_file_hash}" if current_store_name and current_file_hash else None
|
||||
current_key = (
|
||||
f"{current_store_name}:{current_file_hash}"
|
||||
if current_store_name and current_file_hash
|
||||
else None
|
||||
)
|
||||
|
||||
_log(f"Resolved store={current_store_name!r} hash={current_file_hash!r} valid={bool(current_key)}")
|
||||
_log(
|
||||
f"Resolved store={current_store_name!r} hash={current_file_hash!r} valid={bool(current_key)}"
|
||||
)
|
||||
|
||||
if not current_key or not current_backend:
|
||||
current_store_name = None
|
||||
@@ -1010,7 +1032,13 @@ def run_auto_overlay(*, mpv: MPV, poll_s: float = 0.15, config: Optional[dict] =
|
||||
|
||||
# Load/reload lyrics when we have a resolvable key and it differs from what we loaded.
|
||||
# This is important for the autofetch path: the note can appear without the mpv target changing.
|
||||
if current_key and current_key != last_loaded_key and current_store_name and current_file_hash and current_backend:
|
||||
if (
|
||||
current_key
|
||||
and current_key != last_loaded_key
|
||||
and current_store_name
|
||||
and current_file_hash
|
||||
and current_backend
|
||||
):
|
||||
notes: Dict[str, str] = {}
|
||||
try:
|
||||
notes = current_backend.get_note(current_file_hash, config=cfg) or {}
|
||||
@@ -1018,7 +1046,9 @@ def run_auto_overlay(*, mpv: MPV, poll_s: float = 0.15, config: Optional[dict] =
|
||||
notes = {}
|
||||
|
||||
try:
|
||||
_log(f"Loaded notes keys: {sorted([str(k) for k in notes.keys()]) if isinstance(notes, dict) else 'N/A'}")
|
||||
_log(
|
||||
f"Loaded notes keys: {sorted([str(k) for k in notes.keys()]) if isinstance(notes, dict) else 'N/A'}"
|
||||
)
|
||||
except Exception:
|
||||
_log("Loaded notes keys: <error>")
|
||||
|
||||
@@ -1062,7 +1092,11 @@ def run_auto_overlay(*, mpv: MPV, poll_s: float = 0.15, config: Optional[dict] =
|
||||
# Throttle attempts per key to avoid hammering APIs.
|
||||
autofetch_enabled = bool(cfg.get("lyric_autofetch", True))
|
||||
now = time.time()
|
||||
if autofetch_enabled and current_key != last_fetch_attempt_key and (now - last_fetch_attempt_at) > 2.0:
|
||||
if (
|
||||
autofetch_enabled
|
||||
and current_key != last_fetch_attempt_key
|
||||
and (now - last_fetch_attempt_at) > 2.0
|
||||
):
|
||||
last_fetch_attempt_key = current_key
|
||||
last_fetch_attempt_at = now
|
||||
|
||||
@@ -1082,7 +1116,9 @@ def run_auto_overlay(*, mpv: MPV, poll_s: float = 0.15, config: Optional[dict] =
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
_log(f"Autofetch query artist={artist!r} title={title!r} duration={duration_s!r}")
|
||||
_log(
|
||||
f"Autofetch query artist={artist!r} title={title!r} duration={duration_s!r}"
|
||||
)
|
||||
|
||||
if not artist or not title:
|
||||
_log("Autofetch skipped: requires both artist and title")
|
||||
@@ -1091,13 +1127,19 @@ def run_auto_overlay(*, mpv: MPV, poll_s: float = 0.15, config: Optional[dict] =
|
||||
fetched = _fetch_lrclib(
|
||||
artist=artist,
|
||||
title=title,
|
||||
duration_s=float(duration_s) if isinstance(duration_s, (int, float)) else None,
|
||||
duration_s=(
|
||||
float(duration_s) if isinstance(duration_s, (int, float)) else None
|
||||
),
|
||||
)
|
||||
if not fetched or not fetched.strip():
|
||||
fetched = _fetch_lyrics_ovh(artist=artist, title=title)
|
||||
if fetched and fetched.strip():
|
||||
try:
|
||||
ok = bool(current_backend.set_note(current_file_hash, "lyric", fetched, config=cfg))
|
||||
ok = bool(
|
||||
current_backend.set_note(
|
||||
current_file_hash, "lyric", fetched, config=cfg
|
||||
)
|
||||
)
|
||||
_log(f"Autofetch stored lyric note ok={ok}")
|
||||
# Next loop iteration will re-load the note.
|
||||
except Exception as exc:
|
||||
@@ -1187,7 +1229,9 @@ def run_overlay(*, mpv: MPV, entries: List[LrcLine], poll_s: float = 0.15) -> in
|
||||
|
||||
client = mpv.client()
|
||||
if not client.connect():
|
||||
print("mpv IPC is not reachable (is mpv running with --input-ipc-server?).", file=sys.stderr)
|
||||
print(
|
||||
"mpv IPC is not reachable (is mpv running with --input-ipc-server?).", file=sys.stderr
|
||||
)
|
||||
return 3
|
||||
|
||||
while True:
|
||||
@@ -1240,7 +1284,6 @@ def run_overlay(*, mpv: MPV, entries: List[LrcLine], poll_s: float = 0.15) -> in
|
||||
time.sleep(poll_s)
|
||||
|
||||
|
||||
|
||||
def main(argv: Optional[List[str]] = None) -> int:
|
||||
parser = argparse.ArgumentParser(prog="python -m MPV.lyric", add_help=True)
|
||||
parser.add_argument(
|
||||
|
||||
Reference in New Issue
Block a user