disable hidden video disconnect by default

This commit is contained in:
legop3
2026-07-26 21:08:24 -04:00
parent 0f9bed9c99
commit d000b8f4f8
9 changed files with 59 additions and 21 deletions
+5
View File
@@ -60,6 +60,11 @@ bandwidthSavings:
# verifiedOnly: verified/admin users may keep multiple driver tabs; unverified users may not
# notAllowed: every identity is limited to one driver tab
multiTabProtection: "verifiedOnly"
# Disconnect rover video when its player is outside the viewport or the web
# page is in a background browser tab. Rover audio is a separate stream and
# remains connected. /mini intentionally keeps its existing always-warm video
# behavior regardless of this option.
pauseHiddenRoverVideo: false
# Video for users who are attached to a source but do not currently own its
# active turn. "snapshots" saves upload bandwidth; "live" allows full video
# whenever the normal mode/visibility rules allow it.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -78,7 +78,7 @@
<script defer src="https://analytics.otter.land/script.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land"></script>
<script defer src="https://analytics.otter.land/recorder.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land" data-sample-rate="0.15" data-mask-level="moderate" data-max-duration="300000"></script>
<title>Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-C0TEdOkf.js"></script>
<script type="module" crossorigin src="/assets/index-Dsq-cQoD.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-C3R_zn0v.css">
</head>
<body>
+14
View File
@@ -10,6 +10,7 @@ const EXTERNAL_SPECTATOR_ACCESS_MODES = new Set(['off', 'on', 'verifiedOnly', 'a
const DEFAULT_BANDWIDTH_SAVINGS = Object.freeze({
multiTabProtection: 'verifiedOnly',
pauseHiddenRoverVideo: false,
nonTurnVideo: Object.freeze({
mode: 'snapshots',
userThreshold: 0,
@@ -28,6 +29,15 @@ function normalizeEnum(value, allowed, fallback) {
return allowed.has(normalized) ? normalized : fallback;
}
function normalizeBoolean(value, fallback) {
/*
YAML booleans must stay real booleans. Treating strings such as "false" as
truthy would silently enable a bandwidth policy that the operator intended
to disable, so invalid values fall back to the documented server default.
*/
return typeof value === 'boolean' ? value : fallback;
}
function normalizeNonTurnVideo(value) {
const raw = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
const threshold = Number(raw.userThreshold);
@@ -53,6 +63,10 @@ function buildBandwidthSavingsPolicy(config = loadConfig()) {
MULTI_TAB_MODES,
DEFAULT_BANDWIDTH_SAVINGS.multiTabProtection,
),
pauseHiddenRoverVideo: normalizeBoolean(
raw.pauseHiddenRoverVideo,
DEFAULT_BANDWIDTH_SAVINGS.pauseHiddenRoverVideo,
),
nonTurnVideo: normalizeNonTurnVideo(raw.nonTurnVideo),
externalSpectatorVideo: normalizeEnum(
raw.externalSpectatorVideo,
@@ -128,18 +128,37 @@ export default function RoverMediaPlayer({
label,
forceMute = false,
sensors,
pauseVideoWhenHidden = true,
pauseVideoWhenHidden = null,
}) {
const assignedRoverId = useSessionSelector((state) => state.session?.assignment?.roverId ?? null);
const effectiveRoverId = roverId ?? assignedRoverId;
const mode = useSessionSelector((state) => state.session?.mode || null);
const configuredPauseHiddenRoverVideo = useSessionSelector(
/*
The server-normalized session policy is the browser source of truth. An
exact true check also preserves the requested off-by-default behavior
during initial session loading or when connected to an older payload.
*/
(state) => state.session?.bandwidthSavings?.pauseHiddenRoverVideo === true,
);
const rosterEntry = useSessionSelector((state) =>
effectiveRoverId && Array.isArray(state.session?.roster)
? state.session.roster.find((item) => String(item.id) === String(effectiveRoverId)) || null
: null,
);
const hasAudio = hasRoverAudioCapture(rosterEntry);
const { containerRef, isVisible: isVideoVisible } = useVideoVisibilityGate(pauseVideoWhenHidden);
/*
A surface-level override is retained for intentional exceptions. /mini sets
false explicitly because its carousel depends on hidden players remaining
warm; every normal player follows the server-configured bandwidth policy.
*/
const shouldPauseVideoWhenHidden =
typeof pauseVideoWhenHidden === 'boolean'
? pauseVideoWhenHidden
: configuredPauseHiddenRoverVideo;
const { containerRef, isVisible: isVideoVisible } = useVideoVisibilityGate(
shouldPauseVideoWhenHidden,
);
const autoVideoEnabled = videoMode ? videoMode === 'whep' : true;
const autoAudioEnabled = hasAudio;
const autoVideoEntries = useMemo(