Loading nixos/modules/services/audio/music-assistant.nix +4 −0 Original line number Diff line number Diff line Loading @@ -125,6 +125,9 @@ in "AF_INET" "AF_INET6" "AF_NETLINK" ] ++ lib.optionals (lib.elem "snapcast" cfg.providers) [ "AF_UNIX" ]; RestrictNamespaces = true; RestrictRealtime = true; Loading @@ -132,6 +135,7 @@ in SystemCallFilter = [ "@system-service" "~@privileged @resources" "mbind" ] ++ lib.optionals useYTMusic [ "@pkey" Loading pkgs/by-name/mu/music-assistant/builtin-snapcast-server.patch +30 −7 Original line number Diff line number Diff line diff --git a/music_assistant/providers/snapcast/__init__.py b/music_assistant/providers/snapcast/__init__.py index 742d798183ec6fd1ac1fdbcad750912c222de5c3..8d0d95577d31f735977884280b08f384187774bd 100644 --- a/music_assistant/providers/snapcast/__init__.py +++ b/music_assistant/providers/snapcast/__init__.py @@ -87,7 +87,8 @@ DEFAULT_SNAPSTREAM_IDLE_THRESHOLD = 60000 diff --git a/music_assistant/providers/snapcast/constants.py b/music_assistant/providers/snapcast/constants.py index eb5ecb1a..92f03212 100644 --- a/music_assistant/providers/snapcast/constants.py +++ b/music_assistant/providers/snapcast/constants.py @@ -43,7 +43,8 @@ CONTROL_SOCKET_PATH_TEMPLATE = "/tmp/ma-snapcast-{queue_id}.sock" # noqa: S108 MASS_STREAM_PREFIX = "Music Assistant - " MASS_ANNOUNCEMENT_POSTFIX = " (announcement)" SNAPWEB_DIR = pathlib.Path(__file__).parent.resolve().joinpath("snapweb") Loading @@ -12,7 +12,19 @@ index 742d798183ec6fd1ac1fdbcad750912c222de5c3..8d0d95577d31f735977884280b08f384 DEFAULT_SNAPCAST_FORMAT = AudioFormat( content_type=ContentType.PCM_S16LE, @@ -866,6 +867,10 @@ class SnapCastProvider(PlayerProvider): diff --git a/music_assistant/providers/snapcast/provider.py b/music_assistant/providers/snapcast/provider.py index 94c197fc..9a344f53 100644 --- a/music_assistant/providers/snapcast/provider.py +++ b/music_assistant/providers/snapcast/provider.py @@ -32,6 +32,7 @@ from music_assistant.providers.snapcast.constants import ( CONF_STREAM_IDLE_THRESHOLD, CONF_USE_EXTERNAL_SERVER, CONTROL_SCRIPT, + CONTROL_SCRIPT_DIR, CONTROL_SOCKET_PATH_TEMPLATE, DEFAULT_SNAPSERVER_PORT, SNAPWEB_DIR, @@ -215,6 +216,10 @@ class SnapCastProvider(PlayerProvider): args = [ "snapserver", Loading @@ -23,7 +35,7 @@ index 742d798183ec6fd1ac1fdbcad750912c222de5c3..8d0d95577d31f735977884280b08f384 # config settings taken from # https://raw.githubusercontent.com/badaix/snapcast/86cd4b2b63e750a72e0dfe6a46d47caf01426c8d/server/etc/snapserver.conf f"--server.datadir={self.mass.storage_path}", @@ -878,6 +883,7 @@ class SnapCastProvider(PlayerProvider): @@ -227,6 +232,7 @@ class SnapCastProvider(PlayerProvider): f"--stream.buffer={self._snapcast_server_buffer_size}", f"--stream.chunk_ms={self._snapcast_server_chunk_ms}", f"--stream.codec={self._snapcast_server_transport_codec}", Loading @@ -31,3 +43,14 @@ index 742d798183ec6fd1ac1fdbcad750912c222de5c3..8d0d95577d31f735977884280b08f384 f"--stream.send_to_muted={str(self._snapcast_server_send_to_muted).lower()}", f"--streaming_client.initial_volume={self._snapcast_server_initial_volume}", ] @@ -243,9 +249,7 @@ class SnapCastProvider(PlayerProvider): # Copy control script after snapserver starts # (run in executor to avoid blocking) loop = asyncio.get_running_loop() - self._controlscript_available = await loop.run_in_executor( - None, self._setup_controlscript - ) + self._controlscript_available = True def _get_ma_id(self, snap_client_id: str) -> str: search_dict = self._ids_map.inverse pkgs/by-name/mu/music-assistant/dont-install-deps.patch +13 −4 Original line number Diff line number Diff line diff --git a/music_assistant/helpers/util.py b/music_assistant/helpers/util.py index 74540dd3..14f8f864 100644 index bc14912b..0527a831 100644 --- a/music_assistant/helpers/util.py +++ b/music_assistant/helpers/util.py @@ -434,30 +434,11 @@ async def load_provider_module(domain: str, requirements: list[str]) -> Provider @@ -495,30 +495,11 @@ async def load_provider_module(domain: str, requirements: list[str]) -> Provider "ProviderModuleType", importlib.import_module(f".{domain}", "music_assistant.providers") ) Loading Loading @@ -35,10 +35,10 @@ index 74540dd3..14f8f864 100644 async def has_tmpfs_mount() -> bool: """Check if we have a tmpfs mount.""" diff --git a/music_assistant/providers/ytmusic/__init__.py b/music_assistant/providers/ytmusic/__init__.py index 52a7544a..816d0425 100644 index 9cc5981a..dc424b0b 100644 --- a/music_assistant/providers/ytmusic/__init__.py +++ b/music_assistant/providers/ytmusic/__init__.py @@ -197,7 +197,6 @@ class YoutubeMusicProvider(MusicProvider): @@ -200,7 +200,6 @@ class YoutubeMusicProvider(MusicProvider): async def handle_async_init(self) -> None: """Set up the YTMusic provider.""" logging.getLogger("yt_dlp").setLevel(self.logger.level + 10) Loading @@ -46,3 +46,12 @@ index 52a7544a..816d0425 100644 self._cookie = 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 @@ -1091,8 +1090,6 @@ class YoutubeMusicProvider(MusicProvider): # NOTE: Google breaks things quite often which requires us to update # some packages very frequently. Installing them dynamically prevents # us from having to update MA to ensure this provider works. - for package_name in PACKAGES_TO_INSTALL: - await install_package(package_name) # verify if the yt_dlp package is usable try: await asyncio.to_thread(importlib.import_module, "yt_dlp") pkgs/by-name/mu/music-assistant/ffmpeg.patch +33 −20 Original line number Diff line number Diff line diff --git a/music_assistant/helpers/audio.py b/music_assistant/helpers/audio.py index 482ab8e8..a02fc435 100644 --- a/music_assistant/helpers/audio.py +++ b/music_assistant/helpers/audio.py @@ -80,7 +80,7 @@ async def crossfade_pcm_parts( diff --git a/music_assistant/controllers/streams/smart_fades/fades.py b/music_assistant/controllers/streams/smart_fades/fades.py index 03c11ef9..460d53a6 100644 --- a/music_assistant/controllers/streams/smart_fades/fades.py +++ b/music_assistant/controllers/streams/smart_fades/fades.py @@ -76,7 +76,7 @@ class SmartFade(ABC): async with aiofiles.open(fadeout_filename, "wb") as outfile: await outfile.write(fade_out_part) args = [ # generic args - "ffmpeg", + "@ffmpeg@", "-hide_banner", "-loglevel", "quiet", @@ -141,7 +141,7 @@ async def strip_silence( "error", diff --git a/music_assistant/helpers/audio.py b/music_assistant/helpers/audio.py index 0674b009..53158f4d 100644 --- a/music_assistant/helpers/audio.py +++ b/music_assistant/helpers/audio.py @@ -103,7 +103,7 @@ async def strip_silence( reverse: bool = False, ) -> bytes: """Strip silence from begin or end of pcm audio using ffmpeg.""" Loading @@ -20,7 +24,7 @@ index 482ab8e8..a02fc435 100644 args += [ "-acodec", pcm_format.content_type.name.lower(), @@ -912,7 +912,7 @@ async def get_silence( @@ -1134,7 +1134,7 @@ async def get_silence( return # use ffmpeg for all other encodings args = [ Loading @@ -30,11 +34,11 @@ index 482ab8e8..a02fc435 100644 "-loglevel", "quiet", diff --git a/music_assistant/helpers/ffmpeg.py b/music_assistant/helpers/ffmpeg.py index 7c1c8d83..501a7370 100644 index 9ea5ea0c..51b1d62b 100644 --- a/music_assistant/helpers/ffmpeg.py +++ b/music_assistant/helpers/ffmpeg.py @@ -218,7 +218,7 @@ def get_ffmpeg_args( extra_args = [] @@ -249,7 +249,7 @@ def get_ffmpeg_args( # noqa: PLR0915 extra_output_args = [] # generic args generic_args = [ - "ffmpeg", Loading @@ -42,7 +46,7 @@ index 7c1c8d83..501a7370 100644 "-hide_banner", "-loglevel", loglevel, @@ -370,7 +370,7 @@ async def check_ffmpeg_version() -> None: @@ -416,7 +416,7 @@ async def check_ffmpeg_version() -> None: """Check if ffmpeg is present (with libsoxr support).""" # check for FFmpeg presence try: Loading @@ -52,10 +56,10 @@ index 7c1c8d83..501a7370 100644 raise AudioError( "FFmpeg binary is missing from system. " diff --git a/music_assistant/helpers/tags.py b/music_assistant/helpers/tags.py index 06c8bcb5..a703aacd 100644 index fe83301c..d35760b6 100644 --- a/music_assistant/helpers/tags.py +++ b/music_assistant/helpers/tags.py @@ -438,7 +438,7 @@ def parse_tags(input_file: str, file_size: int | None = None) -> AudioTags: @@ -484,7 +484,7 @@ def parse_tags( Input_file may be a (local) filename or URL accessible by ffmpeg. """ args = ( Loading @@ -64,11 +68,20 @@ index 06c8bcb5..a703aacd 100644 "-hide_banner", "-loglevel", "fatal", @@ -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. @@ -551,7 +551,7 @@ def get_file_duration(input_file: str) -> float: NOT Async friendly. """ args = ( - "ffmpeg", + "@ffmpeg@", "-hide_banner", "-loglevel", "info", @@ -707,7 +707,7 @@ async def get_embedded_image(input_file: str) -> bytes | None: # Use FFmpeg for all other cases (URLs, ID3 tags, Vorbis comments, etc.) args = [ - "ffmpeg", + "@ffmpeg@", "-hide_banner", "-loglevel", Loading pkgs/by-name/mu/music-assistant/fix-webserver-tests-in-sandbox.patch 0 → 100644 +13 −0 Original line number Diff line number Diff line diff --git a/music_assistant/helpers/util.py b/music_assistant/helpers/util.py index bc14912b..d207b855 100644 --- a/music_assistant/helpers/util.py +++ b/music_assistant/helpers/util.py @@ -274,7 +274,7 @@ async def get_ip_addresses(include_ipv6: bool = False) -> tuple[str, ...]: if ip.is_IPv6 and not include_ipv6: continue ip_str = str(ip.ip) - if ip_str.startswith(("127", "169.254")): + if ip_str.startswith(("127", "169.254")) and "PYTEST_VERSION" not in os.environ: # filter out IPv4 loopback/APIPA address continue if ip_str.startswith(("::1", "::ffff:", "fe80")): Loading
nixos/modules/services/audio/music-assistant.nix +4 −0 Original line number Diff line number Diff line Loading @@ -125,6 +125,9 @@ in "AF_INET" "AF_INET6" "AF_NETLINK" ] ++ lib.optionals (lib.elem "snapcast" cfg.providers) [ "AF_UNIX" ]; RestrictNamespaces = true; RestrictRealtime = true; Loading @@ -132,6 +135,7 @@ in SystemCallFilter = [ "@system-service" "~@privileged @resources" "mbind" ] ++ lib.optionals useYTMusic [ "@pkey" Loading
pkgs/by-name/mu/music-assistant/builtin-snapcast-server.patch +30 −7 Original line number Diff line number Diff line diff --git a/music_assistant/providers/snapcast/__init__.py b/music_assistant/providers/snapcast/__init__.py index 742d798183ec6fd1ac1fdbcad750912c222de5c3..8d0d95577d31f735977884280b08f384187774bd 100644 --- a/music_assistant/providers/snapcast/__init__.py +++ b/music_assistant/providers/snapcast/__init__.py @@ -87,7 +87,8 @@ DEFAULT_SNAPSTREAM_IDLE_THRESHOLD = 60000 diff --git a/music_assistant/providers/snapcast/constants.py b/music_assistant/providers/snapcast/constants.py index eb5ecb1a..92f03212 100644 --- a/music_assistant/providers/snapcast/constants.py +++ b/music_assistant/providers/snapcast/constants.py @@ -43,7 +43,8 @@ CONTROL_SOCKET_PATH_TEMPLATE = "/tmp/ma-snapcast-{queue_id}.sock" # noqa: S108 MASS_STREAM_PREFIX = "Music Assistant - " MASS_ANNOUNCEMENT_POSTFIX = " (announcement)" SNAPWEB_DIR = pathlib.Path(__file__).parent.resolve().joinpath("snapweb") Loading @@ -12,7 +12,19 @@ index 742d798183ec6fd1ac1fdbcad750912c222de5c3..8d0d95577d31f735977884280b08f384 DEFAULT_SNAPCAST_FORMAT = AudioFormat( content_type=ContentType.PCM_S16LE, @@ -866,6 +867,10 @@ class SnapCastProvider(PlayerProvider): diff --git a/music_assistant/providers/snapcast/provider.py b/music_assistant/providers/snapcast/provider.py index 94c197fc..9a344f53 100644 --- a/music_assistant/providers/snapcast/provider.py +++ b/music_assistant/providers/snapcast/provider.py @@ -32,6 +32,7 @@ from music_assistant.providers.snapcast.constants import ( CONF_STREAM_IDLE_THRESHOLD, CONF_USE_EXTERNAL_SERVER, CONTROL_SCRIPT, + CONTROL_SCRIPT_DIR, CONTROL_SOCKET_PATH_TEMPLATE, DEFAULT_SNAPSERVER_PORT, SNAPWEB_DIR, @@ -215,6 +216,10 @@ class SnapCastProvider(PlayerProvider): args = [ "snapserver", Loading @@ -23,7 +35,7 @@ index 742d798183ec6fd1ac1fdbcad750912c222de5c3..8d0d95577d31f735977884280b08f384 # config settings taken from # https://raw.githubusercontent.com/badaix/snapcast/86cd4b2b63e750a72e0dfe6a46d47caf01426c8d/server/etc/snapserver.conf f"--server.datadir={self.mass.storage_path}", @@ -878,6 +883,7 @@ class SnapCastProvider(PlayerProvider): @@ -227,6 +232,7 @@ class SnapCastProvider(PlayerProvider): f"--stream.buffer={self._snapcast_server_buffer_size}", f"--stream.chunk_ms={self._snapcast_server_chunk_ms}", f"--stream.codec={self._snapcast_server_transport_codec}", Loading @@ -31,3 +43,14 @@ index 742d798183ec6fd1ac1fdbcad750912c222de5c3..8d0d95577d31f735977884280b08f384 f"--stream.send_to_muted={str(self._snapcast_server_send_to_muted).lower()}", f"--streaming_client.initial_volume={self._snapcast_server_initial_volume}", ] @@ -243,9 +249,7 @@ class SnapCastProvider(PlayerProvider): # Copy control script after snapserver starts # (run in executor to avoid blocking) loop = asyncio.get_running_loop() - self._controlscript_available = await loop.run_in_executor( - None, self._setup_controlscript - ) + self._controlscript_available = True def _get_ma_id(self, snap_client_id: str) -> str: search_dict = self._ids_map.inverse
pkgs/by-name/mu/music-assistant/dont-install-deps.patch +13 −4 Original line number Diff line number Diff line diff --git a/music_assistant/helpers/util.py b/music_assistant/helpers/util.py index 74540dd3..14f8f864 100644 index bc14912b..0527a831 100644 --- a/music_assistant/helpers/util.py +++ b/music_assistant/helpers/util.py @@ -434,30 +434,11 @@ async def load_provider_module(domain: str, requirements: list[str]) -> Provider @@ -495,30 +495,11 @@ async def load_provider_module(domain: str, requirements: list[str]) -> Provider "ProviderModuleType", importlib.import_module(f".{domain}", "music_assistant.providers") ) Loading Loading @@ -35,10 +35,10 @@ index 74540dd3..14f8f864 100644 async def has_tmpfs_mount() -> bool: """Check if we have a tmpfs mount.""" diff --git a/music_assistant/providers/ytmusic/__init__.py b/music_assistant/providers/ytmusic/__init__.py index 52a7544a..816d0425 100644 index 9cc5981a..dc424b0b 100644 --- a/music_assistant/providers/ytmusic/__init__.py +++ b/music_assistant/providers/ytmusic/__init__.py @@ -197,7 +197,6 @@ class YoutubeMusicProvider(MusicProvider): @@ -200,7 +200,6 @@ class YoutubeMusicProvider(MusicProvider): async def handle_async_init(self) -> None: """Set up the YTMusic provider.""" logging.getLogger("yt_dlp").setLevel(self.logger.level + 10) Loading @@ -46,3 +46,12 @@ index 52a7544a..816d0425 100644 self._cookie = 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 @@ -1091,8 +1090,6 @@ class YoutubeMusicProvider(MusicProvider): # NOTE: Google breaks things quite often which requires us to update # some packages very frequently. Installing them dynamically prevents # us from having to update MA to ensure this provider works. - for package_name in PACKAGES_TO_INSTALL: - await install_package(package_name) # verify if the yt_dlp package is usable try: await asyncio.to_thread(importlib.import_module, "yt_dlp")
pkgs/by-name/mu/music-assistant/ffmpeg.patch +33 −20 Original line number Diff line number Diff line diff --git a/music_assistant/helpers/audio.py b/music_assistant/helpers/audio.py index 482ab8e8..a02fc435 100644 --- a/music_assistant/helpers/audio.py +++ b/music_assistant/helpers/audio.py @@ -80,7 +80,7 @@ async def crossfade_pcm_parts( diff --git a/music_assistant/controllers/streams/smart_fades/fades.py b/music_assistant/controllers/streams/smart_fades/fades.py index 03c11ef9..460d53a6 100644 --- a/music_assistant/controllers/streams/smart_fades/fades.py +++ b/music_assistant/controllers/streams/smart_fades/fades.py @@ -76,7 +76,7 @@ class SmartFade(ABC): async with aiofiles.open(fadeout_filename, "wb") as outfile: await outfile.write(fade_out_part) args = [ # generic args - "ffmpeg", + "@ffmpeg@", "-hide_banner", "-loglevel", "quiet", @@ -141,7 +141,7 @@ async def strip_silence( "error", diff --git a/music_assistant/helpers/audio.py b/music_assistant/helpers/audio.py index 0674b009..53158f4d 100644 --- a/music_assistant/helpers/audio.py +++ b/music_assistant/helpers/audio.py @@ -103,7 +103,7 @@ async def strip_silence( reverse: bool = False, ) -> bytes: """Strip silence from begin or end of pcm audio using ffmpeg.""" Loading @@ -20,7 +24,7 @@ index 482ab8e8..a02fc435 100644 args += [ "-acodec", pcm_format.content_type.name.lower(), @@ -912,7 +912,7 @@ async def get_silence( @@ -1134,7 +1134,7 @@ async def get_silence( return # use ffmpeg for all other encodings args = [ Loading @@ -30,11 +34,11 @@ index 482ab8e8..a02fc435 100644 "-loglevel", "quiet", diff --git a/music_assistant/helpers/ffmpeg.py b/music_assistant/helpers/ffmpeg.py index 7c1c8d83..501a7370 100644 index 9ea5ea0c..51b1d62b 100644 --- a/music_assistant/helpers/ffmpeg.py +++ b/music_assistant/helpers/ffmpeg.py @@ -218,7 +218,7 @@ def get_ffmpeg_args( extra_args = [] @@ -249,7 +249,7 @@ def get_ffmpeg_args( # noqa: PLR0915 extra_output_args = [] # generic args generic_args = [ - "ffmpeg", Loading @@ -42,7 +46,7 @@ index 7c1c8d83..501a7370 100644 "-hide_banner", "-loglevel", loglevel, @@ -370,7 +370,7 @@ async def check_ffmpeg_version() -> None: @@ -416,7 +416,7 @@ async def check_ffmpeg_version() -> None: """Check if ffmpeg is present (with libsoxr support).""" # check for FFmpeg presence try: Loading @@ -52,10 +56,10 @@ index 7c1c8d83..501a7370 100644 raise AudioError( "FFmpeg binary is missing from system. " diff --git a/music_assistant/helpers/tags.py b/music_assistant/helpers/tags.py index 06c8bcb5..a703aacd 100644 index fe83301c..d35760b6 100644 --- a/music_assistant/helpers/tags.py +++ b/music_assistant/helpers/tags.py @@ -438,7 +438,7 @@ def parse_tags(input_file: str, file_size: int | None = None) -> AudioTags: @@ -484,7 +484,7 @@ def parse_tags( Input_file may be a (local) filename or URL accessible by ffmpeg. """ args = ( Loading @@ -64,11 +68,20 @@ index 06c8bcb5..a703aacd 100644 "-hide_banner", "-loglevel", "fatal", @@ -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. @@ -551,7 +551,7 @@ def get_file_duration(input_file: str) -> float: NOT Async friendly. """ args = ( - "ffmpeg", + "@ffmpeg@", "-hide_banner", "-loglevel", "info", @@ -707,7 +707,7 @@ async def get_embedded_image(input_file: str) -> bytes | None: # Use FFmpeg for all other cases (URLs, ID3 tags, Vorbis comments, etc.) args = [ - "ffmpeg", + "@ffmpeg@", "-hide_banner", "-loglevel", Loading
pkgs/by-name/mu/music-assistant/fix-webserver-tests-in-sandbox.patch 0 → 100644 +13 −0 Original line number Diff line number Diff line diff --git a/music_assistant/helpers/util.py b/music_assistant/helpers/util.py index bc14912b..d207b855 100644 --- a/music_assistant/helpers/util.py +++ b/music_assistant/helpers/util.py @@ -274,7 +274,7 @@ async def get_ip_addresses(include_ipv6: bool = False) -> tuple[str, ...]: if ip.is_IPv6 and not include_ipv6: continue ip_str = str(ip.ip) - if ip_str.startswith(("127", "169.254")): + if ip_str.startswith(("127", "169.254")) and "PYTEST_VERSION" not in os.environ: # filter out IPv4 loopback/APIPA address continue if ip_str.startswith(("::1", "::ffff:", "fe80")):