Element Call is now consumed as our self-built fork (@lotusguild/element-call-embedded); wire up its previously-dormant capabilities and document the fork as live. Soundboard (P5-15): a call-bar button plays user-uploaded audio clips into the call as a real published track (io.lotus.inject_audio) plus local playback. Clips are uploadable like emoji/sticker packs, stored in io.lotus.soundboard account data (synced across devices). Gated by a Settings toggle + volume. Quality controls (P5-31): per-user mic/screenshare bitrate + screenshare framerate (Settings -> Calls), applied via io.lotus.set_quality clamped to any room cap. Room admins set caps and hard call-permissions (allow_screenshare / allow_camera) in Room Settings -> Voice; the call bar hides blocked buttons. - New: CallSoundboard, useSoundboard, soundboardClips; RoomQuality, useCallQuality, callQuality (+ unit tests). - Optimistic-write RoomQuality admin UI (no stale-state clobber). - Docs: mark EC fork live across README/FEATURES/TODO/BUGS/TESTING; add D2 manual-test steps. Numeric quality caps are client-cooperative; screenshare/camera permissions are hard-enforced server-side (see LotusGuild/matrix voice-limit-guard). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
61 lines
2.3 KiB
TypeScript
61 lines
2.3 KiB
TypeScript
import { describe, it } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { buildQualityPayload } from './callQuality';
|
|
|
|
describe('buildQualityPayload', () => {
|
|
it("sends null for every 'auto' field so a prior cap is reset", () => {
|
|
const payload = buildQualityPayload({
|
|
callAudioBitrate: 'auto',
|
|
screenshareBitrate: 'auto',
|
|
screenshareFramerate: 'auto',
|
|
});
|
|
assert.deepEqual(payload, {
|
|
audioMaxBitrate: null,
|
|
screenshareMaxBitrate: null,
|
|
screenshareMaxFramerate: null,
|
|
});
|
|
});
|
|
|
|
it('converts kbps user settings to bits/sec and passes fps through', () => {
|
|
const payload = buildQualityPayload({
|
|
callAudioBitrate: '64',
|
|
screenshareBitrate: '1500',
|
|
screenshareFramerate: '30',
|
|
});
|
|
assert.equal(payload.audioMaxBitrate, 64_000);
|
|
assert.equal(payload.screenshareMaxBitrate, 1_500_000);
|
|
assert.equal(payload.screenshareMaxFramerate, 30);
|
|
});
|
|
|
|
it('clamps the user setting down to the room cap (lower wins)', () => {
|
|
const payload = buildQualityPayload(
|
|
{ callAudioBitrate: '256', screenshareBitrate: '8000', screenshareFramerate: '60' },
|
|
{ audio_max_kbps: 64, screenshare_max_kbps: 1500, screenshare_max_fps: 30 },
|
|
);
|
|
assert.equal(payload.audioMaxBitrate, 64_000);
|
|
assert.equal(payload.screenshareMaxBitrate, 1_500_000);
|
|
assert.equal(payload.screenshareMaxFramerate, 30);
|
|
});
|
|
|
|
it('does not raise a user setting that is already below the room cap', () => {
|
|
const payload = buildQualityPayload(
|
|
{ callAudioBitrate: '32', screenshareBitrate: 'auto', screenshareFramerate: '15' },
|
|
{ audio_max_kbps: 128, screenshare_max_kbps: 3000, screenshare_max_fps: 60 },
|
|
);
|
|
assert.equal(payload.audioMaxBitrate, 32_000);
|
|
// user 'auto' but room caps screenshare bitrate -> room cap applies
|
|
assert.equal(payload.screenshareMaxBitrate, 3_000_000);
|
|
assert.equal(payload.screenshareMaxFramerate, 15);
|
|
});
|
|
|
|
it('applies a room cap even when the user left the field on auto', () => {
|
|
const payload = buildQualityPayload(
|
|
{ callAudioBitrate: 'auto', screenshareBitrate: 'auto', screenshareFramerate: 'auto' },
|
|
{ audio_max_kbps: 96 },
|
|
);
|
|
assert.equal(payload.audioMaxBitrate, 96_000);
|
|
assert.equal(payload.screenshareMaxBitrate, null);
|
|
assert.equal(payload.screenshareMaxFramerate, null);
|
|
});
|
|
});
|