feat(voice): add audio bitrate guild features and 96 kbps cap (#2703)

This commit is contained in:
Hampus
2026-09-11 22:00:15 +02:00
committed by GitHub
parent cfa20b7093
commit 258fe6f742
24 changed files with 358 additions and 61 deletions
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {describe, expect, it} from 'vitest';
import {
clampVoiceChannelBitrate,
GuildFeatures,
getMaxVoiceChannelBitrate,
resolveVoiceChannelBitrate,
} from './GuildConstants';
describe('getMaxVoiceChannelBitrate', () => {
it('caps a guild holding no audio bitrate feature at 96 kbps', () => {
expect(getMaxVoiceChannelBitrate([])).toBe(96000);
expect(getMaxVoiceChannelBitrate([GuildFeatures.VIP_VOICE])).toBe(96000);
});
it('caps a call outside a guild at 96 kbps', () => {
expect(getMaxVoiceChannelBitrate(null)).toBe(96000);
expect(getMaxVoiceChannelBitrate(undefined)).toBe(96000);
});
it('raises the cap to the feature the guild holds', () => {
expect(getMaxVoiceChannelBitrate([GuildFeatures.AUDIO_BITRATE_128_KBPS])).toBe(128000);
expect(getMaxVoiceChannelBitrate([GuildFeatures.AUDIO_BITRATE_256_KBPS])).toBe(256000);
expect(getMaxVoiceChannelBitrate([GuildFeatures.AUDIO_BITRATE_384_KBPS])).toBe(384000);
});
it('takes the highest feature when a guild holds several', () => {
expect(
getMaxVoiceChannelBitrate([GuildFeatures.AUDIO_BITRATE_384_KBPS, GuildFeatures.AUDIO_BITRATE_128_KBPS]),
).toBe(384000);
expect(
getMaxVoiceChannelBitrate(new Set([GuildFeatures.AUDIO_BITRATE_128_KBPS, GuildFeatures.AUDIO_BITRATE_256_KBPS])),
).toBe(256000);
});
});
describe('clampVoiceChannelBitrate', () => {
it('clamps above the cap the guild holds', () => {
expect(clampVoiceChannelBitrate(384000, [])).toBe(96000);
expect(clampVoiceChannelBitrate(384000, [GuildFeatures.AUDIO_BITRATE_128_KBPS])).toBe(128000);
expect(clampVoiceChannelBitrate(384000, [GuildFeatures.AUDIO_BITRATE_384_KBPS])).toBe(384000);
});
it('leaves a value under the cap alone', () => {
expect(clampVoiceChannelBitrate(64000, [])).toBe(64000);
expect(clampVoiceChannelBitrate(8000, [])).toBe(8000);
});
it('raises a value under the minimum', () => {
expect(clampVoiceChannelBitrate(1000, [])).toBe(8000);
});
});
describe('resolveVoiceChannelBitrate', () => {
it('falls back to 64 kbps when the channel stores no bitrate', () => {
expect(resolveVoiceChannelBitrate(null, null)).toBe(64000);
expect(resolveVoiceChannelBitrate(undefined, [])).toBe(64000);
expect(resolveVoiceChannelBitrate(0, [])).toBe(64000);
expect(resolveVoiceChannelBitrate(Number.NaN, [])).toBe(64000);
});
it('clamps a stored bitrate to what the guild currently holds', () => {
expect(resolveVoiceChannelBitrate(384000, [])).toBe(96000);
expect(resolveVoiceChannelBitrate(384000, [GuildFeatures.AUDIO_BITRATE_256_KBPS])).toBe(256000);
});
});
+42
View File
@@ -1,5 +1,13 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {
VOICE_CHANNEL_BITRATE_DEFAULT,
VOICE_CHANNEL_BITRATE_MAX_128_KBPS,
VOICE_CHANNEL_BITRATE_MAX_256_KBPS,
VOICE_CHANNEL_BITRATE_MAX_384_KBPS,
VOICE_CHANNEL_BITRATE_MAX_STANDARD,
VOICE_CHANNEL_BITRATE_MIN,
} from '@fluxer/constants/src/LimitConstants';
import type {ValueOf} from '@fluxer/constants/src/ValueOf';
export const GuildVerificationLevel = {
@@ -97,6 +105,9 @@ export const CONTENT_WARNING_TEXT_MAX_LENGTH = 200;
export const GuildFeatures = {
ANIMATED_ICON: 'ANIMATED_ICON',
ANIMATED_BANNER: 'ANIMATED_BANNER',
AUDIO_BITRATE_128_KBPS: 'AUDIO_BITRATE_128_KBPS',
AUDIO_BITRATE_256_KBPS: 'AUDIO_BITRATE_256_KBPS',
AUDIO_BITRATE_384_KBPS: 'AUDIO_BITRATE_384_KBPS',
BANNER: 'BANNER',
CLONE_EMOJI_DISABLED: 'CLONE_EMOJI_DISABLED',
CLONE_STICKER_DISABLED: 'CLONE_STICKER_DISABLED',
@@ -127,6 +138,37 @@ export const GuildFeatures = {
export type GuildFeature = ValueOf<typeof GuildFeatures>;
export function getMaxVoiceChannelBitrate(features: Iterable<string> | null | undefined): number {
if (features == null) {
return VOICE_CHANNEL_BITRATE_MAX_STANDARD;
}
let maximum = VOICE_CHANNEL_BITRATE_MAX_STANDARD;
for (const feature of features) {
if (feature === GuildFeatures.AUDIO_BITRATE_384_KBPS) {
maximum = Math.max(maximum, VOICE_CHANNEL_BITRATE_MAX_384_KBPS);
} else if (feature === GuildFeatures.AUDIO_BITRATE_256_KBPS) {
maximum = Math.max(maximum, VOICE_CHANNEL_BITRATE_MAX_256_KBPS);
} else if (feature === GuildFeatures.AUDIO_BITRATE_128_KBPS) {
maximum = Math.max(maximum, VOICE_CHANNEL_BITRATE_MAX_128_KBPS);
}
}
return maximum;
}
export function clampVoiceChannelBitrate(bitrate: number, features: Iterable<string> | null | undefined): number {
return Math.min(Math.max(bitrate, VOICE_CHANNEL_BITRATE_MIN), getMaxVoiceChannelBitrate(features));
}
export function resolveVoiceChannelBitrate(
bitrate: number | null | undefined,
features: Iterable<string> | null | undefined,
): number {
if (typeof bitrate !== 'number' || !Number.isFinite(bitrate) || bitrate <= 0) {
return VOICE_CHANNEL_BITRATE_DEFAULT;
}
return clampVoiceChannelBitrate(Math.round(bitrate), features);
}
export const JoinSourceTypes = {
CREATOR: 0,
INSTANT_INVITE: 1,
+6 -1
View File
@@ -4,7 +4,12 @@ export const MAX_GUILDS_PREMIUM = 200;
export const MAX_GUILD_CHANNELS = 500;
export const MAX_CHANNELS_PER_CATEGORY = 50;
export const VOICE_CHANNEL_BITRATE_MIN = 8000;
export const VOICE_CHANNEL_BITRATE_MAX = 320000;
export const VOICE_CHANNEL_BITRATE_DEFAULT = 64000;
export const VOICE_CHANNEL_BITRATE_MAX_STANDARD = 96000;
export const VOICE_CHANNEL_BITRATE_MAX_128_KBPS = 128000;
export const VOICE_CHANNEL_BITRATE_MAX_256_KBPS = 256000;
export const VOICE_CHANNEL_BITRATE_MAX_384_KBPS = 384000;
export const VOICE_CHANNEL_BITRATE_MAX = VOICE_CHANNEL_BITRATE_MAX_384_KBPS;
export const VOICE_CHANNEL_USER_LIMIT_MIN = 0;
export const VOICE_CHANNEL_USER_LIMIT_MAX = 99;
export const VOICE_CHANNEL_CAMERA_USER_LIMIT = 25;