Fixes for the Active source being wrong in some cases (#1099)
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Tue, 20 Feb 2024 00:33:43 +0000 (01:33 +0100)
committerGitHub <noreply@github.com>
Tue, 20 Feb 2024 00:33:43 +0000 (01:33 +0100)
music_assistant/server/controllers/players.py
music_assistant/server/providers/airplay/__init__.py
music_assistant/server/providers/chromecast/__init__.py
music_assistant/server/providers/chromecast/helpers.py
music_assistant/server/providers/dlna/__init__.py
music_assistant/server/providers/sonos/player.py
music_assistant/server/providers/spotify/__init__.py

index 9608742f253894f82907d7f036253ecd144c3db3..f6f580d6c4faf65819e9be80b311bba9474d0b9c 100644 (file)
@@ -238,7 +238,7 @@ class PlayerController(CoreController):
         if player_id not in self._players:
             return
         player = self._players[player_id]
-        # calculate active_source
+        # calculate active_source (if needed)
         player.active_source = self._get_active_source(player)
         # calculate group volume
         player.group_volume = self._get_group_volume_level(player)
@@ -534,6 +534,7 @@ class PlayerController(CoreController):
                 elif member.active_source == group_player.player_id:
                     # turn off child player when group turns off
                     tg.create_task(self.cmd_power(member.player_id, False))
+                    member.active_source = None
             # edge case: group turned on but no members are powered, power them all!
             if not members_powered and power:
                 for member in self.iter_group_members(group_player, only_powered=False):
@@ -789,9 +790,14 @@ class PlayerController(CoreController):
         """Return the active_source id for given player."""
         # if player is synced, return group leader's active source
         if player.synced_to and (parent_player := self.get(player.synced_to)):
-            return parent_player.player_id
-        if active_player_group := self._get_active_player_group(player):
-            return active_player_group.player_id
+            return parent_player.active_source
+        # fallback to the first active group player
+        if player.powered:
+            for group_player in self._get_player_groups(
+                player, available_only=True, powered_only=True
+            ):
+                if group_player.state in (PlayerState.PLAYING, PlayerState.PAUSED):
+                    return group_player.active_source
         # defaults to the player's own player id if not active source set
         return player.active_source or player.player_id
 
index 1e652c51ebb3a167723607fdce3740c0278ae1ad..23dedfc0c5b65ba41ff0efb982714a01eb70bd9c 100644 (file)
@@ -177,7 +177,6 @@ class AirPlayPlayer(DeviceListener):
         self.connected = False
         self._connection_attempts = 0
         self._connection_was_lost = False
-        self._task = None
         self._playing: interface.Playing | None = None
         self.logger = self.mass.players.logger.getChild("airplay").getChild(self.player_id)
         self.cliraop_proc: AsyncProcess | None = None
@@ -229,9 +228,6 @@ class AirPlayPlayer(DeviceListener):
             if self.atv:
                 self.atv.close()
                 self.atv = None
-            if self._task:
-                self._task.cancel()
-                self._task = None
         except Exception:  # pylint: disable=broad-except
             self.logger.exception("An error occurred while disconnecting")
 
@@ -416,11 +412,6 @@ class AirPlayPlayer(DeviceListener):
             mass_player.state = PlayerState.IDLE
         self.mass.players.update(self.player_id)
 
-    @property
-    def is_connecting(self):
-        """Return true if connection is in progress."""
-        return self._task is not None
-
     def address_updated(self, address):
         """Update cached address in config entry."""
         self.logger.debug("Changing address to %s", address)
@@ -974,7 +965,7 @@ class AirplayProvider(PlayerProvider):
         ):
             extra_args += ["-u"]
         if self.mass.config.get_raw_player_config_value(
-            atv_player.player_id, CONF_ALAC_ENCODE, False
+            atv_player.player_id, CONF_ALAC_ENCODE, True
         ):
             extra_args += ["-a"]
         if self.mass.config.get_raw_player_config_value(
index e4cdbc557d36acce1b7dc694e50f56a0dcf7599f..9cfebcd77efbe08ce1aea1a03a42125eb8667ee7 100644 (file)
@@ -527,8 +527,10 @@ class ChromecastProvider(PlayerProvider):
 
         # active source
         if (
-            status.content_id and castplayer.player_id in status.content_id
-        ) or castplayer.cc.app_id == pychromecast.config.APP_MEDIA_RECEIVER:
+            status.content_id
+            and self.mass.streams.base_url in status.content_id
+            and castplayer.player_id in status.content_id
+        ):
             castplayer.player.active_source = castplayer.player_id
         else:
             castplayer.player.active_source = castplayer.cc.app_display_name
index 114a338380671952bc859426531ac006923aed5e..d49a38a635fa2d01124b07370b58c8bc761d2599 100644 (file)
@@ -177,7 +177,7 @@ class CastStatusListener:
         if not self._valid:
             return
         if group_uuid == self.castplayer.player.active_source:
-            self.castplayer.player.active_source = ""
+            self.castplayer.player.active_source = None
         self.prov.logger.debug(
             "%s is removed from multizone: %s", self.castplayer.player.display_name, group_uuid
         )
index 83924d9b26ba0d3178849c01cdf5cb6f122b9da0..d13ed181076f151f60e0d925f2d7e9cdfe35cc9c 100644 (file)
@@ -251,7 +251,7 @@ class DLNAPlayer:
         return PlayerState.IDLE
 
     @staticmethod
-    def get_supported_features(device: DmrDevice) -> set(PlayerFeature):
+    def get_supported_features(device: DmrDevice) -> set[PlayerFeature]:
         """Get player features that are supported at this moment.
 
         Supported features may change as the device enters different states.
index bba358c925c5e3ac9eb9dcd4c1c23ae3903a38ce..ec542e250dd83258dcdea26f6aac2b4017695978 100644 (file)
@@ -710,7 +710,7 @@ class SonosPlayer:
 
         # media info (track info)
         self.mass_player.current_item_id = self.uri
-        if self.uri and self.player_id in self.uri:
+        if self.uri and self.mass.streams.base_url in self.uri and self.player_id in self.uri:
             self.mass_player.active_source = self.player_id
         else:
             self.mass_player.active_source = self.source_name
index 593c2948d5301051ed6f4f0e50e61b9330b124ec..5e5cd8aca22805fe5ef4be563813ef01a1fb8c31 100644 (file)
@@ -122,7 +122,7 @@ class SpotifyProvider(MusicProvider):
 
     async def handle_async_init(self) -> None:
         """Handle async initialization of the provider."""
-        self._throttler = Throttler(rate_limit=1, period=0.1)
+        self._throttler = Throttler(rate_limit=1, period=1)
         self._cache_dir = CACHE_DIR
         self._ap_workaround = False
         # try to get a token, raise if that fails
@@ -742,7 +742,7 @@ class SpotifyProvider(MusicProvider):
                 async with self.mass.http_session.get(
                     url, headers=headers, params=kwargs, ssl=False, timeout=120
                 ) as response:
-                    # get text before json so we can log the body in case of errorrs
+                    # get text before json so we can log the body in case of errors
                     result = await response.text()
                     result = json_loads(result)
                     if "error" in result or ("status" in result and "error" in result["status"]):