Adjust receiver protocol to accommodate config

This commit is contained in:
Danielle McLean 2024-07-11 12:15:34 +10:00
parent 09fe3b3e6c
commit 04859b8c8b
Signed by: 00dani
GPG key ID: 6854781A0488421C
5 changed files with 32 additions and 21 deletions

View file

@ -10,15 +10,15 @@ from .mpd.listener import MpdStateListener
from .song_receiver import (
Receiver,
choose_loop_factory,
import_receiver,
construct_receiver,
)
async def listen(
config: Config, listener: MpdStateListener, receiver_types: Iterable[type[Receiver]]
config: Config, listener: MpdStateListener, receivers: Iterable[Receiver]
) -> None:
await listener.start(config.mpd)
receivers = (rec(listener, config) for rec in receiver_types)
await asyncio.gather(*(rec.start(listener) for rec in receivers))
await listener.loop(receivers)
@ -28,11 +28,11 @@ def main() -> None:
print(config)
listener = MpdStateListener(config.cache)
receiver_types = tuple(import_receiver(rec) for rec in config.receivers)
receivers = tuple(construct_receiver(rec_config) for rec_config in config.receivers)
factory = choose_loop_factory(receivers)
factory = choose_loop_factory(receiver_types)
asyncio.run(
listen(config, listener, receiver_types),
listen(config, listener, receivers),
loop_factory=factory.make_loop,
debug=True,
)

View file

@ -21,7 +21,7 @@ class BaseReceiverConfig(Protocol):
@dataclass(slots=True)
class CocoaReceiverConfig(BaseReceiverConfig):
kind: Literal["cocoa"] = "cocoa"
kind: Literal["cocoa"] = field(default="cocoa", repr=False)
ReceiverConfig = Annotated[

View file

@ -83,8 +83,7 @@ class MpdStateListener(Player):
if status["state"] == "stop":
print("Nothing playing")
for r in self.receivers:
r.update(None)
await self.update(None)
return
art = await self.art_cache.get_cached_artwork(current)
@ -93,8 +92,10 @@ class MpdStateListener(Player):
song = mpd_current_to_song(status, current, to_artwork(art))
rprint(song)
for r in self.receivers:
r.update(song)
await self.update(song)
async def update(self, song: Song | None) -> None:
await asyncio.gather(*(r.update(song) for r in self.receivers))
async def get_art(self, file: str) -> bytes | None:
picture = await self.readpicture(file)

View file

@ -37,7 +37,7 @@ from MediaPlayer import (
MPRemoteCommandHandlerStatusSuccess,
)
from ...config.model import Config
from ...config.model import CocoaReceiverConfig
from ...player import Player
from ...song import PlaybackState, Song
from ...song_receiver import LoopFactory, Receiver
@ -146,7 +146,10 @@ class CocoaNowPlayingReceiver(Receiver):
def loop_factory(cls) -> LoopFactory[CoreFoundationEventLoop]:
return CocoaLoopFactory()
def __init__(self, player: Player, config: Config):
def __init__(self, config: CocoaReceiverConfig):
pass
async def start(self, player: Player) -> None:
self.cmd_center = MPRemoteCommandCenter.sharedCommandCenter()
self.info_center = MPNowPlayingInfoCenter.defaultCenter()
@ -184,7 +187,7 @@ class CocoaNowPlayingReceiver(Receiver):
# unpause with remote commands.
self.info_center.setPlaybackState_(MPMusicPlaybackStatePlaying)
def update(self, song: Song | None) -> None:
async def update(self, song: Song | None) -> None:
if song:
self.info_center.setNowPlayingInfo_(song_to_media_item(song))
self.info_center.setPlaybackState_(playback_state_to_cocoa(song.state))

View file

@ -3,7 +3,7 @@ from dataclasses import dataclass
from importlib import import_module
from typing import Generic, Iterable, Literal, Protocol, TypeVar, cast
from .config.model import BaseReceiverConfig, Config
from .config.model import BaseReceiverConfig
from .player import Player
from .song import Song
from .tools.types import not_none
@ -20,10 +20,12 @@ class LoopFactory(Generic[T], Protocol):
class Receiver(Protocol):
def __init__(self, player: Player, config: Config) -> None: ...
def __init__(self, config: BaseReceiverConfig): ...
@classmethod
def loop_factory(cls) -> LoopFactory[AbstractEventLoop]: ...
def update(self, song: Song | None) -> None: ...
async def start(self, player: Player) -> None: ...
async def update(self, song: Song | None) -> None: ...
class ReceiverModule(Protocol):
@ -42,8 +44,8 @@ class DefaultLoopFactory(LoopFactory[AbstractEventLoop]):
@dataclass
class IncompatibleReceiverError(Exception):
a: type[Receiver]
b: type[Receiver]
a: Receiver
b: Receiver
def import_receiver(config: BaseReceiverConfig) -> type[Receiver]:
@ -53,13 +55,18 @@ def import_receiver(config: BaseReceiverConfig) -> type[Receiver]:
return mod.receiver
def construct_receiver(config: BaseReceiverConfig) -> Receiver:
cls = import_receiver(config)
return cls(config)
def choose_loop_factory(
receivers: Iterable[type[Receiver]],
receivers: Iterable[Receiver],
) -> LoopFactory[AbstractEventLoop]:
"""Given the desired receivers, determine which asyncio event loop implementation will support all of them. Will raise an IncompatibleReceiverError if no such implementation exists."""
chosen_fac: LoopFactory[AbstractEventLoop] = DefaultLoopFactory()
chosen_rec: type[Receiver] | None = None
chosen_rec: Receiver | None = None
for rec in receivers:
fac = rec.loop_factory()