Preserve Sendspin players queue across reconnects#3340
Preserve Sendspin players queue across reconnects#3340maximmaxim345 wants to merge 4 commits intodevfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Keeps Sendspin players registered when clients disconnect so their in-memory queue survives transient network drops, and adds reconnection handling to rebind an existing SendspinPlayer to a new Sendspin client instance.
Changes:
- Replace Sendspin disconnect handling from
unregister()to “mark unavailable” to preserve queues across reconnects. - Add reconnect/reattach flow in
SendspinPlayer(unsubscribe/resubscribe callbacks, refresh client info, resync group state/membership). - Add per-client event versioning in the provider to avoid add/remove race conditions on fast reconnects.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
music_assistant/providers/sendspin/provider.py |
Tracks client event versions, reattaches existing players on reconnect, and marks players unavailable instead of unregistering on disconnect; unregisters players on provider unload. |
music_assistant/providers/sendspin/player.py |
Adds callback subscribe/unsubscribe helpers, implements mark_unavailable + reattach_client, and refreshes device/client info on reconnect. |
| # Add player_id as MAC identifier for protocol linking (if it's a valid MAC) | ||
| # This enables linking with bridged players (e.g., AirPlay via Sendspin bridge) | ||
| if _mac := mac_from_bridge_client_id(player_id): | ||
| if _mac := mac_from_bridge_client_id(self.player_id): | ||
| self._attr_device_info.add_identifier(IdentifierType.MAC_ADDRESS, _mac) | ||
| elif is_valid_mac_address(player_id): | ||
| self._attr_device_info.add_identifier(IdentifierType.MAC_ADDRESS, player_id) | ||
| elif is_valid_mac_address(self.player_id): | ||
| self._attr_device_info.add_identifier(IdentifierType.MAC_ADDRESS, self.player_id) |
There was a problem hiding this comment.
_refresh_client_info re-adds preserved identifiers and then unconditionally calls add_identifier(IdentifierType.MAC_ADDRESS, ...) based on self.player_id, which can overwrite an ARP-enriched/cached MAC already present in preserved_identifiers (used for protocol-linking/universal-player matching). Prefer only setting the MAC from player_id when no MAC is already present, or only when the derived MAC is more reliable than the preserved value.
| if protocol_info.model: | ||
| device_info.model = protocol_info.model | ||
|
|
||
| # Update manufacturer if universal player has generic value | ||
| if device_info.manufacturer in (None, "Music Assistant") and protocol_info.manufacturer: | ||
| if protocol_info.manufacturer: | ||
| device_info.manufacturer = protocol_info.manufacturer |
There was a problem hiding this comment.
_update_universal_device_info now overwrites the universal player’s model/manufacturer whenever the protocol player provides any truthy value, which can regress good device info to generic placeholders (e.g. Sendspin falls back to "Unknown model" / "Unknown Manufacturer" in SendspinPlayer._refresh_client_info). Consider only updating when the universal values are still defaults ("Universal Player"/"Music Assistant") or when the incoming values are known-non-generic, to avoid incorrect metadata and unnecessary config writes.
| # Re-evaluate protocol links on protocol player updates so parent wrappers | ||
| # can refresh metadata (e.g. reconnect with updated device info). | ||
| if player.state.type == PlayerType.PROTOCOL: | ||
| self._evaluate_protocol_links(player) |
There was a problem hiding this comment.
register_or_update now triggers protocol-link evaluation on updates of PROTOCOL players; please add/adjust tests (see tests/core/test_protocol_linking.py) to cover the reconnect/update path and assert it doesn’t create duplicate universal players or mutate existing links unexpectedly.
|
There seems to be a number of unrelated commits in the PR - was that intentional ? I have these 2 comments:
|
|
Related issue: music-assistant/support#5049 |
Summary
Do not unregister offline Sendspin players to keep the queue in memory.
With this PR, Sendspin players now remain registered and are only marked as unavailable, preserving the queue.
Description
The Sendspin provider was previously calling
unregisterwhenever a player/client went offline.This had one major drawback: unregistering a player also deleted that player's queue.
While merged players created a Universal player, which solved this issue for most clients (since they have the type
PROTOCOL), Sendspin-only clients are of typePLAYER.This means that the clients most susceptible to network interruptions are also the ones whose queue gets cleared most often: the Web Player and Mobile Apps.
Alternative Solutions
Alternatively, we could keep Sendspin using
unregisterfor offline players and instead preserve queues after callingPlayerController.unregister(permanent=False).This would also fix the issue for other providers that unregister players, but it might introduce unexpected issues with dangling queues.