music-assistant: replace all pre-compiled binaries, music-assistant: bring back airplay support (#492341)
This commit is contained in:
@@ -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
|
||||
]
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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"):
|
||||
@@ -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";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user