Files
memegoat/backend/src/media/strategies/video-processor.strategy.ts
Mathis HERRIOT a28844e9b7 refactor: replace outputOptions with addOutputOptions in video processor strategy
- Updated FFmpeg command to use `addOutputOptions` for improved readability and consistency.
2026-01-28 14:45:41 +01:00

72 lines
2.1 KiB
TypeScript

import { readFile, unlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
import ffmpeg from "fluent-ffmpeg";
import { v4 as uuidv4 } from "uuid";
import type { MediaProcessingResult } from "../../common/interfaces/media.interface";
import type { IMediaProcessorStrategy } from "./media-processor.strategy";
@Injectable()
export class VideoProcessorStrategy implements IMediaProcessorStrategy {
private readonly logger = new Logger(VideoProcessorStrategy.name);
canHandle(mimeType: string): boolean {
return mimeType.startsWith("video/") || mimeType === "image/gif";
}
async process(
buffer: Buffer,
options: { format: "webm" | "av1" } = { format: "webm" },
): Promise<MediaProcessingResult> {
const { format } = options;
const tempInput = join(tmpdir(), `${uuidv4()}.tmp`);
const tempOutput = join(
tmpdir(),
`${uuidv4()}.${format === "av1" ? "mp4" : "webm"}`,
);
try {
await writeFile(tempInput, buffer);
await new Promise<void>((resolve, reject) => {
let command = ffmpeg(tempInput);
if (format === "webm") {
command = command
.toFormat("webm")
.videoCodec("libvpx-vp9")
.audioCodec("libopus")
.addOutputOptions("-crf", "30", "-b:v", "0");
} else {
command = command
.toFormat("mp4")
.videoCodec("libaom-av1")
.audioCodec("libopus")
.addOutputOptions("-crf", "34", "-b:v", "0", "-strict", "experimental");
}
command
.on("end", () => resolve())
.on("error", (err) => reject(err))
.save(tempOutput);
});
const processedBuffer = await readFile(tempOutput);
return {
buffer: processedBuffer,
mimeType: format === "av1" ? "video/mp4" : "video/webm",
extension: format === "av1" ? "mp4" : "webm",
size: processedBuffer.length,
};
} catch (error) {
this.logger.error(`Error processing video: ${error.message}`);
throw new BadRequestException("Failed to process video");
} finally {
await unlink(tempInput).catch(() => {});
await unlink(tempOutput).catch(() => {});
}
}
}