f
This commit is contained in:
1368
MPV/LUA/main.lua
1368
MPV/LUA/main.lua
File diff suppressed because it is too large
Load Diff
48
MPV/format_probe.py
Normal file
48
MPV/format_probe.py
Normal 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())
|
||||
@@ -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":
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
# Medeia MPV script options
|
||||
store=
|
||||
store=local
|
||||
|
||||
Reference in New Issue
Block a user