feat: implement Opus decoder runtime checks and add tests for decoder functionality
This commit is contained in:
@@ -1,7 +1,5 @@
|
|||||||
# Discord Bot Configuration
|
# Discord Bot Configuration
|
||||||
DISCORD_TOKEN=your_bot_token_here
|
DISCORD_TOKEN=your_bot_token_here
|
||||||
VOICE_CHANNEL_ID=your_voice_channel_id_here
|
|
||||||
GUILD_ID=your_guild_id_here
|
|
||||||
|
|
||||||
# Recording Configuration
|
# Recording Configuration
|
||||||
RECORDINGS_DIR=./recordings
|
RECORDINGS_DIR=./recordings
|
||||||
|
|||||||
@@ -1,6 +1,34 @@
|
|||||||
|
import { createRequire } from "node:module";
|
||||||
import prism from "prism-media";
|
import prism from "prism-media";
|
||||||
import { config } from "../config";
|
import { config } from "../config";
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
|
interface OpusDecoderRuntime {
|
||||||
|
isBun: boolean;
|
||||||
|
canLoadNativeOpus: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldEnableDefaultOpusDecoder(
|
||||||
|
runtime: OpusDecoderRuntime,
|
||||||
|
): boolean {
|
||||||
|
return !runtime.isBun || runtime.canLoadNativeOpus;
|
||||||
|
}
|
||||||
|
|
||||||
|
function canLoadNativeOpus(): boolean {
|
||||||
|
try {
|
||||||
|
require("@discordjs/opus");
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultDecoderEnabled = shouldEnableDefaultOpusDecoder({
|
||||||
|
isBun: Boolean(process.versions.bun),
|
||||||
|
canLoadNativeOpus: canLoadNativeOpus(),
|
||||||
|
});
|
||||||
|
|
||||||
export interface OpusDecoderOptions {
|
export interface OpusDecoderOptions {
|
||||||
cooldownMs: number;
|
cooldownMs: number;
|
||||||
rotateMs: number;
|
rotateMs: number;
|
||||||
@@ -23,8 +51,14 @@ export class OpusDecoder {
|
|||||||
this.onData = options.onData;
|
this.onData = options.onData;
|
||||||
this.createDecoderFn =
|
this.createDecoderFn =
|
||||||
options.createDecoder ??
|
options.createDecoder ??
|
||||||
(() =>
|
(() => {
|
||||||
new prism.opus.Decoder({
|
if (!defaultDecoderEnabled) {
|
||||||
|
throw new Error(
|
||||||
|
"Native @discordjs/opus is unavailable under Bun; web PCM decode disabled to avoid opusscript aborts",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new prism.opus.Decoder({
|
||||||
frameSize: config.OPUS_FRAME_SIZE,
|
frameSize: config.OPUS_FRAME_SIZE,
|
||||||
channels: config.AUDIO_CHANNELS as 1 | 2,
|
channels: config.AUDIO_CHANNELS as 1 | 2,
|
||||||
rate: config.AUDIO_SAMPLE_RATE as
|
rate: config.AUDIO_SAMPLE_RATE as
|
||||||
@@ -33,7 +67,8 @@ export class OpusDecoder {
|
|||||||
| 16000
|
| 16000
|
||||||
| 24000
|
| 24000
|
||||||
| 48000,
|
| 48000,
|
||||||
}));
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
rotateIfNeeded(): void {
|
rotateIfNeeded(): void {
|
||||||
|
|||||||
@@ -57,8 +57,12 @@ export function startWebserver(
|
|||||||
const wss = new WebSocketServer({ server, path: wsPath });
|
const wss = new WebSocketServer({ server, path: wsPath });
|
||||||
wsLogger.info({ port, wsPath }, "WebSocket server listening");
|
wsLogger.info({ port, wsPath }, "WebSocket server listening");
|
||||||
|
|
||||||
// Security headers
|
// Security headers. CSP disabled because the current static UI uses inline scripts/styles.
|
||||||
app.use(helmet());
|
app.use(
|
||||||
|
helmet({
|
||||||
|
contentSecurityPolicy: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
// HTTP request logging
|
// HTTP request logging
|
||||||
app.use(pinoHttp({ logger }));
|
app.use(pinoHttp({ logger }));
|
||||||
|
|||||||
33
tests/decoder.test.ts
Normal file
33
tests/decoder.test.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import process from "node:process";
|
||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
process.env = {
|
||||||
|
...process.env,
|
||||||
|
DISCORD_TOKEN: "token",
|
||||||
|
NODE_ENV: "test",
|
||||||
|
};
|
||||||
|
vi.resetModules();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("shouldEnableDefaultOpusDecoder", () => {
|
||||||
|
it("disables default decoder on Bun when native opus is unavailable", async () => {
|
||||||
|
const { shouldEnableDefaultOpusDecoder } = await import(
|
||||||
|
"../src/recorder/decoder"
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
shouldEnableDefaultOpusDecoder({ isBun: true, canLoadNativeOpus: false }),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("enables default decoder when native opus is available", async () => {
|
||||||
|
const { shouldEnableDefaultOpusDecoder } = await import(
|
||||||
|
"../src/recorder/decoder"
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
shouldEnableDefaultOpusDecoder({ isBun: true, canLoadNativeOpus: true }),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user