Fix mpv pause state when loading new media
Fixes #107 This fixes an issue where loading a new media URL while the player was paused would keep playback paused, causing Music Assistant to show the player as playing while no audio was audible. The mpv pause state is now always set explicitly when starting playback, ensuring deterministic behavior
This commit is contained in:
1 parent
7a92d69864
commit
d42abaa2c9
6 files changed
+205
-68
No files matched your search
@@ -1,32 +1,18 @@
|
||||
"""Media player using mpv in a subprocess."""
|
||||
|
||||
import logging
|
||||
from collections.abc import Callable
|
||||
from threading import Lock
|
||||
from typing import List, Optional, Union
|
||||
from typing import Union, List, Callable, Optional
|
||||
|
||||
from mpv import MPV
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
from .player.libmpv import LibMpvPlayer
|
||||
|
||||
|
||||
class MpvMediaPlayer:
|
||||
def __init__(self, device: Optional[str] = None) -> None:
|
||||
self.player = MPV()
|
||||
"""
|
||||
LVA MediaPlayer implementation using libmpv.
|
||||
"""
|
||||
|
||||
if device:
|
||||
self.player["audio-device"] = device
|
||||
|
||||
self.is_playing = False
|
||||
|
||||
self._playlist: List[str] = []
|
||||
def __init__(self, device: str | None = None):
|
||||
self._log = logging.getLogger(self.__class__.__name__)
|
||||
self._player = LibMpvPlayer(device=device)
|
||||
self._done_callback: Optional[Callable[[], None]] = None
|
||||
self._done_callback_lock = Lock()
|
||||
|
||||
self._duck_volume: int = 50
|
||||
self._unduck_volume: int = 100
|
||||
|
||||
self.player.event_callback("end-file")(self._on_end_file)
|
||||
|
||||
def play(
|
||||
self,
|
||||
@@ -34,61 +20,31 @@ class MpvMediaPlayer:
|
||||
done_callback: Optional[Callable[[], None]] = None,
|
||||
stop_first: bool = True,
|
||||
) -> None:
|
||||
self.stop()
|
||||
self._log.info("Playing %s", url)
|
||||
|
||||
if isinstance(url, str):
|
||||
self._playlist = [url]
|
||||
else:
|
||||
self._playlist = url
|
||||
|
||||
next_url = self._playlist.pop(0)
|
||||
_LOGGER.debug("Playing %s", next_url)
|
||||
if isinstance(url, list):
|
||||
url = url[0]
|
||||
|
||||
self._done_callback = done_callback
|
||||
self.is_playing = True
|
||||
self.player.play(next_url)
|
||||
self._player.play(url, paused=False)
|
||||
|
||||
def pause(self) -> None:
|
||||
self.player.pause = True
|
||||
self.is_playing = False
|
||||
self._player.pause()
|
||||
|
||||
def resume(self) -> None:
|
||||
self.player.pause = False
|
||||
if self._playlist:
|
||||
self.is_playing = True
|
||||
self._player.resume()
|
||||
|
||||
def stop(self) -> None:
|
||||
self.player.stop()
|
||||
self._playlist.clear()
|
||||
self._player.stop()
|
||||
if self._done_callback:
|
||||
self._done_callback()
|
||||
self._done_callback = None
|
||||
|
||||
def duck(self) -> None:
|
||||
self.player.volume = self._duck_volume
|
||||
def set_volume(self, volume: float) -> None:
|
||||
self._player.set_volume(volume)
|
||||
|
||||
def duck(self, factor: float = 0.5) -> None:
|
||||
self._player.duck(factor)
|
||||
|
||||
def unduck(self) -> None:
|
||||
self.player.volume = self._unduck_volume
|
||||
|
||||
def set_volume(self, volume: int) -> None:
|
||||
volume = max(0, min(100, volume))
|
||||
self.player.volume = volume
|
||||
|
||||
self._unduck_volume = volume
|
||||
self._duck_volume = volume // 2
|
||||
|
||||
def _on_end_file(self, event) -> None:
|
||||
if self._playlist:
|
||||
self.player.play(self._playlist.pop(0))
|
||||
return
|
||||
|
||||
self.is_playing = False
|
||||
|
||||
todo_callback: Optional[Callable[[], None]] = None
|
||||
with self._done_callback_lock:
|
||||
if self._done_callback:
|
||||
todo_callback = self._done_callback
|
||||
self._done_callback = None
|
||||
|
||||
if todo_callback:
|
||||
try:
|
||||
todo_callback()
|
||||
except Exception:
|
||||
_LOGGER.exception("Unexpected error running done callback")
|
||||
self._player.unduck()
|
||||
Reference in new issue
Block a user