import { rm, mkdir, writeFile, readFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import ffmpeg from "fluent-ffmpeg"; import { getRandomDirectory } from "../files/getRandomDirectory.mts"; /** * Converts an image in Base64 format to a video encoded in Base64. * * @param inputImageInBase64 - The input image encoded in Base64. * @param outputVideoFormat - Optional. Format of the output video (default is "mp4"). * @param outputVideoDurationInMs - Optional. Duration of the video in milliseconds (default is 1000ms). * @param codec - Optional. Codec used for video coding. Defaults differ based on `outputVideoFormat`. * @param width - Optional. Width of the output video. * @param height - Optional. Height of the output video. * @param fps - Optional. Frame rate of the output video. * * @returns - A promise that resolves to the video as a Base64 encoded string. */ export async function imageToVideoBase64({ inputImageInBase64, outputFilePath, outputDir, clearOutputDirAtTheEnd = true, outputVideoFormat = "mp4", outputVideoDurationInMs = 1000, codec = outputVideoFormat === "webm" ? "libvpx-vp9" : "libx264", width = 1920, height = 1080, fps = 25 }: { inputImageInBase64: string outputFilePath?: string outputDir?: string clearOutputDirAtTheEnd?: boolean outputVideoFormat?: string outputVideoDurationInMs?: number codec?: string width?: number height?: number fps?: number }): Promise { outputDir = outputDir || (await getRandomDirectory()) // Decode the Base64 image and write it to a temporary file. const base64Data = inputImageInBase64.substring(inputImageInBase64.indexOf(',') + 1); const buffer = Buffer.from(base64Data, 'base64'); const inputImagePath = path.join(outputDir, 'inputImage.png'); await writeFile(inputImagePath, buffer); // Set the path for the output video. outputFilePath = outputFilePath || path.join(outputDir, `output.${outputVideoFormat}`); const durationInSeconds = outputVideoDurationInMs / 1000; // Process the image to video conversion using ffmpeg. await new Promise((resolve, reject) => { ffmpeg(inputImagePath) .outputOptions([ `-t ${durationInSeconds}`, `-r ${fps}`, `-s ${width}x${height}`, // set frame size `-c:v ${codec}`, // set the codec '-tune stillimage', '-pix_fmt yuv420p' ]) .on('end', () => resolve()) .on('error', (err) => reject(err)) .save(outputFilePath); }); // Read the video file, encode it to Base64, and format it as a data URI. const videoBuffer = await readFile(outputFilePath); const videoBase64 = videoBuffer.toString('base64'); const resultAsBase64DataUri = `data:video/${outputVideoFormat};base64,${videoBase64}`; // Attempt to clean up temporary work files. if (clearOutputDirAtTheEnd) { try { await rm(outputDir, { recursive: true, force: true }); } catch (error) { console.error('Error removing temporary files:', error); } } return resultAsBase64DataUri; }