music-assistant: replace all pre-compiled binaries, music-assistant: bring back airplay support (#492341)

This commit is contained in:
Sandro
2026-03-26 22:54:45 +00:00
committed by GitHub
8 changed files with 291 additions and 14 deletions
@@ -16,6 +16,7 @@ let
;
inherit (types)
bool
listOf
enum
str
@@ -54,6 +55,15 @@ in
'';
};
openFirewall = lib.mkOption {
type = bool;
default = false;
description = ''
Whether to open required ports for the configured providers.
Currently airplay and sendspin need port to be opened to function.
'';
};
providers = mkOption {
type = listOf (enum cfg.package.providerNames);
default = [ ];
@@ -68,6 +78,31 @@ in
};
config = mkIf cfg.enable {
networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts =
lib.optional cfg.enable 8097 # Music Assistant stream port
++ lib.optional (lib.elem "airplay" cfg.providers) 7000
++ lib.optional (lib.elem "sendspin" cfg.providers) 8927;
# The information published by Apple 1 seem to not apply to libraop.
# The closest we could find that represents the port range being used as observed by tcpdump is the ephemeral port range.
# 1: https://support.apple.com/en-us/103229#:~:text=49152%E2%80%93-,65535,-TCP%2C%20UDP
# 2: https://en.wikipedia.org/wiki/Ephemeral_port#Range
allowedUDPPortRanges = lib.mkIf (lib.elem "airplay" cfg.providers) [
{
from = 32768;
to = 65535;
}
];
};
services.avahi = lib.mkIf (lib.elem "airplay_receiver" cfg.providers) {
enable = true;
publish = {
enable = true;
userServices = true;
};
};
systemd.services.music-assistant = {
description = "Music Assistant";
documentation = [ "https://music-assistant.io" ];
@@ -87,6 +122,13 @@ in
[
lsof
]
++ lib.optionals (lib.elem "airplay" cfg.providers) [
cliairplay
libraop
]
++ lib.optionals (lib.elem "airplay_receiver" cfg.providers) [
shairport-sync
]
++ lib.optionals (lib.elem "spotify" cfg.providers) [
librespot-ma
]
+81
View File
@@ -0,0 +1,81 @@
{
lib,
autoreconfHook,
bison,
curl,
fetchFromGitHub,
fetchpatch,
ffmpeg-headless,
flex,
gperf,
json_c,
libconfuse,
libevent,
libgcrypt,
libgpg-error,
libplist,
libsodium,
libunistring,
libuuid,
libxml2,
pkg-config,
stdenv,
zlib,
}:
stdenv.mkDerivation {
pname = "cliairplay";
# see the beginning of configure.ac for the upstream version number
version = "0.2-unstable-2025-12-30";
src = fetchFromGitHub {
owner = "music-assistant";
repo = "cliairplay";
# we try to closely match the commit used in the last music-assistant release from
# https://github.com/music-assistant/server/tree/stable/music_assistant/providers/airplay/bin
rev = "4660d886585d6bf8f32e889feec2a0e8975c51dc";
fetchSubmodules = true;
hash = "sha256-oDStn9LdLYWKhZNm7Qfdibs4qsct8gE3RZbTKooQeOM=";
};
patches = [
# Support gettext 0.25
(fetchpatch {
url = "https://github.com/music-assistant/cliairplay/commit/92a2445d64c476d740feba1f31c7e5bc768701b7.patch";
hash = "sha256-/YnopvAGHHnQhfr2X1OenLMoF4ZlUq9x8tQZha/XfbQ=";
})
];
nativeBuildInputs = [
autoreconfHook
bison
flex
gperf
pkg-config
];
buildInputs = [
curl
ffmpeg-headless
json_c
libconfuse
libevent
libgcrypt
libgpg-error
libplist
libsodium
libunistring
libuuid
libxml2
zlib
];
meta = {
description = "Command line interface for audio streaming to AirPlay 2 devices";
homepage = "https://github.com/music-assistant/cliairplay";
license = lib.licenses.gpl2Plus;
maintainers = with lib.maintainers; [ SuperSandro2000 ];
mainProgram = "cliap2";
platforms = lib.platforms.unix;
};
}
+15
View File
@@ -0,0 +1,15 @@
diff --git a/Makefile b/Makefile
index 7d735b8..c3ea73a 100644
--- a/Makefile
+++ b/Makefile
@@ -68,7 +68,9 @@ OBJECTS_BIN += $(patsubst %.cpp,$(BUILDDIR)/%.o,$(filter %.cpp,$(SOURCES_BIN)))
LIBRARY = $(CODECS)/$(HOST)/$(PLATFORM)/libcodecs.a $(MDNS)/$(HOST)/$(PLATFORM)/libmdns.a
-ifneq ($(STATIC),)
+ifeq ($(STATIC),)
+LDFLAGS += -lcrypto
+else
LIBRARY += $(OPENSSL)/libopenssl.a
DEFINES += -DSSL_STATIC_LIB
endif
+83
View File
@@ -0,0 +1,83 @@
{
lib,
fetchFromGitHub,
openssl,
stdenv,
}:
let
host =
if stdenv.hostPlatform.isLinux then
"linux"
else if stdenv.hostPlatform.isDarwin then
"macos"
else
throw "libraop does not support this platform, yet";
in
stdenv.mkDerivation {
pname = "libraop";
version = "0-unstable-2026-02-09";
src = fetchFromGitHub {
owner = "music-assistant";
repo = "libraop";
# we try to closely match the commit used in the last music-assistant release from
# https://github.com/music-assistant/server/tree/stable/music_assistant/providers/airplay/bin
rev = "f49284282ea4ea740d07fabc230b4182f8c69a74";
fetchSubmodules = true;
hash = "sha256-m1ll5vRZx4d/5IWCG24yY/SWEIIz2k/iU84vQKHlCdo=";
};
patches = [
# https://github.com/philippe44/libraop/pull/48
./link-libssl.diff
];
postPatch = ''
# the most security critical part we build ourself
rm -r libopenssl/
# do not confuse the prebuilt binaries with the ones we build
rm bin/*
# easen debugging and we strip ourselves, too
substituteInPlace Makefile \
--replace-fail "LDFLAGS += -s" "LDFLAGS +="
''
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
# on darwin the direct dlopen to system libcrypto crashes with
# WARNING: /nix/store/.../bin/cliraop is loading libcrypto in an unsafe way
# Abort trap: 6
substituteInPlace crosstools/src/cross_ssl.c \
--replace-fail '"libcrypto.dylib"' '"${lib.getLib openssl}/lib/libcrypto.dylib"' \
--replace-fail '"libssl.dylib"' '"${lib.getLib openssl}/lib/libssl.dylib"'
'';
buildInputs = [
openssl
];
makeFlags = [
"HOST=${host}"
"PLATFORM=${stdenv.hostPlatform.uname.processor}"
];
installPhase = ''
mkdir -p $out/bin
cp bin/cliraop-${host}-${stdenv.hostPlatform.uname.processor} $out/bin/cliraop
'';
meta = {
description = "RAOP player and library (AirPlay)";
homepage = "https://github.com/music-assistant/libraop";
# https://github.com/philippe44/libraop/issues/36
license = with lib.licenses; [
gpl2Only
mit
];
maintainers = with lib.maintainers; [ SuperSandro2000 ];
mainProgram = "cliraop";
platforms = with lib.platforms; linux ++ darwin;
sourceProvenance = [ lib.sourceTypes.binaryNativeCode ];
};
}
@@ -0,0 +1,29 @@
diff --git a/music_assistant/providers/airplay/helpers.py b/music_assistant/providers/airplay/helpers.py
index c5fef7eb..a628622d 100644
--- a/music_assistant/providers/airplay/helpers.py
+++ b/music_assistant/providers/airplay/helpers.py
@@ -6,6 +6,7 @@
import os
import platform
import time
+from shutil import which
from typing import TYPE_CHECKING
from zeroconf import IPVersion
@@ -176,7 +177,6 @@ async def check_binary(cli_path: str) -> str | None:
pass
return None
- base_path = os.path.join(os.path.dirname(__file__), "bin")
system = platform.system().lower().replace("darwin", "macos")
architecture = platform.machine().lower()
@@ -188,7 +188,7 @@ async def check_binary(cli_path: str) -> str | None:
raise RuntimeError(f"Unsupported streaming protocol requested: {protocol}")
if bridge_binary := await check_binary(
- os.path.join(base_path, f"{package}-{system}-{architecture}")
+ which(package)
):
return bridge_binary
+16 -11
View File
@@ -40,11 +40,6 @@ let
pythonPath = python.pkgs.makePythonPath providerDependencies;
in
assert
(lib.elem "airplay" providers)
-> throw "music-assistant: airplay support is missing libraop, a library we will not package because it depends on OpenSSL 1.1.";
python.pkgs.buildPythonApplication rec {
pname = "music-assistant";
version = "2.7.11";
@@ -66,6 +61,12 @@ python.pkgs.buildPythonApplication rec {
# Look up librespot from PATH at runtime
./librespot.patch
# Look up shairport-sync from PATH at runtime
./shairport-sync.patch
# Look up cliraop/cliap2 from PATH at runtime
./cliraop-cliap2.patch
# Disable interactive dependency resolution, which clashes with the immutable Python environment
./dont-install-deps.patch
@@ -91,7 +92,16 @@ python.pkgs.buildPythonApplication rec {
substituteInPlace pyproject.toml \
--replace-fail "get-mac" "getmac"
rm -rv music_assistant/providers/spotify/bin
rm -rv \
music_assistant/providers/airplay/bin/{cliap2-*,cliraop-*} \
music_assistant/providers/airplay_receiver/bin/{build_binaries.sh,shairport-sync-*} \
music_assistant/providers/spotify/bin
found_bins=$(find music_assistant/ -wholename '*/bin/*' -type f -executable -print0 | tr '\0' ' ')
if [[ -n $found_bins ]]; then
echo "Found binaries that should be replaced with packages built from source: $found_bins"
exit 2
fi
'';
build-system = with python.pkgs; [
@@ -192,11 +202,6 @@ python.pkgs.buildPythonApplication rec {
pythonImportsCheck = [ "music_assistant" ];
postFixup = ''
# binary native code, segfaults when autopatchelf'd, requires openssl 1.1 to build
rm $out/${python3.sitePackages}/music_assistant/providers/airplay/bin/cliraop-*
'';
passthru = {
inherit
python
@@ -0,0 +1,20 @@
diff --git a/music_assistant/providers/airplay_receiver/helpers.py b/music_assistant/providers/airplay_receiver/helpers.py
index 6f2b35df..dd839b73 100644
--- a/music_assistant/providers/airplay_receiver/helpers.py
+++ b/music_assistant/providers/airplay_receiver/helpers.py
@@ -22,15 +22,9 @@ async def check_shairport_sync(shairport_path: str) -> str | None:
except OSError:
return None
- # First, check if bundled binary exists
- base_path = os.path.join(os.path.dirname(__file__), "bin")
system = platform.system().lower().replace("darwin", "macos")
architecture = platform.machine().lower()
- if shairport_binary := await check_shairport_sync(
- os.path.join(base_path, f"shairport-sync-{system}-{architecture}")
- ):
- return shairport_binary
# If no bundled binary, check system PATH
if system_binary := shutil.which("shairport-sync"):
+5 -3
View File
@@ -27,6 +27,7 @@
soxr,
alac,
sndio,
enableAvahi ? true,
enableAirplay2 ? false,
enableStdout ? true,
enableAlsa ? true,
@@ -45,6 +46,7 @@
enableAlac ? true,
enableConvolution ? true,
enableLibdaemon ? false,
enableTinySVCmDNS ? true,
}:
let
@@ -79,10 +81,10 @@ stdenv.mkDerivation (finalAttrs: {
buildInputs = [
openssl
avahi
popt
libconfig
]
++ optional enableAvahi avahi
++ optional enableLibdaemon libdaemon
++ optional enableAlsa alsa-lib
++ optional enableSndio sndio
@@ -115,9 +117,8 @@ stdenv.mkDerivation (finalAttrs: {
"--without-configfiles"
"--sysconfdir=/etc"
"--with-ssl=openssl"
"--with-stdout"
"--with-avahi"
]
++ optional enableAvahi "--with-avahi"
++ optional enablePulse "--with-pa"
++ optional enablePipewire "--with-pw"
++ optional enableAlsa "--with-alsa"
@@ -134,6 +135,7 @@ stdenv.mkDerivation (finalAttrs: {
++ optional enableMetadata "--with-metadata"
++ optional enableMpris "--with-mpris-interface"
++ optional enableMqttClient "--with-mqtt-client"
++ optional enableTinySVCmDNS "--with-tinysvcmdns"
++ optional enableLibdaemon "--with-libdaemon"
++ optional enableAirplay2 "--with-airplay-2";