Skip to content
This repository was archived by the owner on May 2, 2026. It is now read-only.

Commit 91aa6b5

Browse files
authored
Merge pull request #191 from viu-media/minor-fixes
Fix #188 wrong video resolution from animepahe provider
2 parents 59b2a4f + d05128e commit 91aa6b5

2 files changed

Lines changed: 66 additions & 12 deletions

File tree

viu_media/cli/service/player/ipc/mpv.py

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import json
77
import logging
8+
import os
89
import socket
910
import subprocess
1011
import tempfile
@@ -43,7 +44,7 @@ class MPVIPCClient:
4344

4445
def __init__(self, socket_path: str):
4546
self.socket_path = socket_path
46-
self.socket: Optional[socket.socket] = None
47+
self.socket: Optional[Any] = None
4748
self._request_id_counter = 0
4849
self._lock = threading.Lock()
4950

@@ -55,16 +56,55 @@ def __init__(self, socket_path: str):
5556
self._response_dict: Dict[int, Any] = {}
5657
self._response_events: Dict[int, threading.Event] = {}
5758

59+
@staticmethod
60+
def _is_windows_named_pipe(path: str) -> bool:
61+
return path.startswith("\\\\.\\pipe\\")
62+
63+
@staticmethod
64+
def _supports_unix_sockets() -> bool:
65+
return hasattr(socket, "AF_UNIX")
66+
67+
@staticmethod
68+
def _open_windows_named_pipe(path: str):
69+
# MPV's JSON IPC on Windows uses named pipes like: \\.\pipe\mpvpipe
70+
# Opening the pipe as a binary file supports read/write.
71+
f = open(path, "r+b", buffering=0)
72+
73+
class _PipeConn:
74+
def __init__(self, fileobj):
75+
self._f = fileobj
76+
77+
def recv(self, n: int) -> bytes:
78+
return self._f.read(n)
79+
80+
def sendall(self, data: bytes) -> None:
81+
self._f.write(data)
82+
self._f.flush()
83+
84+
def close(self) -> None:
85+
self._f.close()
86+
87+
return _PipeConn(f)
88+
5889
def connect(self, timeout: float = 5.0) -> None:
5990
"""Connect to MPV IPC socket and start the reader thread."""
60-
if not hasattr(socket, "AF_UNIX"):
61-
raise MPVIPCError("Unix domain sockets are unavailable on this platform")
6291

6392
start_time = time.time()
6493
while time.time() - start_time < timeout:
6594
try:
66-
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
67-
self.socket.connect(self.socket_path)
95+
if self._supports_unix_sockets() and not self._is_windows_named_pipe(
96+
self.socket_path
97+
):
98+
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) # type: ignore (type error on Windows but this code path won't be used there)
99+
self.socket.connect(self.socket_path)
100+
else:
101+
if os.name != "nt" or not self._is_windows_named_pipe(self.socket_path):
102+
raise MPVIPCError(
103+
"MPV IPC requires Unix domain sockets (AF_UNIX) or a Windows named pipe path "
104+
"like \\\\.\\pipe\\mpvpipe. Got: "
105+
f"{self.socket_path}"
106+
)
107+
self.socket = self._open_windows_named_pipe(self.socket_path)
68108
logger.info(f"Connected to MPV IPC socket at {self.socket_path}")
69109
self._start_reader_thread()
70110
return
@@ -302,10 +342,6 @@ def play(
302342
def _play_with_ipc(self, player: BasePlayer, params: PlayerParams) -> PlayerResult:
303343
"""Play media using MPV IPC."""
304344
try:
305-
if not hasattr(socket, "AF_UNIX"):
306-
raise MPVIPCError(
307-
"MPV IPC requires Unix domain sockets, which are unavailable on this platform."
308-
)
309345
self._start_mpv_process(player, params)
310346
self._connect_ipc()
311347
self._setup_event_handling()
@@ -336,8 +372,12 @@ def _play_with_ipc(self, player: BasePlayer, params: PlayerParams) -> PlayerResu
336372

337373
def _start_mpv_process(self, player: BasePlayer, params: PlayerParams) -> None:
338374
"""Start MPV process with IPC enabled."""
339-
temp_dir = Path(tempfile.gettempdir())
340-
self.socket_path = str(temp_dir / f"mpv_ipc_{time.time()}.sock")
375+
if hasattr(socket, "AF_UNIX"):
376+
temp_dir = Path(tempfile.gettempdir())
377+
self.socket_path = str(temp_dir / f"mpv_ipc_{time.time()}.sock")
378+
else:
379+
# Windows MPV IPC uses named pipes.
380+
self.socket_path = f"\\\\.\\pipe\\mpv_ipc_{int(time.time() * 1000)}"
341381
self.mpv_process = player.play_with_ipc(params, self.socket_path)
342382
time.sleep(1.0)
343383

@@ -487,7 +527,11 @@ def _cleanup(self):
487527
self.mpv_process.wait(timeout=3)
488528
except subprocess.TimeoutExpired:
489529
self.mpv_process.kill()
490-
if self.socket_path and Path(self.socket_path).exists():
530+
if (
531+
self.socket_path
532+
and not self.socket_path.startswith("\\\\.\\pipe\\")
533+
and Path(self.socket_path).exists()
534+
):
491535
Path(self.socket_path).unlink(missing_ok=True)
492536

493537
def _get_episode(

viu_media/libs/provider/anime/animepahe/mappers.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import logging
2+
13
from ..types import (
24
Anime,
35
AnimeEpisodeInfo,
@@ -20,6 +22,8 @@
2022
"raw": MediaTranslationType.RAW,
2123
}
2224

25+
logger = logging.getLogger(__name__)
26+
2327

2428
def map_to_search_results(data: AnimePaheSearchPage) -> SearchResults:
2529
results = []
@@ -98,6 +102,12 @@ def map_to_server(
98102
)
99103
for link in stream_links
100104
]
105+
106+
# sort links by quality, best to worst
107+
links.sort(key=lambda x: int(x.quality), reverse=True)
108+
logger.debug(f"Aggregated links: {links}")
109+
101110
return Server(
102111
name="kwik", links=links, episode_title=episode.title, headers=headers
103112
)
113+

0 commit comments

Comments
 (0)