From: Marcel van der Veldt Date: Sun, 27 Apr 2025 00:36:54 +0000 (+0200) Subject: Fixes for Sonos AirPlay mode X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=22ad341e25b16e2703813f619184c18580620d35;p=music-assistant-server.git Fixes for Sonos AirPlay mode --- diff --git a/music_assistant/providers/airplay/provider.py b/music_assistant/providers/airplay/provider.py index eea6346a..1312ab43 100644 --- a/music_assistant/providers/airplay/provider.py +++ b/music_assistant/providers/airplay/provider.py @@ -353,10 +353,15 @@ class AirPlayProvider(PlayerProvider): output_format=AIRPLAY_PCM_FORMAT, ) - # if an existing stream session is running, replace it with the new stream + # if an existing stream session is running, we could replace it with the new stream if airplay_player.raop_stream and airplay_player.raop_stream.running: - await airplay_player.raop_stream.session.replace_stream(audio_source) - return + # check if we need to replace the stream + if airplay_player.raop_stream.prevent_playback: + # player is in prevent playback mode, we need to stop the stream + await airplay_player.cmd_stop(False) + else: + await airplay_player.raop_stream.session.replace_stream(audio_source) + return # setup RaopStreamSession for player (and its sync childs if any) sync_clients = self._get_sync_clients(player_id) diff --git a/music_assistant/providers/sonos/provider.py b/music_assistant/providers/sonos/provider.py index 2481cc6e..d25c3b9d 100644 --- a/music_assistant/providers/sonos/provider.py +++ b/music_assistant/providers/sonos/provider.py @@ -18,7 +18,7 @@ from aiosonos.utils import get_discovery_info from music_assistant_models.config_entries import ConfigEntry, PlayerConfig from music_assistant_models.enums import ConfigEntryType, MediaType, PlayerState, ProviderFeature from music_assistant_models.errors import PlayerCommandFailed -from music_assistant_models.player import DeviceInfo, PlayerMedia +from music_assistant_models.player import DeviceInfo, Player, PlayerMedia from zeroconf import ServiceStateChange from music_assistant.constants import ( @@ -299,24 +299,10 @@ class SonosPlayerProvider(PlayerProvider): raise PlayerCommandFailed(msg) # for now always reset the active session sonos_player.client.player.group.active_session_id = None - if airplay := sonos_player.get_linked_airplay_player(True): + if airplay_player := sonos_player.get_linked_airplay_player(True): # airplay mode is enabled, redirect the command self.logger.debug("Redirecting PLAY_MEDIA command to linked airplay player.") - mass_player.active_source = airplay.active_source - # Sonos has an annoying bug (for years already, and they dont seem to care), - # where it looses its sync childs when airplay playback is (re)started. - # Try to handle it here with this workaround. - group_childs = [ - x for x in sonos_player.client.player.group.player_ids if x != player_id - ] - if group_childs: - await self.mass.players.cmd_ungroup_many(group_childs) - await self.mass.players.play_media(airplay.player_id, media) - if group_childs: - # ensure master player is first in the list - group_childs = [sonos_player.player_id, *group_childs] - await asyncio.sleep(5) - await sonos_player.client.player.group.set_group_members(group_childs) + await self._play_media_airplay(sonos_player, airplay_player, media) return if media.media_type in ( @@ -324,7 +310,7 @@ class SonosPlayerProvider(PlayerProvider): MediaType.FLOW_STREAM, ) or media.queue_id.startswith("ugp_"): # flow stream or plugin source playback - # use the legacy playback method for this as it also + # always use the legacy (UPNP) playback method for this await self._play_media_legacy(sonos_player, media) return @@ -676,3 +662,35 @@ class SonosPlayerProvider(PlayerProvider): # crossfading is handled by our streams controller if sonos_player.client.player.group.play_modes.crossfade: await sonos_player.client.player.group.set_play_modes(crossfade=False) + + async def _play_media_airplay( + self, + sonos_player: SonosPlayer, + airplay_player: Player, + media: PlayerMedia, + ) -> None: + """Handle PLAY MEDIA using the legacy upnp api.""" + player_id = sonos_player.player_id + mass_player = self.mass.players.get(player_id) + mass_player.active_source = airplay_player.active_source + if ( + airplay_player.state == PlayerState.PLAYING + and airplay_player.active_source == media.queue_id + ): + # if the airplay player is already playing, + # the stream will be reused so no need to do the whole grouping thing below + await self.mass.players.play_media(airplay_player.player_id, media) + return + + # Sonos has an annoying bug (for years already, and they dont seem to care), + # where it looses its sync childs when airplay playback is (re)started. + # Try to handle it here with this workaround. + group_childs = [x for x in sonos_player.client.player.group.player_ids if x != player_id] + if group_childs: + await self.mass.players.cmd_ungroup_many(group_childs) + await self.mass.players.play_media(airplay_player.player_id, media) + if group_childs: + # ensure master player is first in the list + group_childs = [sonos_player.player_id, *group_childs] + await asyncio.sleep(5) + await sonos_player.client.player.group.set_group_members(group_childs)