mode = "optimized" if self._using_ha_cloud else "basic"
self.logger.info("Starting remote access in %s mode", mode)
+ sendspin_url = f"ws://{self.mass.streams.publish_ip}:8927/sendspin"
+
self.gateway = WebRTCGateway(
http_session=self.mass.http_session,
remote_id=self._remote_id,
certificate=self._certificate,
signaling_url=SIGNALING_SERVER_URL,
local_ws_url=local_ws_url,
+ sendspin_url=sendspin_url,
ice_servers=ice_servers,
# Pass callback to get fresh ICE servers for each client connection
# This ensures TURN credentials are always valid
certificate: RTCCertificate,
signaling_url: str = "wss://signaling.music-assistant.io/ws",
local_ws_url: str = "ws://localhost:8095/ws",
+ sendspin_url: str = "ws://localhost:8927/sendspin",
ice_servers: list[dict[str, Any]] | None = None,
ice_servers_callback: Callable[[], Awaitable[list[dict[str, Any]]]] | None = None,
) -> None:
:param certificate: Persistent RTCCertificate for DTLS, enabling client-side pinning.
:param signaling_url: WebSocket URL of the signaling server.
:param local_ws_url: Local WebSocket URL to bridge to.
+ :param sendspin_url: Internal Sendspin WebSocket URL to bridge to.
:param ice_servers: List of ICE server configurations (used at registration time).
:param ice_servers_callback: Optional callback to fetch fresh ICE servers for each session.
"""
self.http_session = http_session
self.signaling_url = signaling_url
self.local_ws_url = local_ws_url
+ self.sendspin_url = sendspin_url
self._remote_id = remote_id
self._certificate = certificate
self.logger = LOGGER
if session.sendspin_ws and not session.sendspin_ws.closed:
asyncio.run_coroutine_threadsafe(session.sendspin_ws.close(), loop)
- session.sendspin_ws = await self.http_session.ws_connect("ws://127.0.0.1:8927/sendspin")
+ session.sendspin_ws = await self.http_session.ws_connect(self.sendspin_url)
self.logger.debug("Sendspin channel connected for session %s", session.session_id)
# Start forwarding tasks - queued messages will be processed
from music_assistant.controllers.webserver import WebserverController
LOGGER = logging.getLogger(f"{MASS_LOGGER_NAME}.sendspin_proxy")
-INTERNAL_SENDSPIN_URL = "ws://127.0.0.1:8927/sendspin"
class SendspinProxyHandler:
self.mass = webserver.mass
self.logger = LOGGER
+ @property
+ def internal_sendspin_url(self) -> str:
+ """Return the internal sendspin URL for connecting to the internal Sendspin server."""
+ return f"ws://{self.mass.streams.publish_ip}:8927/sendspin"
+
async def handle_sendspin_proxy(self, request: web.Request) -> web.WebSocketResponse:
"""
Handle incoming WebSocket connection and proxy to internal Sendspin server.
return wsock
try:
- internal_ws = await self.mass.http_session.ws_connect(INTERNAL_SENDSPIN_URL)
+ internal_ws = await self.mass.http_session.ws_connect(self.internal_sendspin_url)
except Exception:
self.logger.exception("Failed to connect to internal Sendspin server")
await wsock.close(code=1011, message=b"Internal server error")