This commit is contained in:
2026-03-18 01:26:55 -07:00
parent d5c7d4683d
commit b0e89ff950
6 changed files with 1310 additions and 227 deletions

File diff suppressed because it is too large Load Diff

48
MPV/format_probe.py Normal file
View File

@@ -0,0 +1,48 @@
from __future__ import annotations
import contextlib
import io
import json
import sys
from pathlib import Path
from typing import Any, Dict
REPO_ROOT = Path(__file__).resolve().parents[1]
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
def main(argv: list[str] | None = None) -> int:
args = list(sys.argv[1:] if argv is None else argv)
if not args:
payload: Dict[str, Any] = {
"success": False,
"stdout": "",
"stderr": "",
"error": "Missing url",
"table": None,
}
print(json.dumps(payload, ensure_ascii=False))
return 2
url = str(args[0] or "").strip()
captured_stdout = io.StringIO()
captured_stderr = io.StringIO()
with contextlib.redirect_stdout(captured_stdout), contextlib.redirect_stderr(captured_stderr):
from MPV.pipeline_helper import _run_op
payload = _run_op("ytdlp-formats", {"url": url})
noisy_stdout = captured_stdout.getvalue().strip()
noisy_stderr = captured_stderr.getvalue().strip()
if noisy_stdout:
payload["stdout"] = "\n".join(filter(None, [str(payload.get("stdout") or "").strip(), noisy_stdout]))
if noisy_stderr:
payload["stderr"] = "\n".join(filter(None, [str(payload.get("stderr") or "").strip(), noisy_stderr]))
print(json.dumps(payload, ensure_ascii=False))
return 0 if payload.get("success") else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -35,7 +35,7 @@ import hashlib
import subprocess
import platform
from pathlib import Path
from typing import Any, Dict, Optional
from typing import Any, Callable, Dict, Optional
def _repo_root() -> Path:
@@ -73,6 +73,44 @@ READY_PROP = "user-data/medeia-pipeline-ready"
OBS_ID_REQUEST = 1001
_HELPER_MPV_LOG_EMITTER: Optional[Callable[[str], None]] = None
_HELPER_LOG_BACKLOG: list[str] = []
_HELPER_LOG_BACKLOG_LIMIT = 200
def _start_ready_heartbeat(ipc_path: str, stop_event: threading.Event) -> threading.Thread:
"""Keep READY_PROP fresh even when the main loop blocks on Windows pipes."""
def _heartbeat_loop() -> None:
hb_client = MPVIPCClient(socket_path=ipc_path, timeout=0.5, silent=True)
while not stop_event.is_set():
try:
if hb_client.sock is None and not hb_client.connect():
stop_event.wait(0.25)
continue
hb_client.send_command_no_wait(
["set_property_string", READY_PROP, str(int(time.time()))]
)
except Exception:
try:
hb_client.disconnect()
except Exception:
pass
stop_event.wait(0.75)
try:
hb_client.disconnect()
except Exception:
pass
thread = threading.Thread(
target=_heartbeat_loop,
name="mpv-helper-heartbeat",
daemon=True,
)
thread.start()
return thread
def _run_pipeline(pipeline_text: str, *, seeds: Any = None) -> Dict[str, Any]:
# Import after sys.path fix.
@@ -297,14 +335,11 @@ def _run_op(op: str, data: Any) -> Dict[str, Any]:
0) or 0),
}
# Provide store backend choices using the same source as CLI/Typer autocomplete.
# Provide store backend choices using the dynamic registered store registry only.
if op_name in {"store-choices",
"store_choices",
"get-store-choices",
"get_store_choices"}:
# IMPORTANT:
# - Prefer runtime cwd for config discovery (mpv spawns us with cwd=repo_root).
# - Avoid returning a cached empty result if config was loaded before it existed.
try:
from SYS.config import reload_config # noqa: WPS433
from Store import Store # noqa: WPS433
@@ -317,25 +352,6 @@ def _run_op(op: str, data: Any) -> Dict[str, Any]:
choices = sorted({str(n)
for n in backends if str(n).strip()})
# Fallback: if initialization gated all backends (e.g., missing deps or offline stores),
# still return configured instance names so the UI can present something.
if not choices:
store_cfg = cfg.get("store") if isinstance(cfg, dict) else None
if isinstance(store_cfg, dict):
seen = set()
for _, instances in store_cfg.items():
if not isinstance(instances, dict):
continue
for instance_key, instance_cfg in instances.items():
name = None
if isinstance(instance_cfg, dict):
name = instance_cfg.get("NAME"
) or instance_cfg.get("name")
candidate = str(name or instance_key or "").strip()
if candidate:
seen.add(candidate)
choices = sorted(seen)
debug(f"[store-choices] config_dir={config_root} choices={len(choices)}")
return {
@@ -564,6 +580,11 @@ def _append_helper_log(text: str) -> None:
payload = (text or "").rstrip()
if not payload:
return
_HELPER_LOG_BACKLOG.append(payload)
if len(_HELPER_LOG_BACKLOG) > _HELPER_LOG_BACKLOG_LIMIT:
del _HELPER_LOG_BACKLOG[:-_HELPER_LOG_BACKLOG_LIMIT]
try:
# Try database logging first (best practice: unified logging)
from SYS.database import log_to_db
@@ -573,6 +594,13 @@ def _append_helper_log(text: str) -> None:
import sys
print(f"[mpv-helper] {payload}", file=sys.stderr)
emitter = _HELPER_MPV_LOG_EMITTER
if emitter is not None:
try:
emitter(payload)
except Exception:
pass
def _helper_log_path() -> str:
try:
@@ -800,6 +828,25 @@ def main(argv: Optional[list[str]] = None) -> int:
# Keep trying.
time.sleep(0.10)
def _emit_helper_log_to_mpv(payload: str) -> None:
safe = str(payload or "").replace("\r", " ").replace("\n", " ").strip()
if not safe:
return
if len(safe) > 900:
safe = safe[:900] + "..."
try:
client.send_command_no_wait(["print-text", f"medeia-helper: {safe}"])
except Exception:
return
global _HELPER_MPV_LOG_EMITTER
_HELPER_MPV_LOG_EMITTER = _emit_helper_log_to_mpv
for backlog_line in list(_HELPER_LOG_BACKLOG):
try:
_emit_helper_log_to_mpv(backlog_line)
except Exception:
break
# Mark ready ASAP and keep it fresh.
# Use a unix timestamp so the Lua side can treat it as a heartbeat.
last_ready_ts: float = 0.0
@@ -862,6 +909,9 @@ def main(argv: Optional[list[str]] = None) -> int:
except Exception:
pass
heartbeat_stop = threading.Event()
_start_ready_heartbeat(str(args.ipc), heartbeat_stop)
# Pre-compute store choices at startup and publish to a cached property so Lua
# can read immediately without waiting for a request/response cycle (which may timeout).
try:
@@ -967,6 +1017,7 @@ def main(argv: Optional[list[str]] = None) -> int:
_flush_mpv_repeat()
except Exception:
pass
heartbeat_stop.set()
return 0
if msg.get("event") == "log-message":

View File

@@ -1,2 +1,2 @@
# Medeia MPV script options
store=
store=local