/
/
/
1"""
2Universal Player implementation.
3
4A virtual player for devices that have no native (vendor-specific) provider in
5Music Assistant but support one or more generic streaming protocols such as
6AirPlay, Sendspin, Chromecast, or DLNA.
7
8The Universal Player is automatically created when a protocol player with
9PlayerType.PROTOCOL is registered, providing a unified interface while delegating
10actual playback to the underlying protocol player(s).
11"""
12
13from __future__ import annotations
14
15from typing import TYPE_CHECKING
16
17from music_assistant.models.protocol_backed_player import ProtocolBackedPlayer
18
19if TYPE_CHECKING:
20 from music_assistant.models.player import DeviceInfo
21
22 from .provider import UniversalPlayerProvider
23
24
25class UniversalPlayer(ProtocolBackedPlayer):
26 """
27 Universal Player implementation.
28
29 A virtual player for devices without native Music Assistant support that use
30 generic streaming protocols. It does NOT have PLAY_MEDIA capability on its own.
31 Playback is always delegated to one of the linked protocol players via the protocol
32 linking system.
33 """
34
35 def __init__(
36 self,
37 provider: UniversalPlayerProvider,
38 player_id: str,
39 name: str,
40 device_info: DeviceInfo,
41 protocol_player_ids: list[str],
42 ) -> None:
43 """
44 Initialize UniversalPlayer instance.
45
46 :param provider: The UniversalPlayerProvider instance.
47 :param player_id: Unique player ID (typically based on MAC address).
48 :param name: Display name for the player.
49 :param device_info: Device information aggregated from protocol players.
50 :param protocol_player_ids: List of protocol player IDs to link.
51 """
52 self._protocol_player_ids = protocol_player_ids
53 super().__init__(provider, player_id)
54 # Set player attributes
55 self._attr_name = name
56 self._attr_device_info = device_info
57 # a universal player does not have any features on its own,
58 # it delegates to protocol players
59 self._attr_supported_features = set()
60
61 def add_protocol_player(self, protocol_player_id: str) -> None:
62 """Add a protocol player to this universal player."""
63 if protocol_player_id not in self._protocol_player_ids:
64 self._protocol_player_ids.append(protocol_player_id)
65
66 def remove_protocol_player(self, protocol_player_id: str) -> None:
67 """Remove a protocol player from this universal player."""
68 if protocol_player_id in self._protocol_player_ids:
69 self._protocol_player_ids.remove(protocol_player_id)
70
71 def _backing_protocol_player_ids(self) -> list[str]:
72 """Return the ids of the protocol players backing this player."""
73 return self._protocol_player_ids
74