mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 08:03:30 +01:00
* build: upgrade dependencies * build: upgrade pnpm * test: fix voice * build: regenerate file * fix: reports by ESLint * fix: docs errors * build: downgrades * build: no upstream bump * build: discord-api-types 0.38.40 * build: pnpm 10.30.1 * fix: ignore @typescript-eslint/no-duplicate-type-constituents * fix: jsdoc lint in api-extractor * build: update template for ESLint Co-authored-by: Almeida <github@almeidx.dev> * chore: explicit TODO Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> * chore: revert typings lint change --------- Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> Co-authored-by: Almeida <github@almeidx.dev>
136 lines
4.9 KiB
TypeScript
136 lines
4.9 KiB
TypeScript
import { Buffer } from 'node:buffer';
|
|
import process from 'node:process';
|
|
import { PassThrough, Readable } from 'node:stream';
|
|
import { opus, VolumeTransformer } from 'prism-media';
|
|
import { describe, test, expect, vitest, type MockedFunction, beforeAll, beforeEach } from 'vitest';
|
|
import { SILENCE_FRAME } from '../src/audio/AudioPlayer';
|
|
import { AudioResource, createAudioResource, NO_CONSTRAINT, VOLUME_CONSTRAINT } from '../src/audio/AudioResource';
|
|
import { findPipeline as _findPipeline, StreamType, TransformerType, type Edge } from '../src/audio/TransformerGraph';
|
|
|
|
vitest.mock('../src/audio/TransformerGraph');
|
|
|
|
async function wait() {
|
|
// eslint-disable-next-line no-promise-executor-return
|
|
return new Promise((resolve) => process.nextTick(resolve));
|
|
}
|
|
|
|
async function started(resource: AudioResource) {
|
|
while (!resource.started) {
|
|
await wait();
|
|
}
|
|
|
|
return resource;
|
|
}
|
|
|
|
const findPipeline = _findPipeline as unknown as MockedFunction<typeof _findPipeline>;
|
|
|
|
beforeAll(() => {
|
|
// @ts-expect-error: No type
|
|
findPipeline.mockImplementation((from: StreamType, constraint: (path: Edge[]) => boolean) => {
|
|
const base = [
|
|
{
|
|
cost: 1,
|
|
transformer: () => new PassThrough(),
|
|
type: TransformerType.FFmpegPCM,
|
|
},
|
|
];
|
|
if (constraint === VOLUME_CONSTRAINT) {
|
|
base.push({
|
|
cost: 1,
|
|
// Transformer type shouldn't matter: we are not testing prism-media, but rather the expectation that the stream is VolumeTransformer
|
|
transformer: () => new VolumeTransformer({ type: 's16le' } as any),
|
|
type: TransformerType.InlineVolume,
|
|
});
|
|
}
|
|
|
|
return base as any[];
|
|
});
|
|
});
|
|
|
|
beforeEach(() => {
|
|
findPipeline.mockClear();
|
|
});
|
|
|
|
describe('createAudioResource', () => {
|
|
test('Creates a resource from string path', () => {
|
|
const resource = createAudioResource('mypath.mp3');
|
|
expect(findPipeline).toHaveBeenCalledWith(StreamType.Arbitrary, NO_CONSTRAINT);
|
|
expect(resource.volume).toBeUndefined();
|
|
});
|
|
|
|
test('Creates a resource from string path (volume)', () => {
|
|
const resource = createAudioResource('mypath.mp3', { inlineVolume: true });
|
|
expect(findPipeline).toHaveBeenCalledWith(StreamType.Arbitrary, VOLUME_CONSTRAINT);
|
|
expect(resource.volume).toBeInstanceOf(VolumeTransformer);
|
|
});
|
|
|
|
test('Only infers type if not explicitly given', () => {
|
|
const resource = createAudioResource(new opus.Encoder({ rate: 48_000, channels: 2, frameSize: 960 }), {
|
|
inputType: StreamType.Arbitrary,
|
|
});
|
|
expect(findPipeline).toHaveBeenCalledWith(StreamType.Arbitrary, NO_CONSTRAINT);
|
|
expect(resource.volume).toBeUndefined();
|
|
});
|
|
|
|
test('Infers from opus.Encoder', () => {
|
|
const resource = createAudioResource(new opus.Encoder({ rate: 48_000, channels: 2, frameSize: 960 }), {
|
|
inlineVolume: true,
|
|
});
|
|
expect(findPipeline).toHaveBeenCalledWith(StreamType.Opus, VOLUME_CONSTRAINT);
|
|
expect(resource.volume).toBeInstanceOf(VolumeTransformer);
|
|
expect(resource.encoder).toBeInstanceOf(opus.Encoder);
|
|
});
|
|
|
|
test('Infers from opus.OggDemuxer', () => {
|
|
const resource = createAudioResource(new opus.OggDemuxer());
|
|
expect(findPipeline).toHaveBeenCalledWith(StreamType.Opus, NO_CONSTRAINT);
|
|
expect(resource.volume).toBeUndefined();
|
|
expect(resource.encoder).toBeUndefined();
|
|
});
|
|
|
|
test('Infers from opus.WebmDemuxer', () => {
|
|
const resource = createAudioResource(new opus.WebmDemuxer());
|
|
expect(findPipeline).toHaveBeenCalledWith(StreamType.Opus, NO_CONSTRAINT);
|
|
expect(resource.volume).toBeUndefined();
|
|
});
|
|
|
|
test('Infers from opus.Decoder', () => {
|
|
const resource = createAudioResource(new opus.Decoder({ rate: 48_000, channels: 2, frameSize: 960 }));
|
|
expect(findPipeline).toHaveBeenCalledWith(StreamType.Raw, NO_CONSTRAINT);
|
|
expect(resource.volume).toBeUndefined();
|
|
});
|
|
|
|
test('Infers from VolumeTransformer', () => {
|
|
// Transformer type shouldn't matter: we are not testing prism-media, but rather the expectation that the stream is VolumeTransformer
|
|
const stream = new VolumeTransformer({ type: 's16le' } as any);
|
|
const resource = createAudioResource(stream, { inlineVolume: true });
|
|
expect(findPipeline).toHaveBeenCalledWith(StreamType.Raw, NO_CONSTRAINT);
|
|
expect(resource.volume).toEqual(stream);
|
|
});
|
|
|
|
test('Falls back to Arbitrary for unknown stream type', () => {
|
|
const resource = createAudioResource(new PassThrough());
|
|
expect(findPipeline).toHaveBeenCalledWith(StreamType.Arbitrary, NO_CONSTRAINT);
|
|
expect(resource.volume).toBeUndefined();
|
|
});
|
|
|
|
test('Appends silence frames when ended', async () => {
|
|
const stream = Readable.from(Buffer.from([1]));
|
|
|
|
const resource = new AudioResource([], [stream], null, 5);
|
|
|
|
await started(resource);
|
|
expect(resource.readable).toEqual(true);
|
|
expect(resource.read()).toEqual(Buffer.from([1]));
|
|
for (let index = 0; index < 5; index++) {
|
|
await wait();
|
|
expect(resource.readable).toEqual(true);
|
|
expect(resource.read()).toEqual(SILENCE_FRAME);
|
|
}
|
|
|
|
await wait();
|
|
expect(resource.readable).toEqual(false);
|
|
expect(resource.read()).toEqual(null);
|
|
});
|
|
});
|