diff --git a/common/pc/default.nix b/common/pc/default.nix index 606b578..0c23eeb 100644 --- a/common/pc/default.nix +++ b/common/pc/default.nix @@ -86,6 +86,9 @@ in services.gnome.gnome-keyring.enable = true; security.pam.services.googlebot.enableGnomeKeyring = true; + # Spotify Connect discovery + networking.firewall.allowedTCPPorts = [ 57621 ]; + # Mount personal SMB stores services.mount-samba.enable = true; diff --git a/machines/storage/s0/home-automation.nix b/machines/storage/s0/home-automation.nix index 28c1057..4149cfc 100644 --- a/machines/storage/s0/home-automation.nix +++ b/machines/storage/s0/home-automation.nix @@ -24,6 +24,10 @@ # Music assistant (must be exposed so local devices can fetch the audio stream from it) 8095 8097 + + # Music assistant: Spotify Connect zeroconf discovery (one per librespot instance) + 44200 + 44201 ]; services.zigbee2mqtt = { diff --git a/overlays/default.nix b/overlays/default.nix index 5e2a1f1..9ffc29d 100644 --- a/overlays/default.nix +++ b/overlays/default.nix @@ -15,4 +15,12 @@ in incus-lts = prev.incus-lts.overrideAttrs (old: { nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ prev.writableTmpDirAsHomeHook ]; }); + + # Add --zeroconf-port support to Spotify Connect plugin so librespot + # binds to a fixed port that can be opened in the firewall. + music-assistant = prev.music-assistant.overrideAttrs (old: { + patches = (old.patches or [ ]) ++ [ + ../patches/music-assistant-zeroconf-port.patch + ]; + }); } diff --git a/patches/music-assistant-zeroconf-port.patch b/patches/music-assistant-zeroconf-port.patch new file mode 100644 index 0000000..9878619 --- /dev/null +++ b/patches/music-assistant-zeroconf-port.patch @@ -0,0 +1,40 @@ +diff --git a/music_assistant/providers/spotify_connect/__init__.py b/music_assistant/providers/spotify_connect/__init__.py +index 1111111..2222222 100644 +--- a/music_assistant/providers/spotify_connect/__init__.py ++++ b/music_assistant/providers/spotify_connect/__init__.py +@@ -51,6 +51,7 @@ CONNECT_ITEM_ID = "spotify_connect" + CONF_PUBLISH_NAME = "publish_name" + CONF_ALLOW_PLAYER_SWITCH = "allow_player_switch" + ++CONF_ZEROCONF_PORT = "zeroconf_port" + # Special value for auto player selection + PLAYER_ID_AUTO = "__auto__" + +@@ -117,6 +118,15 @@ async def get_config_entries( + description="How should this Spotify Connect device be named in the Spotify app?", + default_value="Music Assistant", + ), ++ ConfigEntry( ++ key=CONF_ZEROCONF_PORT, ++ type=ConfigEntryType.INTEGER, ++ label="Zeroconf port", ++ description="Fixed TCP port for Spotify Connect discovery (zeroconf). " ++ "Set to a specific port and open it in your firewall to allow " ++ "devices on the network to discover this player. 0 = random port.", ++ default_value=0, ++ ), + # ConfigEntry( + # key=CONF_HANDOFF_MODE, + # type=ConfigEntryType.BOOLEAN, +@@ -677,6 +687,11 @@ class SpotifyConnectProvider(PluginProvider): + "--onevent", + str(EVENTS_SCRIPT), + "--emit-sink-events", ++ *( ++ ["--zeroconf-port", str(zeroconf_port)] ++ if (zeroconf_port := int(self.config.get_value(CONF_ZEROCONF_PORT) or 0)) > 0 ++ else [] ++ ), + ] + self._librespot_proc = librespot = AsyncProcess( + args, stdout=False, stderr=True, name=f"librespot[{self.name}]", env=env