You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

81 lines
2.4 KiB

const express = require("express");
const multer = require("multer");
const { spawn, execSync } = require("child_process");
const fs = require("fs");
const app = express();
const upload = multer({ dest: "uploads/" });
app.post("/convert", upload.fields([{ name: "audio" }, { name: "image" }]), async (req, res) => {
if (!req.files?.audio || !req.files?.image) {
return res.status(400).send("Missing audio or image file.");
}
const audioPath = req.files.audio[0].path;
const imagePath = req.files.image[0].path;
const output = `output-${Date.now()}.mp4`;
let durationSec = 0;
try {
const durationStr = execSync(
`ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 ${audioPath}`
).toString().trim();
durationSec = parseFloat(durationStr);
console.log(`🎧 Audio duration: ${durationSec.toFixed(2)} sec`);
} catch (err) {
console.error("❌ Could not determine audio duration");
return res.status(500).send("Failed to analyze audio.");
}
const args = [
"-loop", "1",
"-i", imagePath,
"-i", audioPath,
"-c:v", "libx264",
"-preset", "ultrafast",
"-tune", "stillimage",
"-crf", "23",
"-c:a", "aac",
"-shortest",
"-pix_fmt", "yuv420p",
"-movflags", "+faststart",
output
];
console.log(`🚀 Starting conversion to ${output}`);
const ffmpeg = spawn("ffmpeg", args);
ffmpeg.stderr.on("data", (data) => {
const line = data.toString().trim();
const timeMatch = line.match(/time=(\d+):(\d+):([\d.]+)/);
if (timeMatch) {
const [, h, m, s] = timeMatch;
const timeSec = parseInt(h) * 3600 + parseInt(m) * 60 + parseFloat(s);
const percent = Math.min(100, ((timeSec / durationSec) * 100).toFixed(1));
console.log(`${line} | 📊 ${percent}%`);
} else {
console.log(line);
}
});
ffmpeg.on("exit", (code) => {
if (code !== 0) {
console.error(`❌ FFmpeg exited with code ${code}`);
return res.status(500).send("Conversion failed");
}
console.log("✅ Conversion complete. Sending file...");
res.download(output, () => {
fs.unlinkSync(audioPath);
fs.unlinkSync(imagePath);
fs.unlinkSync(output);
});
});
});
app.get("/", (req, res) => {
res.send("🎧 FileConvert API is running.");
});
app.listen(3000, () => console.log("✅ Listening on http://0.0.0.0:3000"));