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.
 

80 lines
2.3 KiB

// convert.js
const fs = require("fs");
const path = require("path");
const { spawn, execSync } = require("child_process");
// Ensure output folder exists
if (!fs.existsSync("output")) fs.mkdirSync("output");
// Step 1: Search for files
const inputDir = "inputs";
const files = fs.readdirSync(inputDir);
const audioFile = files.find(f => f.toLowerCase().endsWith(".wav"));
const imageFile = files.find(f => f.toLowerCase().match(/\.(jpg|jpeg|svg|png)$/));
if (!audioFile || !imageFile) {
console.error("❌ Missing .wav and/or .jpg/.svg file in inputs/");
process.exit(1);
}
const audioPath = path.join(inputDir, audioFile);
const imagePath = path.join(inputDir, imageFile);
const outputName = `output-${Date.now() + Math.random() * 1000}.mp4`;
const outputPath = path.join("output", outputName);
// Step 2: Get audio duration
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");
process.exit(1);
}
// Step 3: Convert using ffmpeg
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",
outputPath
];
console.log(`🚀 Starting conversion: ${path.basename(imageFile)} + ${path.basename(audioFile)}${outputName}`);
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;
}
console.log(`✅ Conversion complete! Saved to: ${outputPath}`);
});