From 246f2b2e63fc42c314c5b4282bb0509275467bad Mon Sep 17 00:00:00 2001 From: Maxim Raznatovski Date: Mon, 12 Jan 2026 13:22:42 +0100 Subject: [PATCH] Sendspin web player race condition losing `client/hello` (#2946) fix: Sendspin DataChannel race condition losing `client/hello` Register message/close handlers before ws_connect() to ensure early messages are queued. Change condition to also queue when task is None (during setup), not just when running. --- .../webserver/remote_access/gateway.py | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/music_assistant/controllers/webserver/remote_access/gateway.py b/music_assistant/controllers/webserver/remote_access/gateway.py index 8d6343c3..4ded3555 100644 --- a/music_assistant/controllers/webserver/remote_access/gateway.py +++ b/music_assistant/controllers/webserver/remote_access/gateway.py @@ -712,21 +712,16 @@ class WebRTCGateway: return try: - session.sendspin_ws = await self.http_session.ws_connect("ws://127.0.0.1:8927/sendspin") - self.logger.debug("Sendspin channel connected for session %s", session.session_id) - loop = asyncio.get_event_loop() - session.sendspin_to_local_task = asyncio.create_task( - self._forward_sendspin_to_local(session) - ) - session.sendspin_from_local_task = asyncio.create_task( - self._forward_sendspin_from_local(session) - ) - @channel.on("message") # type: ignore[untyped-decorator] def on_message(message: str | bytes) -> None: - if session.sendspin_to_local_task and not session.sendspin_to_local_task.done(): + # Queue if task not yet created (None) or still running. + # Only drop when task exists and is done (shutdown). + if ( + session.sendspin_to_local_task is None + or not session.sendspin_to_local_task.done() + ): loop.call_soon_threadsafe(session.sendspin_queue.put_nowait, message) @channel.on("close") # type: ignore[untyped-decorator] @@ -734,6 +729,17 @@ class WebRTCGateway: 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") + self.logger.debug("Sendspin channel connected for session %s", session.session_id) + + # Start forwarding tasks - queued messages will be processed + session.sendspin_to_local_task = asyncio.create_task( + self._forward_sendspin_to_local(session) + ) + session.sendspin_from_local_task = asyncio.create_task( + self._forward_sendspin_from_local(session) + ) + except Exception: self.logger.exception( "Failed to connect sendspin channel to internal server for session %s", -- 2.34.1