ff
This commit is contained in:
@@ -872,13 +872,20 @@ class MPVIPCClient:
|
||||
|
||||
try:
|
||||
if self.is_windows:
|
||||
# BinaryIO pipe from open('\\\\.\\pipe\\...')
|
||||
pipe = cast(BinaryIO, self.sock)
|
||||
pipe.write(payload.encode("utf-8"))
|
||||
pipe.flush()
|
||||
try:
|
||||
pipe.write(payload.encode("utf-8"))
|
||||
pipe.flush()
|
||||
except OSError as e:
|
||||
# Windows Errno 22 (EINVAL) often means the pipe handle is now invalid/closed
|
||||
if getattr(e, "errno", 0) == 22:
|
||||
raise BrokenPipeError(str(e))
|
||||
raise
|
||||
else:
|
||||
sock_obj = cast(socket.socket, self.sock)
|
||||
sock_obj.sendall(payload.encode("utf-8"))
|
||||
except (OSError, IOError, BrokenPipeError) as exc:
|
||||
except (OSError, IOError, BrokenPipeError, ConnectionResetError) as exc:
|
||||
# Pipe became invalid (disconnected, corrupted, etc.).
|
||||
# Disconnect and attempt one reconnection.
|
||||
if not self.silent:
|
||||
@@ -889,12 +896,17 @@ class MPVIPCClient:
|
||||
try:
|
||||
if self.is_windows:
|
||||
pipe = cast(BinaryIO, self.sock)
|
||||
pipe.write(payload.encode("utf-8"))
|
||||
pipe.flush()
|
||||
try:
|
||||
pipe.write(payload.encode("utf-8"))
|
||||
pipe.flush()
|
||||
except OSError as e:
|
||||
if getattr(e, "errno", 0) == 22:
|
||||
raise BrokenPipeError(str(e))
|
||||
raise
|
||||
else:
|
||||
sock_obj = cast(socket.socket, self.sock)
|
||||
sock_obj.sendall(payload.encode("utf-8"))
|
||||
except (OSError, IOError, BrokenPipeError) as retry_exc:
|
||||
except (OSError, IOError, BrokenPipeError, ConnectionResetError) as retry_exc:
|
||||
self.disconnect()
|
||||
raise MPVIPCError(f"Pipe write failed after reconnect: {retry_exc}") from retry_exc
|
||||
else:
|
||||
@@ -912,7 +924,7 @@ class MPVIPCClient:
|
||||
try:
|
||||
pipe = cast(BinaryIO, self.sock)
|
||||
return pipe.readline()
|
||||
except (OSError, IOError, BrokenPipeError) as exc:
|
||||
except (OSError, IOError, BrokenPipeError, ConnectionResetError) as exc:
|
||||
# Pipe error; try to reconnect once
|
||||
if not self.silent:
|
||||
debug(f"Pipe readline failed: {exc}")
|
||||
|
||||
@@ -28,6 +28,7 @@ import os
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import threading
|
||||
import logging
|
||||
import re
|
||||
import hashlib
|
||||
@@ -134,8 +135,16 @@ def _run_pipeline(pipeline_text: str, *, seeds: Any = None) -> Dict[str, Any]:
|
||||
"rows": rows_payload
|
||||
}
|
||||
|
||||
start_time = time.time()
|
||||
runner = PipelineRunner()
|
||||
result = runner.run_pipeline(pipeline_text, seeds=seeds)
|
||||
duration = time.time() - start_time
|
||||
try:
|
||||
_append_helper_log(
|
||||
f"[pipeline] run_pipeline completed in {duration:.2f}s pipeline={pipeline_text[:64]}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
table_payload = None
|
||||
try:
|
||||
@@ -152,6 +161,31 @@ def _run_pipeline(pipeline_text: str, *, seeds: Any = None) -> Dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def _run_pipeline_background(pipeline_text: str, *, seeds: Any, req_id: str) -> None:
|
||||
def _target() -> None:
|
||||
try:
|
||||
result = _run_pipeline(pipeline_text, seeds=seeds)
|
||||
status = "success" if result.get("success") else "failed"
|
||||
_append_helper_log(
|
||||
f"[pipeline async {req_id}] {status} error={result.get('error')}"
|
||||
)
|
||||
except Exception as exc: # pragma: no cover - best-effort logging
|
||||
_append_helper_log(
|
||||
f"[pipeline async {req_id}] exception: {type(exc).__name__}: {exc}"
|
||||
)
|
||||
|
||||
thread = threading.Thread(
|
||||
target=_target,
|
||||
name=f"pipeline-async-{req_id}",
|
||||
daemon=True,
|
||||
)
|
||||
thread.start()
|
||||
|
||||
|
||||
def _is_load_url_pipeline(pipeline_text: str) -> bool:
|
||||
return str(pipeline_text or "").lstrip().lower().startswith(".mpv -url")
|
||||
|
||||
|
||||
def _run_op(op: str, data: Any) -> Dict[str, Any]:
|
||||
"""Run a helper-only operation.
|
||||
|
||||
@@ -1030,13 +1064,29 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
async_dispatch = False
|
||||
try:
|
||||
if op:
|
||||
run = _run_op(op, data)
|
||||
else:
|
||||
if not pipeline_text:
|
||||
continue
|
||||
run = _run_pipeline(pipeline_text, seeds=seeds)
|
||||
if _is_load_url_pipeline(pipeline_text):
|
||||
async_dispatch = True
|
||||
run = {
|
||||
"success": True,
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"error": "",
|
||||
"table": None,
|
||||
}
|
||||
_run_pipeline_background(
|
||||
pipeline_text,
|
||||
seeds=seeds,
|
||||
req_id=req_id,
|
||||
)
|
||||
else:
|
||||
run = _run_pipeline(pipeline_text, seeds=seeds)
|
||||
|
||||
resp = {
|
||||
"id": req_id,
|
||||
@@ -1050,6 +1100,8 @@ def main(argv: Optional[list[str]] = None) -> int:
|
||||
}
|
||||
if "choices" in run:
|
||||
resp["choices"] = run.get("choices")
|
||||
if async_dispatch:
|
||||
resp["info"] = "queued asynchronously"
|
||||
except Exception as exc:
|
||||
resp = {
|
||||
"id": req_id,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# uosc provides seeking & volume indicators (via flash-timeline and flash-volume commands)
|
||||
# if you decide to use them, you don't need osd-bar
|
||||
osd-bar=no
|
||||
|
||||
ytdl=yes
|
||||
# uosc will draw its own window controls and border if you disable window border
|
||||
border=no
|
||||
|
||||
|
||||
Reference in New Issue
Block a user