From ee025e8deb12ee14575aa2c874ec6cd661f71d49 Mon Sep 17 00:00:00 2001 From: DEV-bot Date: Mon, 2 Mar 2026 00:00:40 +0000 Subject: [PATCH] Guard legacy player client against secondary-device audio playback --- lobby/templates/lobby/player_screen.html | 3 +++ lobby/tests.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/lobby/templates/lobby/player_screen.html b/lobby/templates/lobby/player_screen.html index 8df3610..b0f3fc6 100644 --- a/lobby/templates/lobby/player_screen.html +++ b/lobby/templates/lobby/player_screen.html @@ -89,6 +89,8 @@ var connectionRetryInFlight=false; var playerShellFatalError=false; var playerShellRecoverInFlight=false; var playerCriticalHydrated=false; +function silencePlayerMediaElements(){if(typeof document==="undefined"||typeof document.querySelectorAll!=="function"){return;}var elements=document.querySelectorAll("audio,video");if(!elements||typeof elements.forEach!=="function"){return;}elements.forEach(function(element){if(!element){return;}element.muted=true;if(typeof element.pause==="function"){element.pause();}});} +function installSecondaryDeviceAudioGuard(){if(typeof window==="undefined"){return;}silencePlayerMediaElements();var mediaProto=window.HTMLMediaElement&&window.HTMLMediaElement.prototype;if(!mediaProto||typeof mediaProto.play!=="function"){return;}if(mediaProto.__wppSecondaryDeviceAudioGuardInstalled){return;}mediaProto.__wppSecondaryDeviceAudioGuardInstalled=true;mediaProto.__wppSecondaryDeviceAudioGuardOriginalPlay=mediaProto.play;mediaProto.play=function(){return Promise.resolve();};} function setPlayerCriticalLoading(isLoading){var skeleton=document.getElementById("playerCriticalSkeleton");var view=document.getElementById("playerCriticalView");if(!skeleton||!view){return;}skeleton.style.display=isLoading?"block":"none";view.style.display=isLoading?"none":"block";} function hydratePlayerCriticalView(data){var phaseEl=document.getElementById("playerCriticalPhase");var roundEl=document.getElementById("playerCriticalRound");var joinEl=document.getElementById("playerCriticalJoin");if(phaseEl){phaseEl.textContent="Fase: "+phaseLabel(currentSessionStatus||((data&&data.session&&data.session.status)||""));} if(roundEl){roundEl.textContent="Round question: "+(rq()||"afventer");} @@ -153,6 +155,7 @@ function submitGuess(){if(guessSubmitted){return Promise.resolve({error:"guess_a ["code","nickname","playerId","sessionToken","roundQuestionId"].forEach(function(fieldId){var field=document.getElementById(fieldId);if(!field){return;}field.addEventListener("input",function(){if(fieldId!=="roundQuestionId"){resetRoundContextForManualChange();}updateLieSubmitState();updateGuessSubmitState();updateJoinState();updateSessionDetailState();savePlayerContext();});field.addEventListener("change",function(){if(fieldId!=="roundQuestionId"){resetRoundContextForManualChange();}updateLieSubmitState();updateGuessSubmitState();updateJoinState();updateSessionDetailState();savePlayerContext();});}); window.addEventListener("error",function(event){setPlayerShellFatalError((event&&event.message)||"Ukendt runtime-fejl");}); window.addEventListener("unhandledrejection",function(event){var reason=event&&event.reason;var detail=(reason&&reason.message)||String(reason||"Unhandled promise rejection");setPlayerShellFatalError(detail);}); +installSecondaryDeviceAudioGuard(); setPlayerCriticalLoading(true); updatePhaseStatus(); updateGuessSubmitState(); diff --git a/lobby/tests.py b/lobby/tests.py index b8ec79d..3584a78 100644 --- a/lobby/tests.py +++ b/lobby/tests.py @@ -1035,6 +1035,10 @@ class UiScreenTests(TestCase): self.assertContains(response, "clearPlayerShellFatalError") self.assertContains(response, "updatePlayerShellErrorBoundary") self.assertContains(response, "player_shell_runtime_error") + self.assertContains(response, "installSecondaryDeviceAudioGuard") + self.assertContains(response, "silencePlayerMediaElements") + self.assertContains(response, "querySelectorAll(\"audio,video\")") + self.assertNotContains(response, "