From 687461d8e1427ef4b8eea864ef75ee37f1ff9ba2 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Wed, 8 May 2024 02:23:14 +0200 Subject: [PATCH] fix polling --- music_assistant/common/models/player.py | 7 +++++++ music_assistant/server/controllers/config.py | 9 +++++++-- music_assistant/server/controllers/players.py | 13 +++++------- .../server/models/player_provider.py | 11 +--------- .../server/providers/chromecast/__init__.py | 20 +++++-------------- .../server/providers/dlna/__init__.py | 17 +++------------- .../server/providers/fully_kiosk/__init__.py | 17 +++------------- .../server/providers/sonos/__init__.py | 17 +++------------- 8 files changed, 34 insertions(+), 77 deletions(-) diff --git a/music_assistant/common/models/player.py b/music_assistant/common/models/player.py index bdc786a8..d0c37de8 100644 --- a/music_assistant/common/models/player.py +++ b/music_assistant/common/models/player.py @@ -94,6 +94,13 @@ class Player(DataClassDictMixin): # can be used by a player provider to exclude some sort of players enabled_by_default: bool = True + # needs_poll: bool that can be set by the player(provider) + # if this player needs to be polled for state changes by the player manager + needs_poll: bool = False + + # poll_interval: a (dynamic) interval in seconds to poll the player (used with needs_poll) + poll_interval: int = 30 + # # THE BELOW ATTRIBUTES ARE MANAGED BY CONFIG AND THE PLAYER MANAGER # diff --git a/music_assistant/server/controllers/config.py b/music_assistant/server/controllers/config.py index 783365c0..42b2cf68 100644 --- a/music_assistant/server/controllers/config.py +++ b/music_assistant/server/controllers/config.py @@ -326,8 +326,11 @@ class ConfigController: if include_values else PlayerConfig.parse([], raw_conf) for raw_conf in list(self.get(CONF_PLAYERS, {}).values()) - # filter out unavailable providers - if raw_conf["provider"] in get_global_cache_value("available_providers", []) + # filter out unavailable providers (only if we requested the full info) + if ( + not include_values + or raw_conf["provider"] in get_global_cache_value("available_providers", []) + ) # optional provider filter and (provider in (None, raw_conf["provider"])) ] @@ -430,6 +433,8 @@ class ConfigController: if provider := self.mass.get_provider(existing["provider"]): assert isinstance(provider, PlayerProvider) provider.on_player_config_removed(player_id) + if not player: + self.mass.signal_event(EventType.PLAYER_REMOVED, player_id) def create_default_player_config( self, diff --git a/music_assistant/server/controllers/players.py b/music_assistant/server/controllers/players.py index 8c05316e..4020be7f 100644 --- a/music_assistant/server/controllers/players.py +++ b/music_assistant/server/controllers/players.py @@ -996,14 +996,11 @@ class PlayerController(CoreController): if player_playing: self.mass.loop.call_soon(self.update, player_id) # Poll player; - # - every 120 seconds if the player if not powered - # - every 30 seconds if the player is powered - # - every 5 seconds if the player is playing - if ( - (player.powered and count % 30 == 0) - or (player_playing and count % 5 == 0) - or count % 120 == 0 - ) and (player_prov := self.get_player_provider(player_id)): + if not player.needs_poll: + continue + if count % player.poll_interval == 0 and ( + player_prov := self.get_player_provider(player_id) + ): try: await player_prov.poll_player(player_id) except PlayerUnavailableError: diff --git a/music_assistant/server/models/player_provider.py b/music_assistant/server/models/player_provider.py index bc902c0f..3d2fec59 100644 --- a/music_assistant/server/models/player_provider.py +++ b/music_assistant/server/models/player_provider.py @@ -279,16 +279,7 @@ class PlayerProvider(Provider): """Poll player for state updates. This is called by the Player Manager; - - every 360 seconds if the player if not powered - - every 30 seconds if the player is powered - - every 10 seconds if the player is playing - - Use this method to request any info that is not automatically updated and/or - to detect if the player is still alive. - If this method raises the PlayerUnavailable exception, - the player is marked as unavailable until - the next successful poll or event where it becomes available again. - If the player does not need any polling, simply do not override this method. + if 'needs_poll' is set to True in the player object. """ def on_child_power(self, player_id: str, child_player_id: str, new_power: bool) -> None: diff --git a/music_assistant/server/providers/chromecast/__init__.py b/music_assistant/server/providers/chromecast/__init__.py index aefd4232..1a8b5503 100644 --- a/music_assistant/server/providers/chromecast/__init__.py +++ b/music_assistant/server/providers/chromecast/__init__.py @@ -278,20 +278,7 @@ class ChromecastProvider(PlayerProvider): ) async def poll_player(self, player_id: str) -> None: - """Poll player for state updates. - - This is called by the Player Manager; - - every 360 seconds if the player if not powered - - every 30 seconds if the player is powered - - every 10 seconds if the player is playing - - Use this method to request any info that is not automatically updated and/or - to detect if the player is still alive. - If this method raises the PlayerUnavailable exception, - the player is marked as unavailable until - the next successful poll or event where it becomes available again. - If the player does not need any polling, simply do not override this method. - """ + """Poll player for state updates.""" castplayer = self.castplayers[player_id] # only update status of media controller if player is on if not castplayer.player.powered: @@ -300,7 +287,7 @@ class ChromecastProvider(PlayerProvider): return try: now = time.time() - if (now - castplayer.last_poll) >= 30: + if (now - castplayer.last_poll) >= 60: castplayer.last_poll = now await asyncio.to_thread(castplayer.cc.media_controller.update_status) await self.update_flow_metadata(castplayer) @@ -389,6 +376,7 @@ class ChromecastProvider(PlayerProvider): PlayerFeature.PAUSE, ), enabled_by_default=enabled_by_default, + needs_poll=True, ), ) self.castplayers[player_id] = castplayer @@ -587,6 +575,7 @@ class ChromecastProvider(PlayerProvider): async def update_flow_metadata(self, castplayer: CastPlayer) -> None: """Update the metadata of a cast player running the flow stream.""" if not castplayer.player.powered: + castplayer.player.poll_interval = 300 return if not castplayer.cc.media_controller.status.player_is_playing: return @@ -600,6 +589,7 @@ class ChromecastProvider(PlayerProvider): return if not (queue.flow_mode or current_item.media_type == MediaType.RADIO): return + castplayer.player.poll_interval = 10 media_controller = castplayer.cc.media_controller # update metadata of current item chromecast if media_controller.status.media_custom_data["queue_item_id"] != current_item.queue_item_id: diff --git a/music_assistant/server/providers/dlna/__init__.py b/music_assistant/server/providers/dlna/__init__.py index 46228bbd..117bb7c6 100644 --- a/music_assistant/server/providers/dlna/__init__.py +++ b/music_assistant/server/providers/dlna/__init__.py @@ -392,20 +392,7 @@ class DLNAPlayerProvider(PlayerProvider): await dlna_player.device.async_mute_volume(muted) async def poll_player(self, player_id: str) -> None: - """Poll player for state updates. - - This is called by the Player Manager; - - every 360 seconds if the player if not powered - - every 30 seconds if the player is powered - - every 10 seconds if the player is playing - - Use this method to request any info that is not automatically updated and/or - to detect if the player is still alive. - If this method raises the PlayerUnavailable exception, - the player is marked as unavailable until - the next successful poll or event where it becomes available again. - If the player does not need any polling, simply do not override this method. - """ + """Poll player for state updates.""" dlna_player = self.dlnaplayers[player_id] # try to reconnect the device if the connection was lost @@ -535,6 +522,8 @@ class DLNAPlayerProvider(PlayerProvider): address=description_url, manufacturer="unknown", ), + needs_poll=True, + poll_interval=30, ), description_url=description_url, ) diff --git a/music_assistant/server/providers/fully_kiosk/__init__.py b/music_assistant/server/providers/fully_kiosk/__init__.py index 62e1ff56..3f6605cd 100644 --- a/music_assistant/server/providers/fully_kiosk/__init__.py +++ b/music_assistant/server/providers/fully_kiosk/__init__.py @@ -132,6 +132,8 @@ class FullyKioskProvider(PlayerProvider): address=address, ), supported_features=(PlayerFeature.VOLUME_SET,), + needs_poll=True, + poll_interval=10, ) self.mass.players.register_or_update(player) self._handle_player_update() @@ -193,20 +195,7 @@ class FullyKioskProvider(PlayerProvider): self.mass.players.update(player_id) async def poll_player(self, player_id: str) -> None: - """Poll player for state updates. - - This is called by the Player Manager; - - every 360 seconds if the player if not powered - - every 30 seconds if the player is powered - - every 10 seconds if the player is playing - - Use this method to request any info that is not automatically updated and/or - to detect if the player is still alive. - If this method raises the PlayerUnavailable exception, - the player is marked as unavailable until - the next successful poll or event where it becomes available again. - If the player does not need any polling, simply do not override this method. - """ + """Poll player for state updates.""" try: async with asyncio.timeout(15): await self._fully.getDeviceInfo() diff --git a/music_assistant/server/providers/sonos/__init__.py b/music_assistant/server/providers/sonos/__init__.py index e1dfaad0..342386f9 100644 --- a/music_assistant/server/providers/sonos/__init__.py +++ b/music_assistant/server/providers/sonos/__init__.py @@ -424,20 +424,7 @@ class SonosPlayerProvider(PlayerProvider): return async def poll_player(self, player_id: str) -> None: - """Poll player for state updates. - - This is called by the Player Manager; - - every 360 seconds if the player if not powered - - every 30 seconds if the player is powered - - every 10 seconds if the player is playing - - Use this method to request any info that is not automatically updated and/or - to detect if the player is still alive. - If this method raises the PlayerUnavailable exception, - the player is marked as unavailable until - the next successful poll or event where it becomes available again. - If the player does not need any polling, simply do not override this method. - """ + """Poll player for state updates.""" if player_id not in self.sonosplayers: return sonos_player = self.sonosplayers[player_id] @@ -524,6 +511,8 @@ class SonosPlayerProvider(PlayerProvider): address=soco.ip_address, manufacturer="SONOS", ), + needs_poll=True, + poll_interval=120, ) self.sonosplayers[player_id] = sonos_player = SonosPlayer( self, -- 2.34.1