Skip to content

Commit

Permalink
Some small bugfixes and optimizations (#1039)
Browse files Browse the repository at this point in the history
* Fix radio mode error in queue controller

* fix clear queue

* get rid of some blocking IO in sonos

* fix syncgroup power state

* filter self from sync with list in sonos

* change poll logic a bit
  • Loading branch information
marcelveldt authored Jan 28, 2024
1 parent a1d02f9 commit 903c936
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 31 deletions.
8 changes: 5 additions & 3 deletions music_assistant/server/controllers/player_queues.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,9 @@ async def play_media(
self.domain, f"default_enqueue_action_{media_item.media_type.value}"
)
)
# clear queue if needed
if option == QueueOption.REPLACE:
self.clear(queue_id)

# collect tracks to play
ctrl = self.mass.music.get_controller(media_item.media_type)
Expand Down Expand Up @@ -340,9 +343,9 @@ async def play_media(

# overwrite or append radio source items
if option not in (QueueOption.ADD, QueueOption.PLAY, QueueOption.NEXT):
queue.radio_source = radio_mode
queue.radio_source = radio_source
else:
queue.radio_source += radio_mode
queue.radio_source += radio_source
# Use collected media items to calculate the radio if radio mode is on
if radio_mode:
tracks = await self._get_radio_tracks(queue_id)
Expand All @@ -359,7 +362,6 @@ async def play_media(

# handle replace: clear all items and replace with the new items
if option == QueueOption.REPLACE:
self.clear(queue_id)
self.load(
queue_id,
queue_items=queue_items,
Expand Down
14 changes: 5 additions & 9 deletions music_assistant/server/controllers/players.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def update(
)
# handle syncgroup - get attributes from first player that has this group as source
if player.player_id.startswith(SYNCGROUP_PREFIX):
if sync_leader := self.get_sync_leader(player):
if player.powered and (sync_leader := self.get_sync_leader(player)):
player.state = sync_leader.state
player.current_item_id = sync_leader.current_item_id
player.elapsed_time = sync_leader.elapsed_time
Expand Down Expand Up @@ -835,14 +835,10 @@ async def _poll_players(self) -> None:
# - every 30 seconds if the player is powered
# - every 10 seconds if the player is playing
if (
(player.available or count == 360)
and (
(player.powered and count % 30 == 0)
or (player_playing and count % 10 == 0)
or count == 360
)
and (player_prov := self.get_player_provider(player_id))
):
(player.powered and count % 30 == 0)
or (player_playing and count % 10 == 0)
or count == 360
) and (player_prov := self.get_player_provider(player_id)):
try:
await player_prov.poll_player(player_id)
except PlayerUnavailableError:
Expand Down
20 changes: 8 additions & 12 deletions music_assistant/server/providers/sonos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,6 @@ async def get_player_config_entries(
) -> tuple[ConfigEntry, ...]:
"""Return Config Entries for the given player."""
base_entries = await super().get_player_config_entries(player_id)
if not (sonos_player := self.sonosplayers.get(player_id)):
return base_entries
return base_entries + (
CONF_ENTRY_CROSSFADE,
ConfigEntry(
Expand All @@ -188,7 +186,6 @@ async def get_player_config_entries(
default_value=0,
range=(-10, 10),
description="Set the Bass level for the Sonos player",
value=sonos_player.soco.bass,
advanced=True,
),
ConfigEntry(
Expand All @@ -198,7 +195,6 @@ async def get_player_config_entries(
default_value=0,
range=(-10, 10),
description="Set the Treble level for the Sonos player",
value=sonos_player.soco.treble,
advanced=True,
),
ConfigEntry(
Expand All @@ -207,7 +203,6 @@ async def get_player_config_entries(
label="Loudness compensation",
default_value=True,
description="Enable loudness compensation on the Sonos player",
value=sonos_player.soco.loudness,
advanced=True,
),
)
Expand Down Expand Up @@ -256,7 +251,7 @@ async def cmd_stop(self, player_id: str) -> None:
player_id,
)
return
await asyncio.to_thread(sonos_player.soco.stop)
await self.mass.create_task(sonos_player.soco.stop)

async def cmd_play(self, player_id: str) -> None:
"""Send PLAY command to given player."""
Expand All @@ -267,7 +262,7 @@ async def cmd_play(self, player_id: str) -> None:
player_id,
)
return
await asyncio.to_thread(sonos_player.soco.play)
await self.mass.create_task(sonos_player.soco.play)

async def cmd_pause(self, player_id: str) -> None:
"""Send PAUSE command to given player."""
Expand All @@ -282,7 +277,7 @@ async def cmd_pause(self, player_id: str) -> None:
# pause not possible
await self.cmd_stop(player_id)
return
await asyncio.to_thread(sonos_player.soco.pause)
await self.mass.create_task(sonos_player.soco.pause)

async def cmd_volume_set(self, player_id: str, volume_level: int) -> None:
"""Send VOLUME_SET command to given player."""
Expand All @@ -291,7 +286,7 @@ def set_volume_level(player_id: str, volume_level: int) -> None:
sonos_player = self.sonosplayers[player_id]
sonos_player.soco.volume = volume_level

await asyncio.to_thread(set_volume_level, player_id, volume_level)
await self.mass.create_task(set_volume_level, player_id, volume_level)

async def cmd_volume_mute(self, player_id: str, muted: bool) -> None:
"""Send VOLUME MUTE command to given player."""
Expand All @@ -300,7 +295,7 @@ def set_volume_mute(player_id: str, muted: bool) -> None:
sonos_player = self.sonosplayers[player_id]
sonos_player.soco.mute = muted

await asyncio.to_thread(set_volume_mute, player_id, muted)
await self.mass.create_task(set_volume_mute, player_id, muted)

async def cmd_sync(self, player_id: str, target_player: str) -> None:
"""Handle SYNC command for given player.
Expand Down Expand Up @@ -356,7 +351,7 @@ async def play_media(
"accept play_media command, it is synced to another player."
)
metadata = create_didl_metadata(self.mass, url, queue_item)
self.mass.create_task(sonos_player.soco.play_uri, url, meta=metadata)
await self.mass.create_task(sonos_player.soco.play_uri, url, meta=metadata)

async def play_stream(self, player_id: str, stream_job: MultiClientStreamJob) -> None:
"""Handle PLAY STREAM on given player.
Expand Down Expand Up @@ -403,11 +398,12 @@ async def enqueue_next_queue_item(self, player_id: str, queue_item: QueueItem):
)
# set crossfade according to player setting
crossfade = await self.mass.config.get_player_config_value(player_id, CONF_CROSSFADE)
if sonos_player.soco.cross_fade != crossfade:
if sonos_player.crossfade != crossfade:

def set_crossfade():
with suppress(Exception):
sonos_player.soco.cross_fade = crossfade
sonos_player.crossfade = crossfade

await asyncio.to_thread(set_crossfade)

Expand Down
41 changes: 34 additions & 7 deletions music_assistant/server/providers/sonos/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ def __init__(
self.mass_player: Player = mass_player
self.available: bool = True
# cached attributes
self.crossfade: bool = False
self.play_mode: str | None = None
self.playback_status: str | None = None
self.channel: str | None = None
Expand Down Expand Up @@ -173,9 +174,27 @@ def missing_subscriptions(self) -> set[str]:

def setup(self) -> None:
"""Run initial setup of the speaker (NOT async friendly)."""
# update volume
self.crossfade = self.soco.cross_fade
self.mass_player.volume_level = self.soco.volume
self.mass_player.volume_muted = self.soco.mute
self.mass.loop.call_soon_threadsafe(
self.mass.config.set_raw_player_config_value,
self.player_id,
"sonos_loudness",
self.soco.loudness,
)
self.mass.loop.call_soon_threadsafe(
self.mass.config.set_raw_player_config_value,
self.player_id,
"sonos_bass",
self.soco.bass,
)
self.mass.loop.call_soon_threadsafe(
self.mass.config.set_raw_player_config_value,
self.player_id,
"sonos_treble",
self.soco.treble,
)
self.update_groups()
if not self.sync_coordinator:
self.poll_media()
Expand Down Expand Up @@ -415,7 +434,7 @@ def _handle_avtransport_event(self, event: SonosEvent) -> None:
return

if crossfade := event.variables.get("current_crossfade_mode"):
self.logger.debug("crossfade changed to %s", crossfade)
self.crossfade = bool(int(crossfade))

# Missing transport_state indicates a transient error
if (new_status := event.variables.get("transport_state")) is None:
Expand Down Expand Up @@ -476,8 +495,11 @@ def _handle_rendering_control_event(self, event: SonosEvent) -> None:

if loudness := variables.get("loudness"):
# TODO: handle this is a better way
self.mass.config.set_raw_player_config_value(
self.player_id, "sonos_loudness", loudness["Master"] == "1"
self.mass.loop.call_soon_threadsafe(
self.mass.config.set_raw_player_config_value,
self.player_id,
"sonos_loudness",
loudness["Master"] == "1",
)

for int_var in (
Expand All @@ -486,8 +508,11 @@ def _handle_rendering_control_event(self, event: SonosEvent) -> None:
):
if int_var in variables:
# TODO: handle this is a better way
self.mass.config.set_raw_player_config_value(
self.player_id, f"sonos_{int_var}", variables[int_var]
self.mass.loop.call_soon_threadsafe(
self.mass.config.set_raw_player_config_value,
self.player_id,
f"sonos_{int_var}",
variables[int_var],
)

self.update_player()
Expand Down Expand Up @@ -676,7 +701,9 @@ def _update_attributes(self):

# zone topology (syncing/grouping) details
self.mass_player.can_sync_with = tuple(
x.player_id for x in self.sonos_prov.sonosplayers.values() if x.sync_coordinator is None
x.player_id
for x in self.sonos_prov.sonosplayers.values()
if x.sync_coordinator is None and x.player_id != self.player_id
)
if self.sync_coordinator:
# player is syned to another player
Expand Down

0 comments on commit 903c936

Please sign in to comment.