link-stack/metamigo-worker/lib/media-convert.ts

85 lines
2.4 KiB
TypeScript
Raw Normal View History

2023-02-13 12:41:30 +00:00
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<Buffer> => {
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<boolean> => {
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<void> => {
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}`
);
};