Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix some issues with Airplay provider #919

Merged
merged 3 commits into from
Nov 11, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions music_assistant/server/providers/airplay/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
key="remove_timeout",
type=ConfigEntryType.INTEGER,
default_value=0,
range=(-1, 3600),
label="Remove timeout",
description="Player discovery is managed using mDNS protocol, "
"which means that a player sends regular keep-alive messages and a bye when "
Expand All @@ -93,11 +94,11 @@
advanced=True,
),
ConfigEntry.from_dict(
{**CONF_ENTRY_OUTPUT_CODEC.to_dict(), "default_value": "pcm", "hidden": True}
{**CONF_ENTRY_OUTPUT_CODEC.to_dict(), "default_value": "flac", "hidden": True}
),
)

NEED_BRIDGE_RESTART = {"values/read_ahead", "values/encryption", "values/alac_encode"}
NEED_BRIDGE_RESTART = {"values/read_ahead", "values/encryption", "values/alac_encode", "enabled"}


async def setup(
Expand Down Expand Up @@ -135,9 +136,11 @@ class AirplayProvider(PlayerProvider):
_closing: bool = False
_config_file: str | None = None
_log_reader_task: asyncio.Task | None = None
_removed_players: set[str] | None = None

async def handle_setup(self) -> None:
"""Handle async initialization of the provider."""
self._removed_players = set()
self._config_file = os.path.join(self.mass.storage_path, "airplay_bridge.xml")
# locate the raopbridge binary (will raise if that fails)
self._bridge_bin = await self._get_bridge_binary()
Expand Down Expand Up @@ -180,6 +183,11 @@ async def update_config():

asyncio.create_task(update_config())

def on_player_config_removed(self, player_id: str) -> None:
"""Call (by config manager) when the configuration of a player is removed."""
self._removed_players.add(player_id)
self.restart_bridge()

async def cmd_stop(self, player_id: str) -> None:
"""Send STOP command to given player."""
# simply forward to underlying slimproto player
Expand Down Expand Up @@ -473,15 +481,15 @@ async def _check_config_xml(self, recreate: bool = False) -> None:
# get/set all device configs
for device_elem in xml_root.findall("device"):
player_id = device_elem.find("mac").text
if player_id in self._removed_players:
xml_root.remove(device_elem)
self._removed_players.remove(player_id)
continue
# use raw config values because players are not
# yet available at startup/init (race condition)
raw_player_conf = self.mass.config.get(f"{CONF_PLAYERS}/{player_id}")
if not raw_player_conf:
continue
# prefer name from UDN because default name is often wrong
udn = device_elem.find("udn").text
udn_name = udn.split("@")[1].split("._")[0]
device_elem.find("name").text = udn_name
device_elem.find("enabled").text = "1" if raw_player_conf["enabled"] else "0"

# set some values that are not (yet) configurable
Expand Down