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_TOKEN=your_bot_token_here
|
||||
VOICE_CHANNEL_ID=your_voice_channel_id_here
|
||||
GUILD_ID=your_guild_id_here
|
||||
|
||||
# Recording Configuration
|
||||
RECORDINGS_DIR=./recordings
|
||||
|
||||
+38
-3
@@ -1,6 +1,34 @@
|
||||
import { createRequire } from "node:module";
|
||||
import prism from "prism-media";
|
||||
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 {
|
||||
cooldownMs: number;
|
||||
rotateMs: number;
|
||||
@@ -23,8 +51,14 @@ export class OpusDecoder {
|
||||
this.onData = options.onData;
|
||||
this.createDecoderFn =
|
||||
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,
|
||||
channels: config.AUDIO_CHANNELS as 1 | 2,
|
||||
rate: config.AUDIO_SAMPLE_RATE as
|
||||
@@ -33,7 +67,8 @@ export class OpusDecoder {
|
||||
| 16000
|
||||
| 24000
|
||||
| 48000,
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
rotateIfNeeded(): void {
|
||||
|
||||
+6
-2
@@ -57,8 +57,12 @@ export function startWebserver(
|
||||
const wss = new WebSocketServer({ server, path: wsPath });
|
||||
wsLogger.info({ port, wsPath }, "WebSocket server listening");
|
||||
|
||||
// Security headers
|
||||
app.use(helmet());
|
||||
// Security headers. CSP disabled because the current static UI uses inline scripts/styles.
|
||||
app.use(
|
||||
helmet({
|
||||
contentSecurityPolicy: false,
|
||||
}),
|
||||
);
|
||||
|
||||
// HTTP request logging
|
||||
app.use(pinoHttp({ logger }));
|
||||
|
||||
@@ -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