From 449282c199d164c0ccebd7f424f969174c772910 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Thu, 9 Jan 2025 12:28:30 +0100 Subject: [PATCH] Simplify enqueue next (again) --- music_assistant/controllers/player_queues.py | 71 ++++++-------------- music_assistant/controllers/streams.py | 19 ++---- 2 files changed, 27 insertions(+), 63 deletions(-) diff --git a/music_assistant/controllers/player_queues.py b/music_assistant/controllers/player_queues.py index f47764a6..0882c923 100644 --- a/music_assistant/controllers/player_queues.py +++ b/music_assistant/controllers/player_queues.py @@ -771,7 +771,6 @@ class PlayerQueuesController(CoreController): queue.flow_mode_stream_log = [] queue.flow_mode = await self.mass.config.get_player_config_value(queue_id, CONF_FLOW_MODE) queue.current_item = queue_item - queue.next_track_enqueued = None # handle resume point of audiobook(chapter) or podcast(episode) if not seek_position and ( @@ -966,14 +965,6 @@ class PlayerQueuesController(CoreController): ), ) - # enqueue/preload next track if needed - next_item_id = queue.next_item.queue_item_id if queue.next_item else None - prev_next_item_id = prev_state["next_item_id"] if prev_state else None - if queue.state == PlayerState.PLAYING and ( - next_item_id != prev_next_item_id or queue.next_track_enqueued is None - ): - self._preload_next_item(queue) - # basic throttle: do not send state changed events if queue did not actually change new_state = CompareState( queue_id=queue_id, @@ -1009,15 +1000,13 @@ class PlayerQueuesController(CoreController): self._prev_states.pop(queue_id, None) # detect change in current index to report that a item has been played + prev_item_id = prev_state["current_item_id"] player_stopped = ( - prev_state["state"] == PlayerState.PLAYING - and new_state["state"] == PlayerState.IDLE - and queue.current_item is not None + prev_state["state"] == PlayerState.PLAYING and new_state["state"] == PlayerState.IDLE ) end_of_queue_reached = ( player_stopped and queue.current_item is not None and queue.next_item is None ) - prev_item_id = prev_state["current_item_id"] if ( prev_item_id is not None and (prev_item_id != new_state["current_item_id"] or player_stopped) @@ -1214,8 +1203,13 @@ class PlayerQueuesController(CoreController): # store the index of the item that is currently (being) loaded in the buffer # which helps us a bit to determine how far the player has buffered ahead queue.index_in_buffer = self.index_by_id(queue_id, item_id) - queue.next_track_enqueued = None + self.logger.debug("PlayerQueue %s loaded item %s in buffer", queue.display_name, item_id) self.signal_update(queue_id) + if queue.flow_mode: + return + # enqueue next track on the player if we're not in flow mode + task_id = f"enqueue_next_item_{queue_id}" + self.mass.call_later(5, self._enqueue_next_item, queue_id, item_id, task_id=task_id) # Main queue manipulation methods @@ -1256,7 +1250,6 @@ class PlayerQueuesController(CoreController): self._queue_items[queue_id] = queue_items self._queues[queue_id].items = len(self._queue_items[queue_id]) self.signal_update(queue_id, True) - self._queues[queue_id].next_track_enqueued = None # Helper methods @@ -1543,42 +1536,18 @@ class PlayerQueuesController(CoreController): insert_at_index=len(self._queue_items[queue_id]) + 1, ) - def _preload_next_item(self, queue: PlayerQueue) -> None: - """Preload the next item in the queue (if needed).""" - current_item = queue.current_item - if current_item is None or queue.next_item is None: - return - if queue.next_track_enqueued == queue.next_item.queue_item_id: - return - # ensure we're at least 2 seconds in the current track - if queue.corrected_elapsed_time < 2: - return - # preload happens when we're (at least) halfway the current track - if current_item.streamdetails and current_item.streamdetails.duration: - track_time = queue.current_item.streamdetails.duration - else: - track_time = current_item.duration or 10 - if not (queue.corrected_elapsed_time - track_time) < (track_time / 2): - return - - async def _enqueue_next(): - next_item = await self.load_next_item(queue.queue_id, current_item.queue_item_id) - # abort if we already enqueued the (selected) next track - if queue.next_track_enqueued == next_item.queue_item_id: - return - if not queue.flow_mode: - await self.mass.players.enqueue_next_media( - player_id=queue.queue_id, - media=self.player_media_from_queue_item(next_item, False), - ) - queue.next_track_enqueued = next_item.queue_item_id - self.logger.debug( - "Preloaded next track %s on queue %s", - next_item.name, - queue.display_name, - ) - - self.mass.create_task(_enqueue_next()) + async def _enqueue_next_item(self, queue_id: str, current_item_id: str) -> None: + """Enqueue the next item on the player.""" + next_item = await self.load_next_item(queue_id, current_item_id) + await self.mass.players.enqueue_next_media( + player_id=queue_id, + media=self.player_media_from_queue_item(next_item, False), + ) + self.logger.debug( + "Enqueued next track %s on queue %s", + next_item.name, + self._queues[queue_id].display_name, + ) async def _resolve_media_items( self, media_item: MediaItemType, start_item: str | None = None diff --git a/music_assistant/controllers/streams.py b/music_assistant/controllers/streams.py index a6e8aebb..29a601dc 100644 --- a/music_assistant/controllers/streams.py +++ b/music_assistant/controllers/streams.py @@ -16,11 +16,7 @@ from typing import TYPE_CHECKING from aiofiles.os import wrap from aiohttp import web -from music_assistant_models.config_entries import ( - ConfigEntry, - ConfigValueOption, - ConfigValueType, -) +from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption, ConfigValueType from music_assistant_models.enums import ( ConfigEntryType, ContentType, @@ -66,12 +62,7 @@ from music_assistant.helpers.audio import ( ) from music_assistant.helpers.ffmpeg import LOGGER as FFMPEG_LOGGER from music_assistant.helpers.ffmpeg import get_ffmpeg_stream -from music_assistant.helpers.util import ( - get_ip, - get_ips, - select_free_port, - try_parse_bool, -) +from music_assistant.helpers.util import get_ip, get_ips, select_free_port, try_parse_bool from music_assistant.helpers.webserver import Webserver from music_assistant.models.core_controller import CoreController from music_assistant.models.plugin import PluginProvider @@ -368,7 +359,6 @@ class StreamsController(CoreController): queue_item.uri, queue.display_name, ) - self.mass.player_queues.track_loaded_in_buffer(queue_id, queue_item_id) # pick pcm format based on the streamdetails and player capabilities if self.mass.config.get_raw_player_config_value(queue_id, CONF_VOLUME_NORMALIZATION, True): @@ -385,6 +375,11 @@ class StreamsController(CoreController): channels=2, ) chunk_num = 0 + + # inform the queue that the track is now loaded in the buffer + # so for example the next track can be enqueued + self.mass.player_queues.track_loaded_in_buffer(queue_id, queue_item_id) + async for chunk in get_ffmpeg_stream( audio_input=self.get_media_stream( streamdetails=queue_item.streamdetails, -- 2.34.1