From: Anatosun <33899455+anatosun@users.noreply.github.com> Date: Mon, 5 Jan 2026 12:02:43 +0000 (+0100) Subject: Plex Connect: Ungroup player before starting playback (#2877) X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=6ca688f54afba42416a9fd89c39a5281c12fc15a;p=music-assistant-server.git Plex Connect: Ungroup player before starting playback (#2877) * Plex Connect: Ungroup player before starting playback When a user selects a specific player for playback in Plex, automatically remove it from any sync groups or permanent groups. This improves UX by ensuring playback only happens on the selected player, not all grouped players. - Added _ungroup_player_if_needed() helper method - Calls set_members directly to bypass static member restrictions - Applied to handle_play_media (new playback) and handle_play (resume) - Works with temporary syncs, permanent groups, and dynamic groups * Plex Connect: Add error handling and feature checks to player ungrouping * Plex Connect: implemented corrections --- diff --git a/music_assistant/providers/plex_connect/player_remote.py b/music_assistant/providers/plex_connect/player_remote.py index 9d8269f4..a6e2d509 100644 --- a/music_assistant/providers/plex_connect/player_remote.py +++ b/music_assistant/providers/plex_connect/player_remote.py @@ -13,7 +13,13 @@ from typing import TYPE_CHECKING, Any from urllib.parse import urlparse from aiohttp import ClientTimeout, web -from music_assistant_models.enums import EventType, PlayerType, QueueOption, RepeatMode +from music_assistant_models.enums import ( + EventType, + PlayerFeature, + PlayerType, + QueueOption, + RepeatMode, +) from plexapi.playqueue import PlayQueue from .gdm import PlexGDMAdvertiser @@ -343,6 +349,32 @@ class PlexRemoteControlServer: }, ) + async def _ungroup_player_if_needed(self, player_id: str) -> None: + """Ungroup player before playback if it's part of a group/sync.""" + player = self.provider.mass.players.get(player_id) + if not player or player.type == PlayerType.GROUP: + return + + if not (player.synced_to or player.group_members or player.active_group): + return + + LOGGER.debug("Ungrouping player %s before starting playback from Plex", player.display_name) + # Use set_members directly on the group to bypass static member check + if ( + player.active_group + and (group := self.provider.mass.players.get(player.active_group)) + and group.supports_feature(PlayerFeature.SET_MEMBERS) + ): + await group.set_members(player_ids_to_remove=[player_id]) + elif ( + player.synced_to + and (sync_leader := self.provider.mass.players.get(player.synced_to)) + and sync_leader.supports_feature(PlayerFeature.SET_MEMBERS) + ): + await sync_leader.set_members(player_ids_to_remove=[player_id]) + elif player.group_members and player.supports_feature(PlayerFeature.SET_MEMBERS): + await player.set_members(player_ids_to_remove=player.group_members) + async def handle_play_media(self, request: web.Request) -> web.Response: """ Handle playMedia command from Plex controller. @@ -377,6 +409,10 @@ class PlexRemoteControlServer: if not player_id: return web.Response(status=500, text="No player assigned to this server") + # Ungroup player if it's part of a group/sync + # User selected this specific player, so remove from any groups + await self._ungroup_player_if_needed(player_id) + if container_key and "/playQueues/" in container_key: # Extract play queue ID from container key queue_id_match = re.search(r"/playQueues/(\d+)", container_key) @@ -964,6 +1000,8 @@ class PlexRemoteControlServer: self._updating_from_plex = True try: if self._ma_player_id: + # Ungroup player before resuming playback + await self._ungroup_player_if_needed(self._ma_player_id) await self.provider.mass.players.cmd_play(self._ma_player_id) await self._broadcast_timeline() return web.Response(status=200)