mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
149 lines
5.5 KiB
JavaScript
149 lines
5.5 KiB
JavaScript
// Bandwidth Savings Helper
|
|
// Purpose: Normalizes bandwidth-saving config and exposes tiny policy helpers.
|
|
// Scope: Keeps cross-service video/tab/spectator decisions consistent without
|
|
// making individual services know raw YAML defaults or legacy config shapes.
|
|
const { loadConfig } = require('./configLoader');
|
|
|
|
const MULTI_TAB_MODES = new Set(['allowed', 'verifiedOnly', 'notAllowed']);
|
|
const VIDEO_MODES = new Set(['snapshots', 'live']);
|
|
const EXTERNAL_SPECTATOR_ACCESS_MODES = new Set(['off', 'on', 'verifiedOnly', 'admin']);
|
|
|
|
const DEFAULT_BANDWIDTH_SAVINGS = Object.freeze({
|
|
multiTabProtection: 'verifiedOnly',
|
|
pauseHiddenRoverVideo: false,
|
|
nonTurnVideo: Object.freeze({
|
|
mode: 'snapshots',
|
|
userThreshold: 0,
|
|
}),
|
|
externalSpectatorVideo: 'snapshots',
|
|
externalSpectatorAccess: 'on',
|
|
});
|
|
|
|
function normalizeEnum(value, allowed, fallback) {
|
|
/*
|
|
Config files are hand-edited on the server, so a typo should not crash the
|
|
process or silently broaden access. Each option falls back to the current
|
|
conservative behavior unless it exactly matches a known value.
|
|
*/
|
|
const normalized = typeof value === 'string' ? value.trim() : '';
|
|
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);
|
|
/*
|
|
userThreshold is intentionally "greater than", not "greater than or equal".
|
|
A value of 4 means the first four controllable users can keep live non-turn
|
|
video, and the fifth controllable user activates snapshot saving. Invalid
|
|
or negative values fall back to zero, which preserves always-on snapshots
|
|
for any real non-turn participant.
|
|
*/
|
|
const userThreshold = Number.isFinite(threshold) ? Math.max(0, Math.floor(threshold)) : 0;
|
|
return {
|
|
mode: normalizeEnum(raw.mode, VIDEO_MODES, DEFAULT_BANDWIDTH_SAVINGS.nonTurnVideo.mode),
|
|
userThreshold,
|
|
};
|
|
}
|
|
|
|
function buildBandwidthSavingsPolicy(config = loadConfig()) {
|
|
const raw = config.bandwidthSavings || {};
|
|
return {
|
|
multiTabProtection: normalizeEnum(
|
|
raw.multiTabProtection,
|
|
MULTI_TAB_MODES,
|
|
DEFAULT_BANDWIDTH_SAVINGS.multiTabProtection,
|
|
),
|
|
pauseHiddenRoverVideo: normalizeBoolean(
|
|
raw.pauseHiddenRoverVideo,
|
|
DEFAULT_BANDWIDTH_SAVINGS.pauseHiddenRoverVideo,
|
|
),
|
|
nonTurnVideo: normalizeNonTurnVideo(raw.nonTurnVideo),
|
|
externalSpectatorVideo: normalizeEnum(
|
|
raw.externalSpectatorVideo,
|
|
VIDEO_MODES,
|
|
DEFAULT_BANDWIDTH_SAVINGS.externalSpectatorVideo,
|
|
),
|
|
externalSpectatorAccess: normalizeEnum(
|
|
raw.externalSpectatorAccess,
|
|
EXTERNAL_SPECTATOR_ACCESS_MODES,
|
|
DEFAULT_BANDWIDTH_SAVINGS.externalSpectatorAccess,
|
|
),
|
|
};
|
|
}
|
|
|
|
function getBandwidthSavingsPolicy() {
|
|
/*
|
|
loadConfig() is cached by configLoader, so rebuilding this small object per
|
|
caller is cheap while still letting tests pass explicit config objects into
|
|
buildBandwidthSavingsPolicy().
|
|
*/
|
|
return buildBandwidthSavingsPolicy(loadConfig());
|
|
}
|
|
|
|
function shouldEnforceSingleDriverTab({ isVerified = false, isAdmin = false } = {}) {
|
|
const { multiTabProtection } = getBandwidthSavingsPolicy();
|
|
if (multiTabProtection === 'allowed') return false;
|
|
if (multiTabProtection === 'notAllowed') return true;
|
|
/*
|
|
verifiedOnly preserves the old behavior: trusted users can run multiple
|
|
driver tabs for operations/testing, while anonymous users are limited to one
|
|
active driver surface for fairness and bandwidth.
|
|
*/
|
|
return !isVerified && !isAdmin;
|
|
}
|
|
|
|
function shouldUseSnapshotsForNonTurnVideo({ controllableUserCount = 0 } = {}) {
|
|
const { nonTurnVideo } = getBandwidthSavingsPolicy();
|
|
if (nonTurnVideo.mode !== 'snapshots') return false;
|
|
/*
|
|
The threshold is evaluated centrally so MediaMTX auth, socket-issued video
|
|
tokens, PTZ authorization, and browser session state all agree. Using a
|
|
strict greater-than comparison makes the configured value read like the
|
|
maximum number of controllable users allowed before snapshots start.
|
|
*/
|
|
return Math.max(0, Number(controllableUserCount) || 0) > nonTurnVideo.userThreshold;
|
|
}
|
|
|
|
function shouldUseSnapshotsForExternalSpectatorVideo() {
|
|
return getBandwidthSavingsPolicy().externalSpectatorVideo === 'snapshots';
|
|
}
|
|
|
|
function canUseExternalSpectatorAccess({
|
|
isLocal = false,
|
|
isAdmin = false,
|
|
isVerified = false,
|
|
hasGrant = false,
|
|
} = {}) {
|
|
/*
|
|
Local/LAN spectators are not the upload-bandwidth problem, and admins need
|
|
to retain access for maintenance. The configured external mode only applies
|
|
to ordinary non-local spectator sockets.
|
|
*/
|
|
if (isLocal || isAdmin) return true;
|
|
const { externalSpectatorAccess } = getBandwidthSavingsPolicy();
|
|
if (externalSpectatorAccess === 'off') return false;
|
|
if (externalSpectatorAccess === 'verifiedOnly') return Boolean(isVerified);
|
|
if (externalSpectatorAccess === 'admin') return Boolean(hasGrant);
|
|
return true;
|
|
}
|
|
|
|
module.exports = {
|
|
DEFAULT_BANDWIDTH_SAVINGS,
|
|
buildBandwidthSavingsPolicy,
|
|
getBandwidthSavingsPolicy,
|
|
shouldEnforceSingleDriverTab,
|
|
shouldUseSnapshotsForNonTurnVideo,
|
|
shouldUseSnapshotsForExternalSpectatorVideo,
|
|
canUseExternalSpectatorAccess,
|
|
};
|