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

View File

@@ -92,7 +92,7 @@
"(hitfile\\.net/[a-z0-9A-Z]{4,9})" "(hitfile\\.net/[a-z0-9A-Z]{4,9})"
], ],
"regexp": "(hitf\\.(to|cc)/([a-z0-9A-Z]{4,9}))|(htfl\\.(net|to|cc)/([a-z0-9A-Z]{4,9}))|(hitfile\\.(net)/download/free/([a-z0-9A-Z]{4,9}))|((hitfile\\.net/[a-z0-9A-Z]{4,9}))", "regexp": "(hitf\\.(to|cc)/([a-z0-9A-Z]{4,9}))|(htfl\\.(net|to|cc)/([a-z0-9A-Z]{4,9}))|(hitfile\\.(net)/download/free/([a-z0-9A-Z]{4,9}))|((hitfile\\.net/[a-z0-9A-Z]{4,9}))",
"status": true "status": false
}, },
"mega": { "mega": {
"name": "mega", "name": "mega",
@@ -595,7 +595,7 @@
"(simfileshare\\.net/download/[0-9]+/)" "(simfileshare\\.net/download/[0-9]+/)"
], ],
"regexp": "(simfileshare\\.net/download/[0-9]+/)", "regexp": "(simfileshare\\.net/download/[0-9]+/)",
"status": false "status": true
}, },
"streamtape": { "streamtape": {
"name": "streamtape", "name": "streamtape",

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 subprocess
import platform import platform
from pathlib import Path from pathlib import Path
from typing import Any, Dict, Optional from typing import Any, Callable, Dict, Optional
def _repo_root() -> Path: def _repo_root() -> Path:
@@ -73,6 +73,44 @@ READY_PROP = "user-data/medeia-pipeline-ready"
OBS_ID_REQUEST = 1001 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]: def _run_pipeline(pipeline_text: str, *, seeds: Any = None) -> Dict[str, Any]:
# Import after sys.path fix. # Import after sys.path fix.
@@ -297,14 +335,11 @@ def _run_op(op: str, data: Any) -> Dict[str, Any]:
0) or 0), 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", if op_name in {"store-choices",
"store_choices", "store_choices",
"get-store-choices", "get-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: try:
from SYS.config import reload_config # noqa: WPS433 from SYS.config import reload_config # noqa: WPS433
from Store import Store # 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) choices = sorted({str(n)
for n in backends if str(n).strip()}) 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)}") debug(f"[store-choices] config_dir={config_root} choices={len(choices)}")
return { return {
@@ -564,6 +580,11 @@ def _append_helper_log(text: str) -> None:
payload = (text or "").rstrip() payload = (text or "").rstrip()
if not payload: if not payload:
return 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:
# Try database logging first (best practice: unified logging) # Try database logging first (best practice: unified logging)
from SYS.database import log_to_db from SYS.database import log_to_db
@@ -573,6 +594,13 @@ def _append_helper_log(text: str) -> None:
import sys import sys
print(f"[mpv-helper] {payload}", file=sys.stderr) 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: def _helper_log_path() -> str:
try: try:
@@ -800,6 +828,25 @@ def main(argv: Optional[list[str]] = None) -> int:
# Keep trying. # Keep trying.
time.sleep(0.10) 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. # Mark ready ASAP and keep it fresh.
# Use a unix timestamp so the Lua side can treat it as a heartbeat. # Use a unix timestamp so the Lua side can treat it as a heartbeat.
last_ready_ts: float = 0.0 last_ready_ts: float = 0.0
@@ -862,6 +909,9 @@ def main(argv: Optional[list[str]] = None) -> int:
except Exception: except Exception:
pass 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 # 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). # can read immediately without waiting for a request/response cycle (which may timeout).
try: try:
@@ -967,6 +1017,7 @@ def main(argv: Optional[list[str]] = None) -> int:
_flush_mpv_repeat() _flush_mpv_repeat()
except Exception: except Exception:
pass pass
heartbeat_stop.set()
return 0 return 0
if msg.get("event") == "log-message": if msg.get("event") == "log-message":

View File

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

View File

@@ -2381,6 +2381,22 @@ def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
except Exception as e: except Exception as e:
debug(f"Could not fetch database logs: {e}") debug(f"Could not fetch database logs: {e}")
pass pass
fallback_logs = [
("Medeia Lua log file tail", str(_lua_log_file())),
("Medeia helper log file tail", str(_helper_log_file())),
]
for title, path in fallback_logs:
try:
lines = _tail_text_file(path, max_lines=120)
except Exception:
lines = []
lines = _apply_log_filter(lines, log_filter_text)
if not lines:
continue
print(f"{title}:")
for line in lines:
print(line)
except Exception: except Exception:
pass pass
try: try: