90 lines
2.6 KiB
JavaScript
90 lines
2.6 KiB
JavaScript
/**
|
|
* render.mjs - Renders a video from a VideoScript JSON file.
|
|
*
|
|
* Usage: node render.mjs <script.json> [output.mp4]
|
|
*/
|
|
import { bundle } from "@remotion/bundler";
|
|
import { renderMedia, selectComposition } from "@remotion/renderer";
|
|
import path from "path";
|
|
import fs from "fs";
|
|
import { fileURLToPath } from "url";
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
async function main() {
|
|
const args = process.argv.slice(2);
|
|
|
|
if (args.length < 1) {
|
|
console.error("Usage: node render.mjs <script.json> [output.mp4]");
|
|
process.exit(1);
|
|
}
|
|
|
|
const scriptPath = path.resolve(args[0]);
|
|
const outputPath = args[1]
|
|
? path.resolve(args[1])
|
|
: path.join(__dirname, "out", "video.mp4");
|
|
|
|
// Ensure output directory exists
|
|
const outputDir = path.dirname(outputPath);
|
|
if (!fs.existsSync(outputDir)) {
|
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
}
|
|
|
|
console.log("Loading script:", scriptPath);
|
|
const script = JSON.parse(fs.readFileSync(scriptPath, "utf-8"));
|
|
|
|
console.log(`Video: "${script.title}"`);
|
|
console.log(` Scenes: ${script.scenes.length}`);
|
|
console.log(` Duration: ${script.totalDurationInSeconds}s`);
|
|
console.log(` Resolution: ${script.width}x${script.height}`);
|
|
console.log(` FPS: ${script.fps}`);
|
|
|
|
const totalFrames = script.totalDurationInSeconds * script.fps;
|
|
|
|
console.log("\nBundling Remotion project...");
|
|
const bundleLocation = await bundle({
|
|
entryPoint: path.resolve(__dirname, "src/index.ts"),
|
|
webpackOverride: (config) => config,
|
|
});
|
|
|
|
console.log("Selecting composition...");
|
|
const composition = await selectComposition({
|
|
serveUrl: bundleLocation,
|
|
id: "PromptVideo",
|
|
inputProps: { script },
|
|
});
|
|
|
|
// Override composition metadata with our script values
|
|
composition.durationInFrames = totalFrames;
|
|
composition.fps = script.fps;
|
|
composition.width = script.width;
|
|
composition.height = script.height;
|
|
|
|
console.log(`Rendering ${totalFrames} frames to ${outputPath} ...`);
|
|
|
|
let lastProgress = 0;
|
|
await renderMedia({
|
|
composition,
|
|
serveUrl: bundleLocation,
|
|
codec: "h264",
|
|
outputLocation: outputPath,
|
|
inputProps: { script },
|
|
onProgress: ({ progress }) => {
|
|
const pct = Math.floor(progress * 100);
|
|
if (pct >= lastProgress + 10) {
|
|
lastProgress = pct;
|
|
process.stdout.write(` ${pct}%\r`);
|
|
}
|
|
},
|
|
});
|
|
|
|
const stats = fs.statSync(outputPath);
|
|
console.log(`\nDone! ${outputPath} (${(stats.size / 1024 / 1024).toFixed(2)} MB)`);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error("Render failed:", err.message || err);
|
|
process.exit(1);
|
|
});
|