f
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
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 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":
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
# Medeia MPV script options
|
# Medeia MPV script options
|
||||||
store=
|
store=local
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user