Better handling of unavailable players
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Thu, 24 Oct 2024 13:55:14 +0000 (15:55 +0200)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Thu, 24 Oct 2024 13:55:14 +0000 (15:55 +0200)
music_assistant/server/controllers/players.py
music_assistant/server/providers/player_group/__init__.py

index 0d7e94c1e435d1e0c0e7d66e16b6afe5a08ba14f..1e783174a2fe8bfe3f2b13ac014a18f7ccf08b71 100644 (file)
@@ -859,6 +859,10 @@ class PlayerController(CoreController):
         if skip_forward and not force_update:
             return
 
+        # handle player becoming unavailable
+        if "available" in changed_values and not player.available:
+            self._handle_player_unavailable(player)
+
         # update/signal group player(s) child's when group updates
         for child_player in self.iter_group_members(player, exclude_self=True):
             self.update(child_player.player_id, skip_forward=True)
@@ -1072,6 +1076,27 @@ class PlayerController(CoreController):
             group_volume = group_volume / active_players
         return int(group_volume)
 
+    def _handle_player_unavailable(self, player: Player) -> None:
+        """Handle a player becoming unavailable."""
+        if player.synced_to:
+            self.mass.create_task(self.cmd_unsync(player.player_id))
+            # also set this optimistically because the above command will most likely fail
+            player.synced_to = None
+            return
+        for group_child_id in player.group_childs:
+            if group_child_id == player.player_id:
+                continue
+            if child_player := self.get(group_child_id):
+                self.mass.create_task(self.cmd_power(group_child_id, False, True))
+                # also set this optimistically because the above command will most likely fail
+                child_player.synced_to = None
+            player.group_childs = set()
+        if player.active_group and (group_player := self.get(player.active_group)):
+            # remove player from group if its part of a group
+            group_player = self.get(player.active_group)
+            if player.player_id in group_player.group_childs:
+                group_player.group_childs.remove(player.player_id)
+
     async def _play_announcement(
         self,
         player: Player,
index 4fa9cef0b5caea58f7ee794290530559ee353a1d..064767ade4f70083861d3442ba68895161bee9df 100644 (file)
@@ -511,6 +511,13 @@ class PlayerGroupProvider(PlayerProvider):
         if 'needs_poll' is set to True in the player object.
         """
         if group_player := self.mass.players.get(player_id):
+            # dynamically change the poll interval
+            if group_player.state == PlayerState.PLAYING:
+                group_player.poll_interval = 5
+            elif group_player.powered:
+                group_player.poll_interval = 20
+            else:
+                group_player.poll_interval = 60
             self._update_attributes(group_player)
 
     async def create_group(self, group_type: str, name: str, members: list[str]) -> Player:
@@ -682,6 +689,8 @@ class PlayerGroupProvider(PlayerProvider):
             supported_features=tuple(player_features),
             group_childs=set(members),
             active_source=group_player_id,
+            needs_poll=True,
+            poll_interval=5,
         )
 
         await self.mass.players.register_or_update(player)