Merge pull request #176 from xodexa/self-fix

This commit is contained in:
Benedict Xavier
2025-12-30 09:28:59 +03:00
committed by GitHub
5 changed files with 63 additions and 10 deletions

View File

@@ -362,6 +362,9 @@ You can run the background worker as a systemd service for persistence.
systemctl --user daemon-reload
systemctl --user enable --now viu-worker.service
```
## Project using it
**[Inazuma](https://github.com/viu-media/Inazuma)** - official gui wrapper over viu built in kivymd
## Contributing

View File

@@ -1,6 +1,6 @@
[project]
name = "viu-media"
version = "3.3.4"
version = "3.3.5"
description = "A browser anime site experience from the terminal"
license = "UNLICENSE"
readme = "README.md"

2
uv.lock generated
View File

@@ -3743,7 +3743,7 @@ wheels = [
[[package]]
name = "viu-media"
version = "3.3.4"
version = "3.3.5"
source = { editable = "." }
dependencies = [
{ name = "click" },

View File

@@ -71,7 +71,7 @@ class ConfigLoader:
return app_config
def load(self, update: Dict = {}) -> AppConfig:
def load(self, update: Dict = {}, allow_setup=True) -> AppConfig:
"""
Loads the configuration and returns a populated, validated AppConfig object.
@@ -84,7 +84,7 @@ class ConfigLoader:
Raises:
ConfigError: If the configuration file contains validation or parsing errors.
"""
if not self.config_path.exists():
if not self.config_path.exists() and allow_setup:
return self._handle_first_run()
try:

View File

@@ -5,6 +5,7 @@ This provides advanced features like episode navigation, quality switching, and
import json
import logging
import os
import socket
import subprocess
import tempfile
@@ -43,7 +44,7 @@ class MPVIPCClient:
def __init__(self, socket_path: str):
self.socket_path = socket_path
self.socket: Optional[socket.socket] = None
self.socket: Optional[Any] = None
self._request_id_counter = 0
self._lock = threading.Lock()
@@ -55,13 +56,54 @@ class MPVIPCClient:
self._response_dict: Dict[int, Any] = {}
self._response_events: Dict[int, threading.Event] = {}
@staticmethod
def _is_windows_named_pipe(path: str) -> bool:
return path.startswith("\\\\.\\pipe\\")
@staticmethod
def _supports_unix_sockets() -> bool:
return hasattr(socket, "AF_UNIX")
@staticmethod
def _open_windows_named_pipe(path: str):
# MPV's JSON IPC on Windows uses named pipes like: \\.\pipe\mpvpipe
# Opening the pipe as a binary file supports read/write.
f = open(path, "r+b", buffering=0)
class _PipeConn:
def __init__(self, fileobj):
self._f = fileobj
def recv(self, n: int) -> bytes:
return self._f.read(n)
def sendall(self, data: bytes) -> None:
self._f.write(data)
self._f.flush()
def close(self) -> None:
self._f.close()
return _PipeConn(f)
def connect(self, timeout: float = 5.0) -> None:
"""Connect to MPV IPC socket and start the reader thread."""
start_time = time.time()
while time.time() - start_time < timeout:
try:
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.socket.connect(self.socket_path)
if self._supports_unix_sockets() and not self._is_windows_named_pipe(
self.socket_path
):
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.socket.connect(self.socket_path)
else:
if os.name != "nt" or not self._is_windows_named_pipe(self.socket_path):
raise MPVIPCError(
"MPV IPC requires Unix domain sockets (AF_UNIX) or a Windows named pipe path "
"like \\\\.\\pipe\\mpvpipe. Got: "
f"{self.socket_path}"
)
self.socket = self._open_windows_named_pipe(self.socket_path)
logger.info(f"Connected to MPV IPC socket at {self.socket_path}")
self._start_reader_thread()
return
@@ -329,8 +371,12 @@ class MpvIPCPlayer(BaseIPCPlayer):
def _start_mpv_process(self, player: BasePlayer, params: PlayerParams) -> None:
"""Start MPV process with IPC enabled."""
temp_dir = Path(tempfile.gettempdir())
self.socket_path = str(temp_dir / f"mpv_ipc_{time.time()}.sock")
if hasattr(socket, "AF_UNIX"):
temp_dir = Path(tempfile.gettempdir())
self.socket_path = str(temp_dir / f"mpv_ipc_{time.time()}.sock")
else:
# Windows MPV IPC uses named pipes.
self.socket_path = f"\\\\.\\pipe\\mpv_ipc_{int(time.time() * 1000)}"
self.mpv_process = player.play_with_ipc(params, self.socket_path)
time.sleep(1.0)
@@ -480,7 +526,11 @@ class MpvIPCPlayer(BaseIPCPlayer):
self.mpv_process.wait(timeout=3)
except subprocess.TimeoutExpired:
self.mpv_process.kill()
if self.socket_path and Path(self.socket_path).exists():
if (
self.socket_path
and not self.socket_path.startswith("\\\\.\\pipe\\")
and Path(self.socket_path).exists()
):
Path(self.socket_path).unlink(missing_ok=True)
def _get_episode(