diff --git a/pkgs/by-name/mu/music-assistant/dont-install-deps.patch b/pkgs/by-name/mu/music-assistant/dont-install-deps.patch index 652a4a072bcd..5281f578bedf 100644 --- a/pkgs/by-name/mu/music-assistant/dont-install-deps.patch +++ b/pkgs/by-name/mu/music-assistant/dont-install-deps.patch @@ -1,8 +1,8 @@ diff --git a/music_assistant/helpers/util.py b/music_assistant/helpers/util.py -index b6e5f2b4..0edead16 100644 +index 8daf159d..af5a6f38 100644 --- a/music_assistant/helpers/util.py +++ b/music_assistant/helpers/util.py -@@ -424,29 +424,11 @@ async def load_provider_module(domain: str, requirements: list[str]) -> Provider +@@ -429,30 +429,11 @@ async def load_provider_module(domain: str, requirements: list[str]) -> Provider def _get_provider_module(domain: str) -> ProviderModuleType: return importlib.import_module(f".{domain}", "music_assistant.providers") @@ -29,7 +29,8 @@ index b6e5f2b4..0edead16 100644 - # try loading the provider again to be safe - # this will fail if something else is wrong (as it should) - return await asyncio.to_thread(_get_provider_module, domain) -+ raise RuntimeError(f"Missing dependencies for provider {domain}") - +- ++ raise RuntimeError(f"Missing dependencies for provider {domain}.") def create_tempfile(): + """Return a (named) temporary file.""" diff --git a/pkgs/by-name/mu/music-assistant/ffmpeg.patch b/pkgs/by-name/mu/music-assistant/ffmpeg.patch index 158451304a34..e0f8f1360979 100644 --- a/pkgs/by-name/mu/music-assistant/ffmpeg.patch +++ b/pkgs/by-name/mu/music-assistant/ffmpeg.patch @@ -1,8 +1,8 @@ diff --git a/music_assistant/helpers/audio.py b/music_assistant/helpers/audio.py -index dad3c5db..d1398d66 100644 +index 482ab8e8..a02fc435 100644 --- a/music_assistant/helpers/audio.py +++ b/music_assistant/helpers/audio.py -@@ -74,7 +74,7 @@ async def crossfade_pcm_parts( +@@ -80,7 +80,7 @@ async def crossfade_pcm_parts( await outfile.write(fade_out_part) args = [ # generic args @@ -11,7 +11,7 @@ index dad3c5db..d1398d66 100644 "-hide_banner", "-loglevel", "quiet", -@@ -135,7 +135,7 @@ async def strip_silence( +@@ -141,7 +141,7 @@ async def strip_silence( reverse: bool = False, ) -> bytes: """Strip silence from begin or end of pcm audio using ffmpeg.""" @@ -20,16 +20,7 @@ index dad3c5db..d1398d66 100644 args += [ "-acodec", pcm_format.content_type.name.lower(), -@@ -734,7 +734,7 @@ async def get_file_stream( - async def check_audio_support() -> tuple[bool, bool, str]: - """Check if ffmpeg is present (with/without libsoxr support).""" - # check for FFmpeg presence -- returncode, output = await check_output("ffmpeg", "-version") -+ returncode, output = await check_output("@ffmpeg@", "-version") - ffmpeg_present = returncode == 0 and "FFmpeg" in output.decode() - - # use globals as in-memory cache -@@ -789,7 +789,7 @@ async def get_silence( +@@ -912,7 +912,7 @@ async def get_silence( return # use ffmpeg for all other encodings args = [ @@ -39,11 +30,11 @@ index dad3c5db..d1398d66 100644 "-loglevel", "quiet", diff --git a/music_assistant/helpers/ffmpeg.py b/music_assistant/helpers/ffmpeg.py -index 0405fc27..570f9157 100644 +index 7c1c8d83..501a7370 100644 --- a/music_assistant/helpers/ffmpeg.py +++ b/music_assistant/helpers/ffmpeg.py -@@ -213,7 +213,7 @@ def get_ffmpeg_args( # noqa: PLR0915 - +@@ -218,7 +218,7 @@ def get_ffmpeg_args( + extra_args = [] # generic args generic_args = [ - "ffmpeg", @@ -51,11 +42,20 @@ index 0405fc27..570f9157 100644 "-hide_banner", "-loglevel", loglevel, +@@ -370,7 +370,7 @@ async def check_ffmpeg_version() -> None: + """Check if ffmpeg is present (with libsoxr support).""" + # check for FFmpeg presence + try: +- returncode, output = await check_output("ffmpeg", "-version") ++ returncode, output = await check_output("@ffmpeg@", "-version") + except FileNotFoundError: + raise AudioError( + "FFmpeg binary is missing from system. " diff --git a/music_assistant/helpers/tags.py b/music_assistant/helpers/tags.py -index 55def74b..7c26e13d 100644 +index 06c8bcb5..a703aacd 100644 --- a/music_assistant/helpers/tags.py +++ b/music_assistant/helpers/tags.py -@@ -387,7 +387,7 @@ async def parse_tags(input_file: str, file_size: int | None = None) -> AudioTags +@@ -438,7 +438,7 @@ def parse_tags(input_file: str, file_size: int | None = None) -> AudioTags: Input_file may be a (local) filename or URL accessible by ffmpeg. """ args = ( @@ -64,7 +64,7 @@ index 55def74b..7c26e13d 100644 "-hide_banner", "-loglevel", "fatal", -@@ -448,7 +448,7 @@ async def get_embedded_image(input_file: str) -> bytes | None: +@@ -553,7 +553,7 @@ async def get_embedded_image(input_file: str) -> bytes | None: Input_file may be a (local) filename or URL accessible by ffmpeg. """ args = ( diff --git a/pkgs/by-name/mu/music-assistant/frontend.nix b/pkgs/by-name/mu/music-assistant/frontend.nix index 6e62e6a5e70a..4a3a70d31a1d 100644 --- a/pkgs/by-name/mu/music-assistant/frontend.nix +++ b/pkgs/by-name/mu/music-assistant/frontend.nix @@ -7,12 +7,12 @@ buildPythonPackage rec { pname = "music-assistant-frontend"; - version = "2.9.16"; + version = "2.11.11"; pyproject = true; src = fetchPypi { inherit pname version; - hash = "sha256-bUtclj8GMq1JdC4tYNETl+l+TNF91y4yTANSpuOOm70="; + hash = "sha256-fPFszZfJjdc2KTQipeb2KVbIwWwL56bqFGba1LSJXuQ="; }; postPatch = '' diff --git a/pkgs/by-name/mu/music-assistant/librespot.patch b/pkgs/by-name/mu/music-assistant/librespot.patch new file mode 100644 index 000000000000..95b135a46b15 --- /dev/null +++ b/pkgs/by-name/mu/music-assistant/librespot.patch @@ -0,0 +1,29 @@ +diff --git a/music_assistant/providers/spotify/helpers.py b/music_assistant/providers/spotify/helpers.py +index 8b6c4e78..20c2a269 100644 +--- a/music_assistant/providers/spotify/helpers.py ++++ b/music_assistant/providers/spotify/helpers.py +@@ -11,23 +11,4 @@ from music_assistant.helpers.process import check_output + async def get_librespot_binary() -> str: + """Find the correct librespot binary belonging to the platform.""" + +- # ruff: noqa: SIM102 +- async def check_librespot(librespot_path: str) -> str | None: +- try: +- returncode, output = await check_output(librespot_path, "--version") +- if returncode == 0 and b"librespot" in output: +- return librespot_path +- except OSError: +- 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_librespot( +- os.path.join(base_path, f"librespot-{system}-{architecture}") +- ): +- return bridge_binary +- +- msg = f"Unable to locate Librespot for {system}/{architecture}" +- raise RuntimeError(msg) ++ return "@librespot@" diff --git a/pkgs/by-name/mu/music-assistant/package.nix b/pkgs/by-name/mu/music-assistant/package.nix index c35d0addd85e..382b337bfd53 100644 --- a/pkgs/by-name/mu/music-assistant/package.nix +++ b/pkgs/by-name/mu/music-assistant/package.nix @@ -3,6 +3,7 @@ python3, fetchFromGitHub, ffmpeg-headless, + librespot, nixosTests, replaceVars, providers ? [ ], @@ -12,27 +13,16 @@ let python = python3.override { self = python; packageOverrides = self: super: { - aiojellyfin = super.aiojellyfin.overridePythonAttrs (oldAttrs: rec { - version = "0.10.1"; - - src = fetchFromGitHub { - owner = "Jc2k"; - repo = "aiojellyfin"; - tag = "v${version}"; - hash = "sha256-A+uvM1/7HntRMIdknfHr0TMGIjHk7BCwsZopXdVoEO8="; - }; - }); - music-assistant-frontend = self.callPackage ./frontend.nix { }; music-assistant-models = super.music-assistant-models.overridePythonAttrs (oldAttrs: rec { - version = "1.1.4"; + version = "1.1.30"; src = fetchFromGitHub { owner = "music-assistant"; repo = "models"; tag = version; - hash = "sha256-keig18o32X53q/QcoaPO0o9AT4XTEZ+dQ3L6u6BVkLU="; + hash = "sha256-ZLTRHarjVFAk+tYPkgLm192rE+C82vNzqs8PmJhGSeg="; }; postPatch = '' @@ -54,14 +44,14 @@ in python.pkgs.buildPythonApplication rec { pname = "music-assistant"; - version = "2.3.6"; + version = "2.4.2"; pyproject = true; src = fetchFromGitHub { owner = "music-assistant"; repo = "server"; tag = version; - hash = "sha256-CSGpG1E4ou1TGz/S1mXFHyk49p7dStEwxUTB+xxfNEc="; + hash = "sha256-5FIIXIn4tEz6w/uAh6PGkU4tU+mz7Jpb3+bq1mRNr2Y="; }; patches = [ @@ -69,6 +59,9 @@ python.pkgs.buildPythonApplication rec { ffmpeg = "${lib.getBin ffmpeg-headless}/bin/ffmpeg"; ffprobe = "${lib.getBin ffmpeg-headless}/bin/ffprobe"; }) + (replaceVars ./librespot.patch { + librespot = lib.getExe librespot; + }) # Disable interactive dependency resolution, which clashes with the immutable Python environment ./dont-install-deps.patch @@ -77,6 +70,8 @@ python.pkgs.buildPythonApplication rec { postPatch = '' substituteInPlace pyproject.toml \ --replace-fail "0.0.0" "${version}" + + rm -rv music_assistant/providers/spotify/bin ''; build-system = with python.pkgs; [ @@ -85,6 +80,7 @@ python.pkgs.buildPythonApplication rec { pythonRelaxDeps = [ "aiohttp" + "aiosqlite" "certifi" "colorlog" "cryptography" @@ -123,8 +119,10 @@ python.pkgs.buildPythonApplication rec { memory-tempfile music-assistant-frontend music-assistant-models + mutagen orjson pillow + podcastparser python-slugify shortuuid unidecode @@ -136,7 +134,6 @@ python.pkgs.buildPythonApplication rec { nativeCheckInputs = with python.pkgs; [ - aiojellyfin pytest-aiohttp pytest-cov-stub pytest-timeout @@ -144,7 +141,9 @@ python.pkgs.buildPythonApplication rec { syrupy pytest-timeout ] - ++ lib.flatten (lib.attrValues optional-dependencies); + ++ lib.flatten (lib.attrValues optional-dependencies) + ++ (providerPackages.jellyfin python.pkgs) + ++ (providerPackages.opensubsonic python.pkgs); pytestFlagsArray = [ # blocks in poll() diff --git a/pkgs/by-name/mu/music-assistant/providers.nix b/pkgs/by-name/mu/music-assistant/providers.nix index 52249470f2ab..919e4ad3cd0d 100644 --- a/pkgs/by-name/mu/music-assistant/providers.nix +++ b/pkgs/by-name/mu/music-assistant/providers.nix @@ -1,12 +1,20 @@ # Do not edit manually, run ./update-providers.py { - version = "2.3.6"; + version = "2.4.2"; providers = { airplay = ps: [ ]; apple_music = ps: [ ]; # missing pywidevine + audible = + ps: with ps; [ + audible + ]; + audiobookshelf = + ps: with ps; [ + aioaudiobookshelf + ]; bluesound = ps: with ps; [ pyblu @@ -19,8 +27,9 @@ ]; deezer = ps: with ps; [ + deezer-python-async pycryptodome - ]; # missing deezer-python-async + ]; dlna = ps: with ps; [ async-upnp-client @@ -41,6 +50,8 @@ ]; hass_players = ps: [ ]; + ibroadcast = ps: [ + ]; # missing ibroadcastaio jellyfin = ps: with ps; [ aiojellyfin @@ -57,6 +68,10 @@ ps: with ps; [ plexapi ]; + podcastfeed = + ps: with ps; [ + podcastparser + ]; qobuz = ps: [ ]; radiobrowser = @@ -87,6 +102,8 @@ ps: with ps; [ pkce ]; + spotify_connect = ps: [ + ]; template_player_provider = ps: [ ]; test = ps: [ @@ -101,6 +118,7 @@ ]; ytmusic = ps: with ps; [ + duration-parser yt-dlp ytmusicapi ];