import fs from 'node:fs/promises'; import { writeFile, readFile } from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import { v4 as uuidv4 } from "uuid"; import ffmpeg from 'fluent-ffmpeg'; export type ScaleVideoParams = { input: string; height: number; debug?: boolean; asBase64?: boolean; } /** * Rescale a video (either file or base 64) to a given height. * This returns a base64 video. * * Some essential things to note in this implementation: * * If the input is a valid base64 string, it gets decoded and stored as a temporary .mp4 file. * The ffmpeg.outputOptions includes the arguments for setting the output video height and keeping the aspect ratio intact. The -1 in scale=-1:${height} tells ffmpeg to preserve aspect ratio based on the height. * The output is a libx264-encoded MP4 video, matching typical browser support standards. * Upon completion, the temporary output file is read into a buffer, converted to a base64 string with the correct prefix, and then cleaned up by removing temporary files. * To call this function with desired input and height, you'd use it similarly to the provided convertMp4ToMp3 function example, being mindful that input must be a file path or properly-formatted base64 string and height is a number representing the new height of the video. * * Enter your message... * * @param param0 * @returns */ export async function scaleVideo({ input, height, asBase64 = false, debug = false }: ScaleVideoParams): Promise { const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "ffmpeg-")); const tempOutPath = path.join(tempDir, `${uuidv4()}.mp4`); let inputPath; if (input.startsWith('data:')) { // Extract the base64 content and decode it to a temporary file const base64Content = input.split(';base64,').pop(); if (!base64Content) { throw new Error('Invalid base64 input provided'); } inputPath = path.join(tempDir, `${uuidv4()}.mp4`); await writeFile(inputPath, base64Content, 'base64'); } else { inputPath = input; } if (debug) { console.log("inputPath:", inputPath) } // Return a promise that resolves with the base64 string of the output video return new Promise((resolve, reject) => { ffmpeg(inputPath) .outputOptions([ '-vf', `scale=-1:${height}`, '-c:v', 'libx264', '-preset', 'fast', '-crf', '22' ]) .on('error', (err) => { reject(new Error(`Error scaling the video: ${err.message}`)); }) .on('end', async () => { if (!asBase64) { resolve(tempOutPath) return } // Convert the output file to a base64 string try { const videoBuffer = await readFile(tempOutPath); const videoBase64 = `data:video/mp4;base64,${videoBuffer.toString('base64')}`; resolve(videoBase64); } catch (error) { reject(new Error(`Error loading the video file: ${error}`)); } finally { // Clean up temporary files await fs.rm(tempDir, { recursive: true }); } }) .save(tempOutPath); }); }