import { Readable } from "stream"; import ffmpeg from "fluent-ffmpeg"; import * as R from "remeda"; const requiredCodecs = ["mp3", "webm", "wav"]; export interface AudioConvertOpts { bitrate?: string; audioCodec?: string; format?: string; } const defaultAudioConvertOpts = { bitrate: "32k", audioCodec: "libmp3lame", format: "mp3", }; /** * Converts an audio file to a different format. defaults to converting to mp3 with a 32k bitrate using the libmp3lame codec * * @param input the buffer containing the binary data of the input file * @param opts options to control how the audio file is converted * @return resolves to a buffer containing the binary data of the converted file **/ export const convert = ( input: Buffer, opts?: AudioConvertOpts ): Promise => { const settings = { ...defaultAudioConvertOpts, ...opts }; return new Promise((resolve, reject) => { const stream = Readable.from(input); let out = Buffer.alloc(0); const cmd = ffmpeg(stream) .audioCodec(settings.audioCodec) .audioBitrate(settings.bitrate) .toFormat(settings.format) .on("error", (err, stdout, stderr) => { console.error(err.message); console.log("FFMPEG OUTPUT"); console.log(stdout); console.log("FFMPEG ERROR"); console.log(stderr); reject(err); }) .on("end", () => { resolve(out); }); const outstream = cmd.pipe(); outstream.on("data", (chunk: Buffer) => { out = Buffer.concat([out, chunk]); }); }); }; /** * Check if ffmpeg is installed and usable. Checks for required codecs and a working ffmpeg installation. * * @return resolves to true if ffmpeg is installed and usable * */ export const selfCheck = (): Promise => { return new Promise((resolve) => { ffmpeg.getAvailableFormats((err, codecs) => { if (err) { console.error("FFMPEG error:", err); resolve(false); } const preds = R.map(requiredCodecs, (codec) => (available: any) => available[codec] && available[codec].canDemux && available[codec].canMux ); resolve(R.allPass(codecs, preds)); }); }); }; export const assertFfmpegAvailable = async (): Promise => { const r = await selfCheck(); if (!r) throw new Error( `ffmpeg is not installed, could not be located, or does not support the required codecs: ${requiredCodecs}` ); };