from contextlib import suppress
from typing import TYPE_CHECKING, Any, TypedDict
+import shortuuid
+
from music_assistant.common.helpers.util import get_changed_keys
from music_assistant.common.models.config_entries import (
ConfigEntry,
if player.active_group and player.active_group != player.player_id:
return self.get_active_queue(player.active_group)
# active_source may be filled with other queue id
- if player.active_source != player_id and (
- queue := self.get_active_queue(player.active_source)
- ):
- return queue
+ return self.get(player.active_source) or self.get(player_id)
return self.get(player_id)
# Queue commands
queue.state = player.state
queue.current_item = self.get_item(queue_id, queue.current_index)
queue.next_item = self._get_next_item(queue_id)
+
# correct elapsed time when seeking
if (
queue.current_item
# 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)
+
+ queue.resume_pos = queue.corrected_elapsed_time
# do not send full updates if only time was updated
if changed_keys == {"elapsed_time"}:
self.mass.signal_event(
def signal_update(self, queue_id: str, items_changed: bool = False) -> None:
"""Signal state changed of given queue."""
queue = self._queues[queue_id]
+ queue.queue_version = shortuuid.random(8)
if items_changed:
- queue.queue_items_last_updated = time.time()
self.mass.signal_event(EventType.QUEUE_ITEMS_UPDATED, object_id=queue_id, data=queue)
# save items in cache
self.mass.create_task(
# set some basic vars
pcm_sample_size = int(pcm_format.sample_rate * (pcm_format.bit_depth / 8) * 2)
- crossfade_duration = await self.mass.config.get_player_config_value(
- queue.queue_id, CONF_CROSSFADE_DURATION
+ crossfade_duration = self.mass.config.get_raw_player_config_value(
+ queue.queue_id, CONF_CROSSFADE_DURATION, 10
)
crossfade_size = int(pcm_sample_size * crossfade_duration)
bytes_written = 0
"""Handle PLAY MEDIA on given player."""
async with self._play_media_lock:
player = self.mass.players.get(player_id)
+ # set the active source for the player to the media queue
+ # this accounts for syncgroups and linked players (e.g. sonos)
+ player.active_source = media.queue_id
if player.synced_to:
# should not happen, but just in case
raise RuntimeError("Player is synced")
group_leader.group_childs.remove(player_id)
player.synced_to = None
await self.cmd_stop(player_id)
- self.mass.players.update(player_id)
+ # make sure that the player manager gets an update
+ self.mass.players.update(player.player_id, skip_forward=True)
+ self.mass.players.update(group_leader.player_id, skip_forward=True)
async def _getcliraop_binary(self):
"""Find the correct raop/airplay binary belonging to the platform."""