mirror of
https://git.srhtnin.garden/sarah/SaraTune.git
synced 2026-10-15 02:23:17 +02:00
Media controls (MPRIS, Windows SMTC); stop old tracks when switching
This commit is contained in:
1 parent
e2d7515e08
commit
d7f5eba1c2
14 files changed
+799
-26
No files matched your search
@@ -37,6 +37,9 @@ class Player:
|
||||
self.OnEnded: Callable[[], None] = lambda: None
|
||||
self.OnChanged: Callable[[], None] = lambda: None # playing/paused, duration, source
|
||||
self.OnError: Callable[[str], None] = lambda Message: None
|
||||
# Others following along (the system media controls): called with
|
||||
# "Changed", "Started" or "Seeked" after the owner's own callbacks.
|
||||
self.Watchers: list[Callable[[str], None]] = []
|
||||
|
||||
# --- Position -----------------------------------------------------------
|
||||
|
||||
@@ -51,6 +54,12 @@ class Player:
|
||||
self._Position = max(0, int(Milliseconds))
|
||||
self._PositionAt = time.monotonic()
|
||||
|
||||
def _Tell(self, Kind: str) -> None:
|
||||
if Kind == "Changed":
|
||||
self.OnChanged()
|
||||
for Watcher in self.Watchers:
|
||||
Watcher(Kind)
|
||||
|
||||
# --- Events from the client ---------------------------------------------
|
||||
|
||||
def _StateChanged(self, Audio: fta.Audio, e: fta.AudioStateChangeEvent) -> None:
|
||||
@@ -61,6 +70,7 @@ class Player:
|
||||
self._SetPosition(self._Position)
|
||||
self.Playing = True
|
||||
self.OnStarted()
|
||||
self._Tell("Started")
|
||||
elif e.state == fta.AudioState.COMPLETED:
|
||||
self.Playing = False
|
||||
self._SetPosition(0)
|
||||
@@ -72,7 +82,7 @@ class Player:
|
||||
if self.Playing:
|
||||
self._SetPosition(self.Position)
|
||||
self.Playing = False
|
||||
self.OnChanged()
|
||||
self._Tell("Changed")
|
||||
|
||||
def _PositionChanged(self, Audio: fta.Audio, e: fta.AudioPositionChangeEvent) -> None:
|
||||
if Audio is self.Audio and self.Playing:
|
||||
@@ -86,7 +96,7 @@ class Player:
|
||||
if Value > 0:
|
||||
self.Duration = Value
|
||||
self._Ready.set()
|
||||
self.OnChanged()
|
||||
self._Tell("Changed")
|
||||
|
||||
def _Loaded(self, Audio: fta.Audio) -> None:
|
||||
if Audio is self.Audio:
|
||||
@@ -109,9 +119,16 @@ class Player:
|
||||
Old, self.Audio = self.Audio, None
|
||||
if Old is None:
|
||||
return
|
||||
# Taking a service off the page doesn't stop its native player: it
|
||||
# would play on, unseen. Stop and free it first.
|
||||
for Method in (Old.pause, Old.release):
|
||||
try:
|
||||
await asyncio.wait_for(Method(), 2)
|
||||
except (asyncio.TimeoutError, RuntimeError):
|
||||
pass
|
||||
if Old in self.Page.services:
|
||||
self.Page.services.remove(Old)
|
||||
self.Page.update() # disposes the old service and its native player
|
||||
self.Page.update()
|
||||
|
||||
async def Load(self, FilePath: str, Start: int = 0, Play: bool = True, Duration: int = 0) -> bool:
|
||||
"""Switches to FilePath and (if Play) starts at Start (ms). Duration is
|
||||
@@ -138,7 +155,7 @@ class Player:
|
||||
self.Audio = Audio
|
||||
self.Page.services.append(Audio)
|
||||
self.Page.update()
|
||||
self.OnChanged()
|
||||
self._Tell("Changed")
|
||||
if Play:
|
||||
return await self.Play(Start)
|
||||
return True
|
||||
@@ -166,6 +183,7 @@ class Player:
|
||||
if not await self._Call(Audio.seek, ft.Duration(milliseconds=int(Position))):
|
||||
return False
|
||||
self._SetPosition(Position) # it played from 0 until the seek landed
|
||||
self._Tell("Seeked")
|
||||
return True
|
||||
return await self._Call(Audio.play, ft.Duration(milliseconds=0))
|
||||
|
||||
@@ -174,7 +192,7 @@ class Player:
|
||||
self._SetPosition(self.Position)
|
||||
self.Playing = False
|
||||
await self._Call(self.Audio.pause)
|
||||
self.OnChanged()
|
||||
self._Tell("Changed")
|
||||
|
||||
async def Toggle(self) -> None:
|
||||
if self.Playing:
|
||||
@@ -193,7 +211,8 @@ class Player:
|
||||
await self._Call(self.Audio.seek, ft.Duration(milliseconds=Milliseconds))
|
||||
else:
|
||||
# Paused or not started: remember it, Play starts from there.
|
||||
self.OnChanged()
|
||||
self._Tell("Changed")
|
||||
self._Tell("Seeked")
|
||||
|
||||
async def Stop(self) -> None:
|
||||
"""Back to nothing loaded."""
|
||||
@@ -202,10 +221,11 @@ class Player:
|
||||
self.Playing = False
|
||||
self.Duration = 0
|
||||
self._SetPosition(0)
|
||||
self.OnChanged()
|
||||
self._Tell("Changed")
|
||||
|
||||
def SetVolume(self, Volume: float) -> None:
|
||||
self.Volume = max(0.0, min(1.0, Volume))
|
||||
if self.Audio is not None:
|
||||
self.Audio.volume = self.Volume
|
||||
self.Audio.update()
|
||||
self._Tell("Volume")
|
||||
Reference in new issue
Block a user