aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/video-player.js18
-rw-r--r--packages/meshbay-hub/tests/test_cast_discovery.py15
2 files changed, 33 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/video-player.js b/packages/meshbay-hub/src/meshbay_hub/static/video-player.js
index 4fa652b..243e0c2 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/video-player.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/video-player.js
@@ -1471,6 +1471,24 @@ function VideoPlayer({ entry, transportRef, gekRef, onClose, onDownload }) {
// The scan lives as long as the picker is open: closing it, or picking a
// receiver, stops the updates. Receivers are listed as they answer, and the
// spinner sits below them so one landing never moves the row being aimed at.
+ // While casting, the film plays on the receiver; the local element keeps
+ // playing — its playhead is what paces the stream the relay forwards — but
+ // silently. It went on with its sound, so a phone in the room doubled the
+ // television's audio, screen off included. What the viewer had set comes
+ // back when the cast ends.
+ const mutedBeforeCastRef = useRef(null);
+ useEffect(() => {
+ const v = videoRef.current;
+ if (!v) return;
+ if (castActive) {
+ if (mutedBeforeCastRef.current === null) mutedBeforeCastRef.current = v.muted;
+ v.muted = true;
+ } else if (mutedBeforeCastRef.current !== null) {
+ v.muted = mutedBeforeCastRef.current;
+ mutedBeforeCastRef.current = null;
+ }
+ }, [castActive]);
+
useEffect(() => {
if (!castPickerOpen) return undefined;
setCastDevices([]);
diff --git a/packages/meshbay-hub/tests/test_cast_discovery.py b/packages/meshbay-hub/tests/test_cast_discovery.py
index 9e9ce85..77b0ef6 100644
--- a/packages/meshbay-hub/tests/test_cast_discovery.py
+++ b/packages/meshbay-hub/tests/test_cast_discovery.py
@@ -106,3 +106,18 @@ def test_a_new_scan_is_not_cut_short_by_the_one_it_replaced(run):
assert run["restartedStillScanning"] is True
assert run["restartClearedOldDevices"] is True
assert run["restartedEnds"] is True
+
+
+def test_the_local_player_is_silent_while_casting():
+ """The local element keeps playing while casting — its playhead paces the
+ stream the relay forwards — so it must not keep its sound: a phone in the
+ room doubled the television's audio, screen off included. Whatever the
+ viewer had set comes back when the cast ends."""
+ from spa_source import STATIC
+ player = (STATIC / "video-player.js").read_text(encoding="utf-8")
+ effect = player.split("const mutedBeforeCastRef = useRef(null);", 1)[1]
+ effect = effect.split("}, [castActive]);", 1)[0]
+ assert "v.muted = true;" in effect
+ assert "mutedBeforeCastRef.current = v.muted;" in effect
+ assert "v.muted = mutedBeforeCastRef.current;" in effect
+ assert "pause()" not in effect, "pausing would stop the stream the receiver is playing"