music-assistant: 2.8.7 -> 2.9.4

something tried to use sched_setscheduler on startup, so @resources must
be allowed. Could be torch but since that is a general requirement we
cannot make this conditional.

Changelog: https://github.com/music-assistant/server/releases/tag/2.9.0
Changelog: https://github.com/music-assistant/server/releases/tag/2.9.1
Changelog: https://github.com/music-assistant/server/releases/tag/2.9.2
Changelog: https://github.com/music-assistant/server/releases/tag/2.9.3
Changelog: https://github.com/music-assistant/server/releases/tag/2.9.4
Diff: https://github.com/music-assistant/server/compare/2.8.7...2.9.4
This commit is contained in:
Sandro Jäckel
2026-06-29 17:39:32 +02:00
parent 6d15905968
commit 932c63b704
6 changed files with 165 additions and 19 deletions
@@ -171,6 +171,7 @@ in
DevicePolicy = "closed";
LockPersonality = true;
# breaks pyopenssl's cffi calls, used in remote access feature
# not compatible with llvmlite which is required by numba -> librosa
MemoryDenyWriteExecute = false;
ProcSubset = "pid";
ProtectClock = true;
@@ -194,7 +195,7 @@ in
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"~@privileged @resources"
"~@privileged"
"mbind"
]
++ lib.optionals useYTMusic [
@@ -43,7 +43,7 @@ index 0ac8a70f..5d74e25b 100644
"""Set up the YTMusic provider."""
logging.getLogger("yt_dlp").setLevel(self.logger.level + 10)
- await self._install_packages()
self._cookie = self.config.get_value(CONF_COOKIE)
self._cookie = str(self.config.get_value(CONF_COOKIE))
self._po_token_server_url = (
self.config.get_value(CONF_PO_TOKEN_SERVER_URL) or DEFAULT_PO_TOKEN_SERVER_URL
@@ -1103,8 +1102,6 @@ async def _install_packages(self) -> None:
+2 -2
View File
@@ -8,13 +8,13 @@
buildPythonPackage rec {
pname = "music-assistant-frontend";
version = "2.17.146";
version = "2.17.186";
pyproject = true;
src = fetchPypi {
pname = "music_assistant_frontend";
inherit version;
hash = "sha256-VU2SXL6SLFiKntmJJtCabBOYOeImYFPHYxvMPmnhmEc=";
hash = "sha256-dNGzXDRZuQLRkMY0erjJZE4h26yFP4Fdn9a3K6T0RvM=";
};
build-system = [ setuptools ];
+36 -5
View File
@@ -6,6 +6,7 @@
ffmpeg_7-headless,
nixosTests,
replaceVars,
writableTmpDirAsHomeHook,
providers ? [ ],
}:
@@ -15,10 +16,10 @@ let
music-assistant-frontend = prev.callPackage ./frontend.nix { };
music-assistant-models = final.music-assistant-models.overridePythonAttrs (oldAttrs: {
version = "1.1.115";
version = "1.1.129";
src = oldAttrs.src.override {
hash = "sha256-oEXL0B8JNH4PcltpES375ov7QGs+gtYKlMGr1B7BlKY=";
hash = "sha256-6gVHlFTt/bsj4nUGPS6HDUQ7zczpfos75U6l4Yk9W6k=";
};
});
}
@@ -39,14 +40,14 @@ assert
pythonPackages.buildPythonApplication rec {
pname = "music-assistant";
version = "2.8.7";
version = "2.9.4";
pyproject = true;
src = fetchFromGitHub {
owner = "music-assistant";
repo = "server";
tag = version;
hash = "sha256-m91q/8XYoZ5Azu79fKD0euRCuf29w3vj5cxdFheDsmI=";
hash = "sha256-PiSBghhlxknijRqghkO8wn1CB2XqaJrjrvGNvZUlNbo=";
};
patches = [
@@ -82,6 +83,11 @@ pythonPackages.buildPythonApplication rec {
];
postPatch = ''
# Undo Python 3.14 only syntax
substituteInPlace music_assistant/controllers/streams/controller.py \
--replace-fail "except BrokenPipeError, ConnectionResetError, ConnectionError:" "except (BrokenPipeError, ConnectionResetError, ConnectionError):" \
--replace-fail "except BrokenPipeError, ConnectionResetError:" "except (BrokenPipeError, ConnectionResetError):"
substituteInPlace pyproject.toml \
--replace-fail "0.0.0" "${version}" \
--replace-fail "==" ">="
@@ -110,6 +116,7 @@ pythonPackages.buildPythonApplication rec {
"mashumaro"
"orjson"
"xmltodict"
"zeroconf"
];
pythonRemoveDeps = [
@@ -140,6 +147,7 @@ pythonPackages.buildPythonApplication rec {
ifaddr
librosa
mashumaro
modern-colorthief
music-assistant-frontend
music-assistant-models
mutagen
@@ -151,6 +159,8 @@ pythonPackages.buildPythonApplication rec {
pyjwt
python-slugify
shortuuid
torch
torchaudio
unidecode
xmltodict
zeroconf
@@ -175,18 +185,36 @@ pythonPackages.buildPythonApplication rec {
with pythonPackages;
[
pytestCheckHook
writableTmpDirAsHomeHook
]
++ lib.concatAttrValues optional-dependencies
++ (lib.concatMap (provider: providerPackages.${provider} python.pkgs) [
++ (lib.concatMap (provider: providerPackages.${provider} pythonPackages) [
"acoustid_lookup"
"audible"
"dlna"
"fastmcp_server"
"jellyfin"
"mpd"
"msx_bridge"
"opensubsonic"
"sendspin"
"smart_fades"
"snapcast"
"sonic_analysis"
"sonic_similarity"
"tidal"
"wiim"
"ytmusic"
]);
preCheck = ''
export NUMBA_CACHE_DIR=$(mktemp -d)
# required for smart_fades tests
mkdir -p $HOME/.cache/torch/hub/checkpoints/
cp ${pythonPackages.beat-this.passthru.small0Ckpt} $HOME/.cache/torch/hub/checkpoints/beat_this-small0.ckpt
'';
disabledTestPaths = [
# no multicast support in build sandbox:
# "OSError: [Errno 19] No such device"
@@ -194,9 +222,12 @@ pythonPackages.buildPythonApplication rec {
# provider is missing dependencies
"tests/providers/apple_music"
"tests/providers/bandcamp"
"tests/providers/hue_entertainment"
"tests/providers/kion_music"
"tests/providers/nicovideo"
"tests/providers/qqmusic"
"tests/providers/yandex_music"
"tests/providers/yandex_ynison"
"tests/providers/zvuk_music"
# mocking music_assistant.providers.airplay.pairing.AirPlayPairing does not work
"tests/providers/airplay/test_player.py::test_start_pairing__pin_decision"
+102 -7
View File
@@ -1,8 +1,27 @@
# Do not edit manually, run ./update-providers.py
{
version = "2.8.7";
version = "2.9.4";
builtins = [
"builtin"
"coverartarchive"
"fanarttv"
"itunes_artwork"
"local_audio"
"loudness_analysis"
"lrclib"
"musicbrainz"
"sendspin"
"sync_group"
"theaudiodb"
"universal_player"
"wikipedia"
];
providers = {
acoustid_lookup =
ps: with ps; [
pyacoustid
];
airplay =
ps: with ps; [
srptools
@@ -65,6 +84,10 @@
];
fanarttv = ps: [
];
fastmcp_server =
ps: with ps; [
fastmcp
];
filesystem_local = ps: [
];
filesystem_nfs = ps: [
@@ -89,10 +112,14 @@
ps: with ps; [
pyheos
];
hue_entertainment = ps: [
]; # missing hue-entertainment
ibroadcast = ps: [
]; # missing ibroadcastaio
internet_archive = ps: [
];
itunes_artwork = ps: [
];
itunes_podcasts = ps: [
];
jellyfin =
@@ -101,6 +128,8 @@
];
kion_music = ps: [
]; # missing yandex-music
lastfm_recommendations = ps: [
];
lastfm_scrobble =
ps: with ps; [
pylast
@@ -109,16 +138,40 @@
ps: with ps; [
liblistenbrainz
];
local_audio =
ps: with ps; [
sounddevice
];
loudness_analysis = ps: [
];
lrclib = ps: [
];
motherearthradio = ps: [
];
mpd =
ps: with ps; [
python-mpd2
];
msx_bridge =
ps: with ps; [
pydantic
];
musicbrainz = ps: [
];
musiccast =
ps: with ps; [
aiomusiccast
];
nicovideo = ps: [
]; # missing niconico.py-ma
musicme = ps: [
];
neteasecloudmusic = ps: [
];
nicovideo =
ps: with ps; [
pydantic
]; # missing niconico.py-ma
nts = ps: [
];
nugs = ps: [
];
opensubsonic =
@@ -147,6 +200,8 @@
];
qobuz = ps: [
];
qqmusic = ps: [
]; # missing qqmusic-api-python
radiobrowser =
ps: with ps; [
radios
@@ -158,13 +213,26 @@
async-upnp-client
rokuecp
];
samsung_wam = ps: [
]; # missing pywam
sendspin =
ps: with ps; [
ps:
with ps;
[
aiosendspin
av
];
]
++ aiosendspin.optional-dependencies.server;
siriusxm = ps: [
]; # missing sxm
smart_fades =
ps: with ps; [
beat-this
nnaudio
threadpoolctl
];
smart_playlist = ps: [
];
snapcast =
ps: with ps; [
bidict
@@ -173,6 +241,20 @@
];
somafm = ps: [
];
sonic_analysis =
ps: with ps; [
huggingface-hub
pyyaml
threadpoolctl
torchlibrosa
transformers
];
sonic_similarity =
ps: with ps; [
huggingface-hub
transformers
usearch
];
sonos =
ps: with ps; [
aiosonos
@@ -220,8 +302,20 @@
ps: with ps; [
aiovban
];
webdav = ps: [
];
wiim =
ps: with ps; [
wiim
];
wikipedia = ps: [
];
yandex_music = ps: [
]; # missing yandex-music
]; # missing yandex-music, ya-passport-auth
yandex_smarthome = ps: [
]; # missing ya-passport-auth
yandex_ynison = ps: [
]; # missing ya-passport-auth
yousee = ps: [
];
ytmusic =
@@ -232,6 +326,7 @@
ytmusicapi
]; # missing deno
zvuk_music = ps: [
]; # missing zvuk-music
];
# missing zvuk-music
};
}
@@ -33,9 +33,13 @@ TEMPLATE = """# Do not edit manually, run ./update-providers.py
{%- for provider in providers | sort(attribute='domain') %}
{{ provider.domain }} = {% if provider.available %}ps: with ps;{% else %}ps:{% endif %} [
{%- for requirement in provider.available | sort %}
{{ requirement }}
{{ requirement }}
{%- endfor %}
];{% if provider.missing %} # missing {{ ", ".join(provider.missing) }}{% endif %}
]
{%- for requirement in provider.extra_list_deps | sort %}
++ {{ requirement }}
{%- endfor %}
;{% if provider.missing %} # missing {{ ", ".join(provider.missing) }}{% endif %}
{%- endfor %}
};
}
@@ -62,6 +66,9 @@ PACKAGE_MAP = {
EXTRA_DEPS = {
# Those providers cannot guard pydantic behind TYPE_CHECKING
"msx_bridge": ["pydantic"],
"nicovideo": ["pydantic"],
"ytmusic": [
# https://github.com/music-assistant/server/blob/2.5.8/music_assistant/providers/ytmusic/__init__.py#L120
"bgutil-ytdlp-pot-provider",
@@ -70,6 +77,11 @@ EXTRA_DEPS = {
}
EXTRA_LIST_DEPS = {
"sendspin": ["aiosendspin.optional-dependencies.server"],
}
def run_sync(cmd: List[str]) -> None:
print(f"$ {' '.join(cmd)}")
process = run(cmd)
@@ -194,6 +206,7 @@ class Provider:
domain: str
available: list[str] = field(default_factory=list)
missing: list[str] = field(default_factory=list)
extra_list_deps: list[str] = field(default_factory=list)
def __eq__(self, other):
return self.domain == other.domain
@@ -207,7 +220,7 @@ async def resolve_providers(manifests) -> tuple[Set, Set]:
providers = set()
for manifest in manifests:
provider = Provider(manifest.domain)
requirements = manifest.requirements + EXTRA_DEPS.get(manifest.domain, [])
requirements = manifest.requirements
for requirement in requirements:
# allow substituting requirement specifications that packaging cannot parse
if requirement in PACKAGE_MAP:
@@ -228,6 +241,12 @@ async def resolve_providers(manifests) -> tuple[Set, Set]:
version = await get_package_version(attr)
if version not in requirement.specifier:
errors.append(f"{requirement} not satisfied by version {version}")
if manifest.domain in EXTRA_DEPS:
for requirement in EXTRA_DEPS[manifest.domain]:
provider.available.append(requirement)
if manifest.domain in EXTRA_LIST_DEPS:
for requirement in EXTRA_LIST_DEPS[manifest.domain]:
provider.extra_list_deps.append(requirement)
providers.add(provider)
if errors:
print("\n - ", end="")