feat(videos): add render-all script for dual-format batch rendering

This commit is contained in:
SnapOtter
2026-05-08 17:17:53 +08:00
parent b970bd0f0a
commit f7531e19aa
+58 -14
View File
@@ -1,49 +1,93 @@
import { bundle } from "@remotion/bundler";
import { renderMedia, selectComposition } from "@remotion/renderer";
import { renderMedia, renderStill, selectComposition } from "@remotion/renderer";
import { enableTailwind } from "@remotion/tailwind";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const COMPOSITIONS = [
{ id: "XLaunchVideo", slug: "x-launch" },
{ id: "ProductDemo", slug: "product-demo" },
{ id: "PromoTeaser", slug: "promo-teaser-square" },
{ id: "PromoTeaserVertical", slug: "promo-teaser-vertical" },
{ id: "PrivacyPromise", slug: "privacy-promise", posterFrame: 140 },
{ id: "ToolGalaxy", slug: "tool-galaxy", posterFrame: 450 },
{ id: "OneCommand", slug: "one-command", posterFrame: 300 },
{ id: "AiMagicReel", slug: "ai-magic-reel", posterFrame: 190 },
{ id: "FormatUniverse", slug: "format-universe", posterFrame: 90 },
{ id: "PipelineFlow", slug: "pipeline-flow", posterFrame: 260 },
{ id: "FloatingTools", slug: "floating-tools", posterFrame: 60 },
{ id: "BrandGradient", slug: "brand-gradient", posterFrame: 0 },
{ id: "The48", slug: "the-48", posterFrame: 420 },
{ id: "CloudVsLocal", slug: "cloud-vs-local", posterFrame: 380 },
];
const OUTPUT_DIR = path.resolve("./out");
const OUTPUT_DIR = path.resolve(__dirname, "../../landing/public/videos");
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
console.log("Bundling...");
console.log("Bundling Remotion project...");
const bundleLocation = await bundle({
entryPoint: "./src/index.ts",
entryPoint: path.resolve(__dirname, "../src/index.ts"),
webpackOverride: (config) => enableTailwind(config),
});
console.log("Bundle complete.\n");
for (const comp of COMPOSITIONS) {
console.log(`Rendering ${comp.id}...`);
const total = COMPOSITIONS.length;
for (let i = 0; i < total; i++) {
const comp = COMPOSITIONS[i];
console.log(`[${i + 1}/${total}] ${comp.id}`);
const composition = await selectComposition({
serveUrl: bundleLocation,
id: comp.id,
});
// MP4 (H.264, CRF 22)
console.log(" Rendering MP4 (H.264)...");
await renderMedia({
composition,
serveUrl: bundleLocation,
codec: "h264",
crf: 20,
crf: 22,
pixelFormat: "yuv420p",
imageFormat: "jpeg",
concurrency: 4,
outputLocation: path.join(OUTPUT_DIR, `${comp.slug}.mp4`),
onProgress: ({ progress }) => {
const pct = Math.round(progress * 100);
if (pct % 10 === 0) process.stdout.write(`\r ${pct}%`);
if (pct % 10 === 0) process.stdout.write(`\r MP4: ${pct}%`);
},
});
console.log(`\r MP4: done`);
console.log(`\r Done: ${comp.slug}.mp4`);
// WebM (VP9, CRF 31)
console.log(" Rendering WebM (VP9)...");
await renderMedia({
composition,
serveUrl: bundleLocation,
codec: "vp9",
crf: 31,
imageFormat: "jpeg",
concurrency: 4,
outputLocation: path.join(OUTPUT_DIR, `${comp.slug}.webm`),
onProgress: ({ progress }) => {
const pct = Math.round(progress * 100);
if (pct % 10 === 0) process.stdout.write(`\r WebM: ${pct}%`);
},
});
console.log(`\r WebM: done`);
// Poster frame (WebP still)
console.log(" Rendering poster frame...");
await renderStill({
composition,
serveUrl: bundleLocation,
frame: comp.posterFrame,
imageFormat: "webp",
output: path.join(OUTPUT_DIR, `${comp.slug}-poster.webp`),
});
console.log(" Poster: done\n");
}
console.log(`\nAll ${COMPOSITIONS.length} videos rendered to ${OUTPUT_DIR}`);
console.log(
`All ${total} compositions rendered (MP4 + WebM + poster) to ${OUTPUT_DIR}`,
);