more fixes for playback and enqueuing
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Thu, 13 Jun 2024 22:14:34 +0000 (00:14 +0200)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Thu, 13 Jun 2024 22:14:34 +0000 (00:14 +0200)
music_assistant/common/models/player_queue.py
music_assistant/server/controllers/player_queues.py
music_assistant/server/providers/hass_players/__init__.py

index 45176099e01cdaa8587ce1d7298a5a35969a24c3..d205ce431a60f00fc5c88239f0d30c782bc603bd 100644 (file)
@@ -40,6 +40,7 @@ class PlayerQueue(DataClassDictMixin):
     # flow_mode_start_index: index of the first item of the flow stream
     flow_mode_start_index: int = 0
     stream_finished: bool | None = None
+    end_of_track_reached: bool | None = None
 
     @property
     def corrected_elapsed_time(self) -> float:
index 76e953f0e06ab7a5a724af7e109614a0242ffea2..096272657c550863860413dd8c72210690184972 100644 (file)
@@ -521,6 +521,7 @@ class PlayerQueuesController(CoreController):
         queue = self._queues[queue_id]
         queue.radio_source = []
         queue.stream_finished = None
+        queue.end_of_track_reached = None
         if queue.state != PlayerState.IDLE:
             self.mass.create_task(self.stop(queue_id))
         queue.current_index = None
@@ -538,6 +539,7 @@ class PlayerQueuesController(CoreController):
         """
         if queue := self.get(queue_id):
             queue.stream_finished = None
+            queue.end_of_track_reached = None
         # forward the actual stop command to the player provider
         if player_provider := self.mass.players.get_player_provider(queue_id):
             await player_provider.cmd_stop(queue_id)
@@ -719,6 +721,7 @@ class PlayerQueuesController(CoreController):
         next_index = self._get_next_index(queue_id, index, allow_repeat=False)
         queue.flow_mode = player_needs_flow_mode and next_index is not None
         queue.stream_finished = False
+        queue.end_of_track_reached = False
         # get streamdetails - do this here to catch unavailable items early
         queue_item.streamdetails = await get_stream_details(
             self.mass, queue_item, seek_position=seek_position, fade_in=fade_in
@@ -848,6 +851,16 @@ class PlayerQueuesController(CoreController):
         # return early if nothing changed
         if len(changed_keys) == 0:
             return
+        # check if we've reached the end of (the current) track
+        if (
+            queue.current_item
+            and (duration := queue.current_item.duration)
+            and (duration - queue.elapsed_time) < 10
+        ):
+            queue.end_of_track_reached = True
+        elif prev_state["current_index"] != new_state["current_index"]:
+            queue.end_of_track_reached = False
+
         # handle enqueuing of next item to play
         if not queue.flow_mode or queue.stream_finished:
             self._check_enqueue_next(player, queue, prev_state, new_state)
@@ -1164,7 +1177,10 @@ class PlayerQueuesController(CoreController):
         # we wait for the player to stop after it reaches the end of the track
         if (
             (not queue.flow_mode or queue.repeat_mode == RepeatMode.ALL)
+            # we have a couple of guards here to prevent the player starting
+            # playback again when its stopped outside of MA's control
             and queue.stream_finished
+            and queue.end_of_track_reached
             and queue.state == PlayerState.IDLE
         ):
             queue.stream_finished = None
index 1fd7275d89e42ac27f4908f9ce207db8cfc3eece..40fd0f4b1d91b6b169bcb5cffda8bc82923f6c6e 100644 (file)
@@ -34,9 +34,10 @@ from music_assistant.server.providers.hass import DOMAIN as HASS_DOMAIN
 if TYPE_CHECKING:
     from collections.abc import AsyncGenerator
 
-    from hass_client.models import CompressedState, EntityStateEvent
+    from hass_client.models import CompressedState
     from hass_client.models import Device as HassDevice
     from hass_client.models import Entity as HassEntity
+    from hass_client.models import EntityStateEvent
     from hass_client.models import State as HassState
 
     from music_assistant.common.models.config_entries import ProviderConfig
@@ -88,6 +89,10 @@ class MediaPlayerEntityFeature(IntFlag):
 
 CONF_ENFORCE_MP3 = "enforce_mp3"
 
+CONF_ENTRY_ENFORCE_MP3_DEFAULT_ENABLED = ConfigEntry.from_dict(
+    {**CONF_ENTRY_ENFORCE_MP3.to_dict(), "default_value": True}
+)
+
 PLAYER_CONFIG_ENTRIES = (
     CONF_ENTRY_CROSSFADE_FLOW_MODE_REQUIRED,
     CONF_ENTRY_CROSSFADE_DURATION,
@@ -233,6 +238,18 @@ class HomeAssistantPlayers(PlayerProvider):
                 "media_content_id": media.uri,
                 "media_content_type": "music",
                 "enqueue": "replace",
+                "extra": {
+                    "metadata": {
+                        "title": media.title,
+                        "artist": media.artist,
+                        "metadataType": 3,
+                        "album": media.album,
+                        "albumName": media.album,
+                        "duration": media.duration,
+                        "images": [{"url": media.image_url}] if media.image_url else None,
+                        "imageUrl": media.image_url,
+                    }
+                },
             },
             target={"entity_id": player_id},
         )
@@ -338,6 +355,7 @@ class HomeAssistantPlayers(PlayerProvider):
                 entity_id
                 for entity_id, entity in entity_registry.items()
                 if entity["platform"] == entity_registry_entry["platform"]
+                and state["entity_id"].startswith("media_player")
                 and entity_id != state["entity_id"]
             ]
             hass_device = device_registry.get(entity_registry_entry["device_id"])