i’m trying to compress some uploaded files to firebase storage .. using firebase cloud functions but it give me the error Error: Cannot find ffmpeg
here is my function :
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const ffmpeg = require("fluent-ffmpeg");
const ffmpegStatic = require("ffmpeg-static");
const axios = require("axios");
// const {onSchedule} = require("firebase-functions/v2/scheduler");
admin.initializeApp();
// Ensure fluent-ffmpeg uses the binary
ffmpeg.setFfmpegPath(ffmpegStatic.path);
const db = admin.firestore();
const bucket = admin.storage().bucket();
const fs = require("fs");
const downloadVideo = async (url, outputPath) => {
const response = await axios.get(url, {responseType: "stream"});
const writer = fs.createWriteStream(outputPath);
response.data.pipe(writer);
return new Promise((resolve, reject) => {
writer.on("finish", () => resolve(outputPath));
writer.on("error", reject);
});
};
const compressVideo = (videoFullPath, outputFileName, targetSize) => {
return new Promise((resolve, reject) => {
ffmpeg.ffprobe(videoFullPath, (err, metadata) => {
if (err) return reject(err);
const duration = parseFloat(metadata.format.duration);
const targetTotalBitrate =
(targetSize * 1024 * 8) / (1.073741824 * duration);
let audioBitrate =
metadata.streams.find((s) => s.codec_type === "audio").bit_rate;
if (10 * audioBitrate > targetTotalBitrate) {
audioBitrate = targetTotalBitrate / 10;
}
const videoBitrate = targetTotalBitrate - audioBitrate;
ffmpeg(videoFullPath)
.output(outputFileName)
.videoCodec("libx264")
.audioCodec("aac")
.videoBitrate(videoBitrate)
.audioBitrate(audioBitrate)
.on("end", resolve)
.on("error", reject)
.run();
});
});
};
const uploadVideoWithResumableUpload = (filePath, destinationBlobName) => {
const blob = bucket.file(destinationBlobName);
const options = {resumable: true, validation: "crc32c"};
return blob.createWriteStream(options).end(fs.readFileSync(filePath));
};
exports.processLessonsOnDemand =
functions.https.onRequest({timeoutSeconds: 3600, memory: "2GB"}
, async (context) => {
console.log("Fetching lessons from Firestore...");
const lessonsRef = db.collection("leassons");
const lessonsSnapshot = await lessonsRef.get();
if (lessonsSnapshot.empty) {
console.log("No lessons found in Firestore.");
return; // Exit if no lessons are available
}
const lessonDoc = lessonsSnapshot.docs[0]; // Get the first document
const lessonData = lessonDoc.data();
if (lessonData.shrinked) {
console.log(
`Skipping lesson ID ${lessonDoc.id} as it's already shrunk.`,
);
return; // Exit if the first lesson is already shrunk
}
const videoURL = lessonData.videoURL;
if (!videoURL) {
console.log(
`No video URL for lesson ID: ${lessonDoc.id}. Skipping...`,
);
return; // Exit if no video URL is available
}
const tempVideoPath = "/tmp/temp_video.mp4";
try {
await downloadVideo(videoURL, tempVideoPath);
const targetSize = (fs.statSync(tempVideoPath).size * 0.30) / 1024;
const outputCompressedVideo = `/tmp/compressed_${lessonDoc.id}.mp4`;
await compressVideo(tempVideoPath, outputCompressedVideo, targetSize);
await uploadVideoWithResumableUpload(
outputCompressedVideo,
`compressed_videos/compressed_${lessonDoc.id}.mp4`,
);
const newVideoURL = `https://storage.googleapis.com/${bucket.name}/compressed_videos/compressed_${lessonDoc.id}.mp4`;
const oldVideoPath = videoURL.replace(`https://storage.googleapis.com/${bucket.name}/`, "");
const oldBlob = bucket.file(oldVideoPath);
await oldBlob.delete();
await lessonsRef.doc(lessonDoc.id).update({
videoURL: newVideoURL,
shrinked: true,
});
console.log(`Processed lesson ID: ${lessonDoc.id}`);
fs.unlinkSync(tempVideoPath); // Clean up temporary files
fs.unlinkSync(outputCompressedVideo); // Clean up compressed file
} catch (error) {
console.error(`Error processing lesson ID ${lessonDoc.id}:`, error);
}
});