hkh
This commit is contained in:
@@ -48,6 +48,42 @@ def _windows_pipe_available(path: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _windows_pipe_bytes_available(pipe: BinaryIO) -> Optional[int]:
|
||||
"""Return the number of bytes ready to read from a Windows named pipe."""
|
||||
if platform.system() != "Windows":
|
||||
return None
|
||||
try:
|
||||
import msvcrt
|
||||
|
||||
handle = msvcrt.get_osfhandle(pipe.fileno())
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
PeekNamedPipe = kernel32.PeekNamedPipe
|
||||
PeekNamedPipe.argtypes = [
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_void_p,
|
||||
ctypes.c_uint32,
|
||||
ctypes.c_void_p,
|
||||
ctypes.POINTER(ctypes.c_uint32),
|
||||
ctypes.c_void_p,
|
||||
]
|
||||
PeekNamedPipe.restype = ctypes.c_bool
|
||||
|
||||
total_available = ctypes.c_uint32(0)
|
||||
ok = PeekNamedPipe(
|
||||
ctypes.c_void_p(handle),
|
||||
None,
|
||||
0,
|
||||
None,
|
||||
ctypes.byref(total_available),
|
||||
None,
|
||||
)
|
||||
if not ok:
|
||||
return None
|
||||
return int(total_available.value)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _windows_pythonw_exe(python_exe: Optional[str]) -> Optional[str]:
|
||||
"""Return a pythonw.exe adjacent to python.exe if available (Windows only)."""
|
||||
if platform.system() != "Windows":
|
||||
@@ -921,15 +957,46 @@ class MPVIPCClient:
|
||||
deadline = _time.time() + max(0.0, effective_timeout)
|
||||
|
||||
if self.is_windows:
|
||||
try:
|
||||
pipe = cast(BinaryIO, self.sock)
|
||||
return pipe.readline()
|
||||
except (OSError, IOError, BrokenPipeError, ConnectionResetError) as exc:
|
||||
# Pipe error; try to reconnect once
|
||||
if not self.silent:
|
||||
debug(f"Pipe readline failed: {exc}")
|
||||
self.disconnect()
|
||||
return None
|
||||
pipe = cast(BinaryIO, self.sock)
|
||||
while True:
|
||||
nl = self._recv_buffer.find(b"\n")
|
||||
if nl != -1:
|
||||
line = self._recv_buffer[:nl + 1]
|
||||
self._recv_buffer = self._recv_buffer[nl + 1:]
|
||||
return line
|
||||
|
||||
remaining = deadline - _time.time()
|
||||
if remaining <= 0:
|
||||
return None
|
||||
|
||||
try:
|
||||
available = _windows_pipe_bytes_available(pipe)
|
||||
except Exception as exc:
|
||||
if not self.silent:
|
||||
debug(f"Pipe availability probe failed: {exc}")
|
||||
self.disconnect()
|
||||
return None
|
||||
|
||||
if available is None:
|
||||
self.disconnect()
|
||||
return None
|
||||
|
||||
if available <= 0:
|
||||
_time.sleep(min(0.01, max(0.001, remaining)))
|
||||
continue
|
||||
|
||||
try:
|
||||
chunk = pipe.read(min(available, 4096))
|
||||
except (OSError, IOError, BrokenPipeError, ConnectionResetError) as exc:
|
||||
if not self.silent:
|
||||
debug(f"Pipe readline failed: {exc}")
|
||||
self.disconnect()
|
||||
return None
|
||||
|
||||
if not chunk:
|
||||
return b""
|
||||
|
||||
self._recv_buffer += chunk
|
||||
|
||||
# Unix: buffer until newline.
|
||||
sock_obj = cast(socket.socket, self.sock)
|
||||
@@ -1034,6 +1101,7 @@ class MPVIPCClient:
|
||||
try:
|
||||
# Try to open the named pipe
|
||||
self.sock = open(self.socket_path, "r+b", buffering=0)
|
||||
self._recv_buffer = b""
|
||||
return True
|
||||
except (OSError, IOError) as exc:
|
||||
if not self.silent:
|
||||
@@ -1055,6 +1123,7 @@ class MPVIPCClient:
|
||||
self.sock = socket.socket(af_unix, socket.SOCK_STREAM)
|
||||
self.sock.settimeout(self.timeout)
|
||||
self.sock.connect(self.socket_path)
|
||||
self._recv_buffer = b""
|
||||
return True
|
||||
except Exception as exc:
|
||||
if not self.silent:
|
||||
@@ -1167,6 +1236,7 @@ class MPVIPCClient:
|
||||
except Exception:
|
||||
pass
|
||||
self.sock = None
|
||||
self._recv_buffer = b""
|
||||
|
||||
def __del__(self) -> None:
|
||||
"""Cleanup on object destruction."""
|
||||
|
||||
Reference in New Issue
Block a user