feat(audio): add ffmpeg-backed audio converter for mp3/wav/ogg/flac/aac/m4a

This commit is contained in:
2026-08-01 14:13:09 +02:00
parent 13d095aa4c
commit 05be353664
2 changed files with 149 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { register } from './registry.js';
const execFileAsync = promisify(execFile);
export const AUDIO_FORMATS = ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a'];
const CODEC_ARGS = {
mp3: ['-c:a', 'libmp3lame'],
ogg: ['-c:a', 'libvorbis'],
aac: ['-c:a', 'aac'],
m4a: ['-c:a', 'aac'],
wav: ['-c:a', 'pcm_s16le'],
flac: ['-c:a', 'flac'],
};
const BITRATE_CAPABLE_FORMATS = ['mp3', 'ogg', 'aac', 'm4a'];
export function registerAudioConverters() {
for (const sourceFormat of AUDIO_FORMATS) {
for (const targetFormat of AUDIO_FORMATS) {
if (sourceFormat === targetFormat) continue;
register({
family: 'audio',
sourceFormat,
targetFormat,
convert: async (inputPath, outputPath, { quality, timeoutMs } = {}) => {
const ffmpegPath = process.env.FFMPEG_PATH || 'ffmpeg';
const args = ['-y', '-i', inputPath, ...CODEC_ARGS[targetFormat]];
if (quality != null && BITRATE_CAPABLE_FORMATS.includes(targetFormat)) {
args.push('-b:a', `${quality}k`);
}
args.push(outputPath);
await execFileAsync(ffmpegPath, args, { timeout: timeoutMs });
},
});
}
}
}