Adjust receiver protocol to accommodate config
This commit is contained in:
parent
09fe3b3e6c
commit
04859b8c8b
5 changed files with 32 additions and 21 deletions
|
@ -10,15 +10,15 @@ from .mpd.listener import MpdStateListener
|
||||||
from .song_receiver import (
|
from .song_receiver import (
|
||||||
Receiver,
|
Receiver,
|
||||||
choose_loop_factory,
|
choose_loop_factory,
|
||||||
import_receiver,
|
construct_receiver,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def listen(
|
async def listen(
|
||||||
config: Config, listener: MpdStateListener, receiver_types: Iterable[type[Receiver]]
|
config: Config, listener: MpdStateListener, receivers: Iterable[Receiver]
|
||||||
) -> None:
|
) -> None:
|
||||||
await listener.start(config.mpd)
|
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)
|
await listener.loop(receivers)
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,11 +28,11 @@ def main() -> None:
|
||||||
print(config)
|
print(config)
|
||||||
|
|
||||||
listener = MpdStateListener(config.cache)
|
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(
|
asyncio.run(
|
||||||
listen(config, listener, receiver_types),
|
listen(config, listener, receivers),
|
||||||
loop_factory=factory.make_loop,
|
loop_factory=factory.make_loop,
|
||||||
debug=True,
|
debug=True,
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,7 +21,7 @@ class BaseReceiverConfig(Protocol):
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
class CocoaReceiverConfig(BaseReceiverConfig):
|
class CocoaReceiverConfig(BaseReceiverConfig):
|
||||||
kind: Literal["cocoa"] = "cocoa"
|
kind: Literal["cocoa"] = field(default="cocoa", repr=False)
|
||||||
|
|
||||||
|
|
||||||
ReceiverConfig = Annotated[
|
ReceiverConfig = Annotated[
|
||||||
|
|
|
@ -83,8 +83,7 @@ class MpdStateListener(Player):
|
||||||
|
|
||||||
if status["state"] == "stop":
|
if status["state"] == "stop":
|
||||||
print("Nothing playing")
|
print("Nothing playing")
|
||||||
for r in self.receivers:
|
await self.update(None)
|
||||||
r.update(None)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
art = await self.art_cache.get_cached_artwork(current)
|
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))
|
song = mpd_current_to_song(status, current, to_artwork(art))
|
||||||
rprint(song)
|
rprint(song)
|
||||||
for r in self.receivers:
|
await self.update(song)
|
||||||
r.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:
|
async def get_art(self, file: str) -> bytes | None:
|
||||||
picture = await self.readpicture(file)
|
picture = await self.readpicture(file)
|
||||||
|
|
|
@ -37,7 +37,7 @@ from MediaPlayer import (
|
||||||
MPRemoteCommandHandlerStatusSuccess,
|
MPRemoteCommandHandlerStatusSuccess,
|
||||||
)
|
)
|
||||||
|
|
||||||
from ...config.model import Config
|
from ...config.model import CocoaReceiverConfig
|
||||||
from ...player import Player
|
from ...player import Player
|
||||||
from ...song import PlaybackState, Song
|
from ...song import PlaybackState, Song
|
||||||
from ...song_receiver import LoopFactory, Receiver
|
from ...song_receiver import LoopFactory, Receiver
|
||||||
|
@ -146,7 +146,10 @@ class CocoaNowPlayingReceiver(Receiver):
|
||||||
def loop_factory(cls) -> LoopFactory[CoreFoundationEventLoop]:
|
def loop_factory(cls) -> LoopFactory[CoreFoundationEventLoop]:
|
||||||
return CocoaLoopFactory()
|
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.cmd_center = MPRemoteCommandCenter.sharedCommandCenter()
|
||||||
self.info_center = MPNowPlayingInfoCenter.defaultCenter()
|
self.info_center = MPNowPlayingInfoCenter.defaultCenter()
|
||||||
|
|
||||||
|
@ -184,7 +187,7 @@ class CocoaNowPlayingReceiver(Receiver):
|
||||||
# unpause with remote commands.
|
# unpause with remote commands.
|
||||||
self.info_center.setPlaybackState_(MPMusicPlaybackStatePlaying)
|
self.info_center.setPlaybackState_(MPMusicPlaybackStatePlaying)
|
||||||
|
|
||||||
def update(self, song: Song | None) -> None:
|
async def update(self, song: Song | None) -> None:
|
||||||
if song:
|
if song:
|
||||||
self.info_center.setNowPlayingInfo_(song_to_media_item(song))
|
self.info_center.setNowPlayingInfo_(song_to_media_item(song))
|
||||||
self.info_center.setPlaybackState_(playback_state_to_cocoa(song.state))
|
self.info_center.setPlaybackState_(playback_state_to_cocoa(song.state))
|
||||||
|
|
|
@ -3,7 +3,7 @@ from dataclasses import dataclass
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from typing import Generic, Iterable, Literal, Protocol, TypeVar, cast
|
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 .player import Player
|
||||||
from .song import Song
|
from .song import Song
|
||||||
from .tools.types import not_none
|
from .tools.types import not_none
|
||||||
|
@ -20,10 +20,12 @@ class LoopFactory(Generic[T], Protocol):
|
||||||
|
|
||||||
|
|
||||||
class Receiver(Protocol):
|
class Receiver(Protocol):
|
||||||
def __init__(self, player: Player, config: Config) -> None: ...
|
def __init__(self, config: BaseReceiverConfig): ...
|
||||||
@classmethod
|
@classmethod
|
||||||
def loop_factory(cls) -> LoopFactory[AbstractEventLoop]: ...
|
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):
|
class ReceiverModule(Protocol):
|
||||||
|
@ -42,8 +44,8 @@ class DefaultLoopFactory(LoopFactory[AbstractEventLoop]):
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class IncompatibleReceiverError(Exception):
|
class IncompatibleReceiverError(Exception):
|
||||||
a: type[Receiver]
|
a: Receiver
|
||||||
b: type[Receiver]
|
b: Receiver
|
||||||
|
|
||||||
|
|
||||||
def import_receiver(config: BaseReceiverConfig) -> type[Receiver]:
|
def import_receiver(config: BaseReceiverConfig) -> type[Receiver]:
|
||||||
|
@ -53,13 +55,18 @@ def import_receiver(config: BaseReceiverConfig) -> type[Receiver]:
|
||||||
return mod.receiver
|
return mod.receiver
|
||||||
|
|
||||||
|
|
||||||
|
def construct_receiver(config: BaseReceiverConfig) -> Receiver:
|
||||||
|
cls = import_receiver(config)
|
||||||
|
return cls(config)
|
||||||
|
|
||||||
|
|
||||||
def choose_loop_factory(
|
def choose_loop_factory(
|
||||||
receivers: Iterable[type[Receiver]],
|
receivers: Iterable[Receiver],
|
||||||
) -> LoopFactory[AbstractEventLoop]:
|
) -> 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."""
|
"""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_fac: LoopFactory[AbstractEventLoop] = DefaultLoopFactory()
|
||||||
chosen_rec: type[Receiver] | None = None
|
chosen_rec: Receiver | None = None
|
||||||
|
|
||||||
for rec in receivers:
|
for rec in receivers:
|
||||||
fac = rec.loop_factory()
|
fac = rec.loop_factory()
|
||||||
|
|
Loading…
Reference in a new issue