Skip to content

Commit

Permalink
Bugfixes (#1044)
Browse files Browse the repository at this point in the history
* Fix for corrupted sonos config

* do not support late joining on multi client stream

* prevent SoCoUPnPException
  • Loading branch information
marcelveldt authored Jan 29, 2024
1 parent b9822a3 commit a652572
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 47 deletions.
15 changes: 15 additions & 0 deletions music_assistant/server/controllers/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ async def setup(self) -> None:
"""Async initialize of controller."""
await self._load()
self.initialized = True
#### temp fix issue introduced in b89 ##########
# TODO: remove after b92
final_player_configs = {}
for player_id, player_conf in self.get(CONF_PLAYERS, {}).items():
if "provider" in player_conf:
final_player_configs[player_id] = player_conf
self.set(CONF_PLAYERS, final_player_configs)
#### end of temp fix ############################

# create default server ID if needed (also used for encrypting passwords)
self.set_default(CONF_SERVER_ID, uuid4().hex)
server_id: str = self.get(CONF_SERVER_ID)
Expand Down Expand Up @@ -588,6 +597,9 @@ def set_raw_provider_config_value(
Note that this only stores the (raw) value without any validation or default.
"""
if not self.get(f"{CONF_PROVIDERS}/{provider_instance}"):
# only allow setting raw values if main entry exists
raise KeyError(f"Invalid provider_instance: {provider_instance}")
self.set(f"{CONF_PROVIDERS}/{provider_instance}/{key}", value)

def set_raw_player_config_value(self, player_id: str, key: str, value: ConfigValueType) -> None:
Expand All @@ -596,6 +608,9 @@ def set_raw_player_config_value(self, player_id: str, key: str, value: ConfigVal
Note that this only stores the (raw) value without any validation or default.
"""
if not self.get(f"{CONF_PLAYERS}/{player_id}"):
# only allow setting raw values if main entry exists
raise KeyError(f"Invalid player_id: {player_id}")
self.set(f"{CONF_PLAYERS}/{player_id}/values/{key}", value)

def save(self, immediate: bool = False) -> None:
Expand Down
9 changes: 4 additions & 5 deletions music_assistant/server/controllers/streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,11 @@ async def subscribe(self, player_id: str) -> AsyncGenerator[bytes, None]:
self.subscribed_players[player_id] = sub_queue = asyncio.Queue(2)

if self._all_clients_connected.is_set():
# client subscribes while we're already started
self.logger.warning(
"Client %s is joining while the stream is already started", player_id
# client subscribes while we're already started - we dont support that (for now?)
raise RuntimeError(
f"Client {player_id} is joining while the stream is already started"
)
else:
self.logger.debug("Subscribed client %s", player_id)
self.logger.debug("Subscribed client %s", player_id)

if len(self.subscribed_players) == len(self.expected_players):
# we reached the number of expected subscribers, set event
Expand Down
6 changes: 1 addition & 5 deletions music_assistant/server/providers/slimproto/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,14 +890,10 @@ def _get_sync_clients(self, player_id: str) -> list[SlimClient]:

def _get_corrected_elapsed_milliseconds(self, client: SlimClient) -> int:
"""Return corrected elapsed milliseconds."""
skipped_millis = 0
active_queue = self.mass.player_queues.get_active_queue(client.player_id)
if stream_job := self.mass.streams.multi_client_jobs.get(active_queue.queue_id):
skipped_millis = stream_job.client_seconds_skipped.get(client.player_id, 0) * 1000
sync_delay = self.mass.config.get_raw_player_config_value(
client.player_id, CONF_SYNC_ADJUST, 0
)
current_millis = int(skipped_millis + client.elapsed_milliseconds)
current_millis = client.elapsed_milliseconds
if sync_delay != 0:
return current_millis - sync_delay
return current_millis
20 changes: 13 additions & 7 deletions music_assistant/server/providers/sonos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from soco import events_asyncio, zonegroupstate
from soco.core import SoCo
from soco.discovery import discover
from soco.exceptions import SoCoUPnPException

from music_assistant.common.models.config_entries import (
CONF_ENTRY_CROSSFADE,
Expand Down Expand Up @@ -177,13 +178,16 @@ 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(
key="sonos_bass",
type=ConfigEntryType.INTEGER,
label="Bass",
default_value=0,
default_value=sonos_player.bass,
value=sonos_player.bass,
range=(-10, 10),
description="Set the Bass level for the Sonos player",
advanced=True,
Expand All @@ -192,7 +196,8 @@ async def get_player_config_entries(
key="sonos_treble",
type=ConfigEntryType.INTEGER,
label="Treble",
default_value=0,
default_value=sonos_player.treble,
value=sonos_player.treble,
range=(-10, 10),
description="Set the Treble level for the Sonos player",
advanced=True,
Expand All @@ -201,7 +206,8 @@ async def get_player_config_entries(
key="sonos_loudness",
type=ConfigEntryType.BOOLEAN,
label="Loudness compensation",
default_value=True,
default_value=sonos_player.loudness,
value=sonos_player.loudness,
description="Enable loudness compensation on the Sonos player",
advanced=True,
),
Expand Down Expand Up @@ -401,11 +407,11 @@ async def enqueue_next_queue_item(self, player_id: str, queue_item: QueueItem):
if sonos_player.crossfade != crossfade:

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

await asyncio.to_thread(set_crossfade)
with suppress(SoCoUPnPException):
await asyncio.to_thread(set_crossfade)

await self._enqueue_item(sonos_player, url=url, queue_item=queue_item)

Expand Down
52 changes: 22 additions & 30 deletions music_assistant/server/providers/sonos/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ def __init__(
self.uri: str | None = None
self.position: int | None = None
self.position_updated_at: datetime.datetime | None = None
self.loudness: bool = False
self.bass: int = 0
self.treble: int = 0
# Subscriptions and events
self._subscriptions: list[SubscriptionBase] = []
self._subscription_lock: asyncio.Lock | None = None
Expand Down Expand Up @@ -181,24 +184,9 @@ def setup(self) -> None:
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.loudness = self.soco.loudness
self.bass = self.soco.bass
self.treble = self.soco.treble
self.update_groups()
if not self.sync_coordinator:
self.poll_media()
Expand Down Expand Up @@ -514,25 +502,29 @@ def _handle_rendering_control_event(self, event: SonosEvent) -> None:

if loudness := variables.get("loudness"):
# TODO: handle this is a better way
self.mass.loop.call_soon_threadsafe(
self.mass.config.set_raw_player_config_value,
self.player_id,
"sonos_loudness",
loudness["Master"] == "1",
)
self.loudness = loudness["Master"] == "1"
with contextlib.suppress(KeyError):
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 (
"bass",
"treble",
):
if int_var in variables:
# TODO: handle this is a better way
self.mass.loop.call_soon_threadsafe(
self.mass.config.set_raw_player_config_value,
self.player_id,
f"sonos_{int_var}",
variables[int_var],
)
setattr(self, int_var, variables[int_var])
with contextlib.suppress(KeyError):
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

0 comments on commit a652572

Please sign in to comment.