music-assistant-server
16.4 KB•MD
README.md
16.4 KB • 329 lines • markdown
1# Player Controller Architecture
2
3This document provides an overview of the Music Assistant Player Controller architecture, including the Player/PlayerState model, multi-protocol player system, and universal player concept.
4
5## Table of Contents
6
7- [Overview](#overview)
8- [Player vs PlayerState](#player-vs-playerstate)
9- [Core Components](#core-components)
10- [Player Types](#player-types)
11- [Multi-Protocol Player System](#multi-protocol-player-system)
12- [Universal Player](#universal-player)
13- [Protocol Linking](#protocol-linking)
14- [Development Guide](#development-guide)
15
16## Overview
17
18The Player Controller is a core controller that manages all connected audio players from various providers. It provides:
19- Unified control interface for all players (play, pause, volume, etc.)
20- Multi-protocol player linking (combining AirPlay, Chromecast, DLNA for the same device)
21- Universal Player wrapping for devices without native vendor support
22- Sync group management for synchronized playback
23- Player state management and event broadcasting
24- User access control and permissions
25
26## Player vs PlayerState
27
28The Player Controller distinguishes between two key concepts:
29
30### Player (Internal Model)
31
32The `Player` class is the actual object provided by a Player Provider. It:
33- Incorporates the actual state of the player (volume, playback state, etc.)
34- Contains methods for controlling the player (play, pause, volume, etc.)
35- Is used internally by providers and the controller
36- May contain provider-specific implementation details
37
38### PlayerState (API Model)
39
40The `PlayerState` is a dataclass representing the final state of the player. It:
41- Includes any user customizations (custom name, hidden status, etc.)
42- Applies transformations (e.g., fake power/volume controls)
43- Is the object exposed to the outside world via the API
44- Is a snapshot created when `player.update_state()` is called
45- Contains only serializable data suitable for API consumers
46
47```
48âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
49â Player (Internal) â
50â - Provider-specific implementation â
51â - Control methods (play, pause, volume_set, etc.) â
52â - Raw state (_attr_volume_level, _attr_playback_state, etc.) â
53â - Device info and identifiers â
54âââââââââââââââââââââââââââââââââââ¬ââââââââââââââââââââââââââââââââ
55 â
56 â update_state()
57 â¼
58âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
59â PlayerState (API) â
60â - Final display name (with user customizations) â
61â - Transformed state (fake controls applied) â
62â - Player controls configuration â
63â - Serializable for API/WebSocket â
64âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
65```
66
67## Core Components
68
69### 1. PlayerController ([controller.py](controller.py))
70
71The main orchestrator that manages:
72- Player registration and lifecycle
73- Player commands (play, pause, stop, volume, etc.)
74- Protocol linking and evaluation
75- Universal player creation
76- Sync group coordination
77
78**Key responsibilities:**
79- Routes commands to appropriate players or protocol players
80- Manages player availability and state
81- Handles announcements and TTS playback
82- Coordinates sync groups and grouped playback
83
84### 2. ProtocolLinkingMixin ([protocol_linking.py](protocol_linking.py))
85
86Mixin class containing all protocol linking logic:
87- Matching protocol players to native players via device identifiers
88- Creating and managing Universal Players
89- Protocol link lifecycle (add, remove, cleanup)
90- Output protocol selection for playback
91
92### 3. Helper Utilities ([helpers.py](helpers.py))
93
94Contains standalone helper functions and decorators:
95- `handle_player_command` decorator for command validation
96- `AnnounceData` type definition
97
98## Player Types
99
100Players in Music Assistant have different types based on their capabilities:
101
102### PlayerType.PLAYER
103
104A regular player with native (vendor-specific) support. Examples:
105- Sonos speakers via the Sonos provider
106- Apple devices via the AirPlay provider (HomePod, Apple TV)
107- Google devices via the Chromecast provider (Nest Audio, Google Home)
108
109### PlayerType.PROTOCOL
110
111A generic protocol player without native vendor support. These are streaming endpoints discovered via generic protocols but manufactured by third parties. Examples:
112- Samsung TV discovered via AirPlay (not an Apple device)
113- Sony speaker discovered via Chromecast (not a Google device)
114- Any DLNA/UPnP device (always PROTOCOL type)
115
116**Important:** Protocol players with `PlayerType.PROTOCOL` are hidden from the UI and wrapped in a Universal Player or attached to an existing native player.
117
118### PlayerType.GROUP
119
120A group player that represents (synchronized) playback across multiple physical speakers.
121
122### PlayerType.STEREO_PAIR
123
124A dedicated stereo pair of two speakers acting as one player.
125
126## Multi-Protocol Player System
127
128Modern audio devices often support multiple streaming protocols (AirPlay, Chromecast, DLNA). The Player Controller automatically detects and links these protocols to provide a unified experience.
129
130### How It Works
131
132```
133âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
134â Physical Device â
135â (e.g., Samsung Soundbar) â
136âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¤
137â ââââââââââââââââ ââââââââââââââââ ââââââââââââââââ â
138â â AirPlay â â Chromecast â â DLNA â â
139â â Protocol â â Protocol â â Protocol â â
140â â Player â â Player â â Player â â
141â â (hidden) â â (hidden) â â (hidden) â â
142â ââââââââ¬ââââââââ ââââââââ¬ââââââââ ââââââââ¬ââââââââ â
143â â â â â
144â âââââââââââââââââââ¼ââââââââââââââââââ â
145â â â
146â â¼ â
147â âââââââââââââââââââââââââââ â
148â â Universal Player â â
149â â (visible in UI) â â
150â â - Aggregates protocols â â
151â â - Selects best output â â
152â â - Unified control â â
153â âââââââââââââââââââââââââââ â
154âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
155```
156
157### Device Identifier Matching
158
159Protocol players are matched to the same physical device using identifiers in order of reliability:
160
1611. **MAC_ADDRESS** - Most reliable, unique to the network interface
1622. **SERIAL_NUMBER** - Unique device serial number
1633. **UUID** - Universally unique identifier
1644. **CAST_UUID / AIRPLAY_ID** - Protocol-specific stable identifiers
1655. **IP_ADDRESS** - Last resort only when strong identifiers are unavailable or unreliable
1666. **player_id** - Fallback device key for players without usable identifiers (e.g., Sendspin)
167
168**Note:** IP matching is intentionally conservative. The controller prefers strong identifiers first, verifies MAC addresses with ARP where possible, and only falls back to IP when at least one side lacks a reliable hardware identifier or when a protocol/universal player still needs a last-resort match.
169
170**Important:** Protocol players from the **same protocol domain** (same provider.domain) will NOT be matched together, even if they share the same MAC/IP address. This is intentional to handle multiple software player instances (e.g., multiple Snapcast clients, multiple SendSpin web players) running on the same host. These are separate logical players, not multiple protocols of the same physical device.
171
172**Fallback behavior:** Protocol players that don't expose any identifiers (like Sendspin clients) will still get wrapped in a Universal Player using their player_id as the device key. This ensures all protocol players get a consistent user-facing interface.
173
174### Output Protocol Selection
175
176When playing media, the controller selects the best output protocol:
177
1781. **Grouped protocol** - If a protocol is actively grouped/synced, use it
1792. **User preference** - Honor user's configured preferred protocol
1803. **Native playback** - Use native PLAY_MEDIA if available
1814. **Best available** - Select by protocol priority (AirPlay > Chromecast > DLNA)
182
183## Universal Player
184
185The Universal Player is a virtual player that wraps one or more protocol players when no native vendor support exists.
186
187### When Created
188
189A Universal Player is created when:
1901. A device is discovered via a protocol but has no native provider
1912. The device's protocol player has `PlayerType.PROTOCOL`
1923. There is no existing native player that matches the device identifiers
193
194### Features
195
196- **Aggregates Features** - Combines capabilities from all linked protocols
197- **No PLAY_MEDIA** - Delegates playback to protocol players
198- **Unified Control** - Single point of control for volume, power, etc.
199- **Protocol Selection** - Automatically selects best protocol for playback
200
201### Lifecycle
202
203```
2041. Protocol player registered with PlayerType.PROTOCOL
2052. Controller checks for cached parent_id from previous session:
206 - If found, restores link immediately (skips evaluation)
207 - If parent not yet registered, keeps waiting without creating a universal player
2083. If no cached parent, checks for matching native player (links immediately if found)
2094. If no native player, schedules delayed evaluation:
210 - 15 seconds standard delay (allows other protocols to register)
211 - 45 seconds if previously linked to a parent that is not registered yet
2125. After delay, finds all matching protocol players by identifiers
2136. Creates or updates a UniversalPlayer and links all matching protocols
2147. Protocol players become hidden, Universal Player visible
215```
216
217## Protocol Linking
218
219### Native Player Linking
220
221When a native player (e.g., Sonos) is registered, the controller:
2221. Searches for protocol players with matching identifiers
2232. Links matching protocols to the native player
2243. Protocol players become hidden, native player gains `output_protocols`
225
226### Protocol to Universal
227
228When protocol players are registered without a native match:
2291. Each protocol player schedules a delayed evaluation
2302. After the delay, matching protocols are grouped
2313. A Universal Player is created even for a single unmatched protocol player
2324. All protocol players link to the Universal Player
233
234### Universal to Native Promotion
235
236When a native player appears for a device that has a Universal Player:
2371. Native player is registered
2382. Controller finds matching Universal Player
2393. Active and cached protocol ownership transfers to the native player
2404. Universal Player is removed
2415. Native player becomes the visible entity
242
243## Development Guide
244
245### Adding Protocol Support
246
247When implementing a new protocol provider:
248
2491. Set `_attr_type = PlayerType.PROTOCOL` for generic devices (non-vendor devices)
2502. Set `_attr_type = PlayerType.PLAYER` for devices with native support (vendor's own devices)
2513. **Populate `device_info.identifiers`** with validated identifiers:
252 ```python
253 from music_assistant.helpers.util import is_valid_mac_address
254
255 # IMPORTANT: Validate MAC addresses before adding them
256 if is_valid_mac_address(mac_address):
257 self._attr_device_info.add_identifier(IdentifierType.MAC_ADDRESS, mac_address)
258 self._attr_device_info.add_identifier(IdentifierType.IP_ADDRESS, ip_address)
259 self._attr_device_info.add_identifier(IdentifierType.UUID, uuid)
260 ```
2614. Filter out devices that should only be handled by native providers (e.g., passive satellites)
2625. The Player Controller handles linking automatically
263
264### Adding Native Provider Support
265
266When implementing a native provider (e.g., Sonos, Bluesound) that should link to protocol players:
267
2681. Set `_attr_type = PlayerType.PLAYER` (or the property 'type') for all devices
2692. **Populate device identifiers** - This is critical for protocol linking:
270 ```python
271 from music_assistant.helpers.util import is_valid_mac_address
272
273 self._attr_device_info = DeviceInfo(
274 model="Device Model",
275 manufacturer="Manufacturer Name",
276 )
277 # Add identifiers in order of preference (MAC is most reliable)
278 # IMPORTANT: Validate MAC addresses before adding them
279 if is_valid_mac_address(mac_address):
280 self._attr_device_info.add_identifier(IdentifierType.MAC_ADDRESS, mac_address)
281 self._attr_device_info.add_identifier(IdentifierType.UUID, "device-uuid-here")
282 ```
2833. The controller will automatically:
284 - Find protocol players (AirPlay, Chromecast, DLNA) with matching identifiers
285 - Link them to your native player as `output_protocols`
286 - Replace any existing Universal Player for that device
287
288**Identifier Priority:**
289- `MAC_ADDRESS` - Most reliable, unique to network interface
290- `SERIAL_NUMBER` - Unique device serial number
291- `UUID` - Universally unique identifier
292- `CAST_UUID` / `AIRPLAY_ID` - Protocol-specific stable identifiers
293- `IP_ADDRESS` - Last resort fallback when strong identifiers are not usable
294- `player_id` - Fallback device key when no identifiers are available
295
296**Important Notes:**
297- **Always validate MAC addresses** using `is_valid_mac_address()` before adding them
298 - Rejects invalid MACs like `00:00:00:00:00:00` or `ff:ff:ff:ff:ff:ff`
299 - Prevents false matches between unrelated devices
300 - The controller will attempt ARP lookup to resolve real MACs automatically
301- `IP_ADDRESS` is only used as a last resort after strong identifiers were checked first
302
303### Testing Protocol Linking
304
305Key scenarios to test:
306
3071. **Single protocol device** - Should create Universal Player
3082. **Multi-protocol device** - All protocols linked to one Universal Player
3093. **Late protocol discovery** - New protocol added to existing Universal Player
3104. **Native player appears** - Universal Player replaced by native; its user
311 settings (name, config values, DSP and queue settings) carry over
3125. **Permanent parent removal** - Protocol links reset and discovery is re-scheduled
3136. **Protocol disappears** - Handle graceful degradation
314
315### Configuration Storage
316
317Protocol links are persisted in player configuration:
318- `linked_protocol_ids` - List of protocol player IDs
319- Restored on restart for fast reconnection
320
321### Key Methods (in protocol_linking.py)
322
323- `_evaluate_protocol_links()` - Entry point for link evaluation
324- `_try_link_protocol_to_native()` - Link protocol to existing native
325- `_schedule_protocol_evaluation()` - Delay evaluation for batching
326- `_create_or_update_universal_player()` - Create/update Universal Player
327- `_check_replace_universal_player()` - Replace Universal with native
328- `_select_best_output_protocol()` - Choose protocol for playback
329