Fix Airplay binary detection
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Fri, 10 Jan 2025 09:56:44 +0000 (10:56 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Fri, 10 Jan 2025 09:56:44 +0000 (10:56 +0100)
music_assistant/providers/airplay/helpers.py
music_assistant/providers/airplay/provider.py

index 4ecb594b4e3ef9a0bd72f183cd789e81c2880eab..693cbe502a3f6432ad22e3c0d1032f9685a88d3b 100644 (file)
@@ -2,10 +2,13 @@
 
 from __future__ import annotations
 
+import os
+import platform
 from typing import TYPE_CHECKING
 
 from zeroconf import IPVersion
 
+from music_assistant.helpers.process import check_output
 from music_assistant.providers.airplay.const import BROKEN_RAOP_MODELS
 
 if TYPE_CHECKING:
@@ -84,3 +87,31 @@ def is_broken_raop_model(manufacturer: str, model: str) -> bool:
         if broken_manufacturer in (manufacturer, "*") and broken_model in (model, "*"):
             return True
     return False
+
+
+async def get_cliraop_binary() -> str:
+    """Find the correct raop/airplay binary belonging to the platform."""
+
+    async def check_binary(cliraop_path: str) -> str | None:
+        try:
+            returncode, output = await check_output(
+                cliraop_path,
+                "-check",
+            )
+            if returncode == 0 and output.strip().decode() == "cliraop check":
+                return cliraop_path
+        except OSError:
+            pass
+        return None
+
+    base_path = os.path.join(os.path.dirname(__file__), "bin")
+    system = platform.system().lower().replace("darwin", "macos")
+    architecture = platform.machine().lower()
+
+    if bridge_binary := await check_binary(
+        os.path.join(base_path, f"cliraop-{system}-{architecture}")
+    ):
+        return bridge_binary
+
+    msg = f"Unable to locate RAOP Play binary for {system}/{architecture}"
+    raise RuntimeError(msg)
index fc41997beaa6b27ce3fce7702436fd7abfff9058..d89dbb385605a4244b402d3a06ebc049f8203114 100644 (file)
@@ -3,8 +3,6 @@
 from __future__ import annotations
 
 import asyncio
-import os
-import platform
 import socket
 import time
 from random import randrange
@@ -38,7 +36,6 @@ from music_assistant.constants import (
 )
 from music_assistant.helpers.datetime import utc
 from music_assistant.helpers.ffmpeg import get_ffmpeg_stream
-from music_assistant.helpers.process import check_output
 from music_assistant.helpers.util import TaskManager, get_ip_pton, lock, select_free_port
 from music_assistant.models.player_provider import PlayerProvider
 from music_assistant.providers.airplay.raop import RaopStreamSession
@@ -56,6 +53,7 @@ from .const import (
 )
 from .helpers import (
     convert_airplay_volume,
+    get_cliraop_binary,
     get_model_info,
     get_primary_ip_address,
     is_broken_raop_model,
@@ -148,7 +146,7 @@ class AirplayProvider(PlayerProvider):
     async def handle_async_init(self) -> None:
         """Handle async initialization of the provider."""
         self._players = {}
-        self.cliraop_bin = await self._getcliraop_binary()
+        self.cliraop_bin: str | None = await get_cliraop_binary()
         dacp_port = await select_free_port(39831, 49831)
         self.dacp_id = dacp_id = f"{randrange(2 ** 64):X}"
         self.logger.debug("Starting DACP ActiveRemote %s on port %s", dacp_id, dacp_port)
@@ -429,37 +427,6 @@ class AirplayProvider(PlayerProvider):
         self.mass.players.update(mass_player.player_id, skip_forward=True)
         self.mass.players.update(group_leader.player_id, skip_forward=True)
 
-    async def _getcliraop_binary(self) -> str:
-        """Find the correct raop/airplay binary belonging to the platform."""
-        # ruff: noqa: SIM102
-        if self.cliraop_bin is not None:
-            return self.cliraop_bin
-
-        async def check_binary(cliraop_path: str) -> str | None:
-            try:
-                returncode, output = await check_output(
-                    cliraop_path,
-                    "-check",
-                )
-                if returncode == 0 and output.strip().decode() == "cliraop check":
-                    self.cliraop_bin = cliraop_path
-                    return cliraop_path
-            except OSError:
-                pass
-            return None
-
-        base_path = os.path.join(os.path.dirname(__file__), "bin")
-        system = platform.system().lower().replace("darwin", "macos")
-        architecture = platform.machine().lower()
-
-        if bridge_binary := await check_binary(
-            os.path.join(base_path, f"cliraop-{system}-{architecture}")
-        ):
-            return bridge_binary
-
-        msg = f"Unable to locate RAOP Play binary for {system}/{architecture}"
-        raise RuntimeError(msg)
-
     def _get_sync_clients(self, player_id: str) -> list[AirPlayPlayer]:
         """Get all sync clients for a player."""
         mass_player = self.mass.players.get(player_id, True)