remote-rdr / ssr.js
shiveshnavin's picture
Added job management
040aee9
import { renderFrames, renderStill, stitchFramesToVideo } from '@remotion/renderer';
import { bundle } from '@remotion/bundler';
import path from 'path';
import fs from 'fs';
import { execSync } from 'child_process';
const { RenderUtils } = await import('./src/RenderUtils.cjs');
const { GenerateScript } = await import('./src/GenerateScript.cjs');
//npx tsc src/GenerateScript.ts --module commonjs --outDir build --esModuleInterop --declaration false
const entry = path.join(process.cwd(), 'src/index.ts'); // your Remotion entry file
const originalManuScriptPath = path.join(process.cwd(), 'public/original_manuscript.json');
const compositionId = 'IGReelComposition';
const frameOutputDir = path.join(process.cwd(), 'frames');
const outputDir = path.join(process.cwd(), 'out');
const bundleLocation = path.join(process.cwd(), 'build');
const assetsInfoFile = path.join(process.cwd(), 'public/assetsInfo.json');
function readAssetInfo() {
if (!fs.existsSync(assetsInfoFile)) {
return undefined
}
let assetsInfoStr = fs.readFileSync(assetsInfoFile)
let assetsInfo = JSON.parse(assetsInfoStr);
return assetsInfo
}
function updateAssetInfo(assetInfoNew) {
fs.writeFileSync(assetsInfoFile, JSON.stringify(assetInfoNew))
}
export const renderSSR = async (outFile, startFrame, endFrame, controller) => {
const ScriptStr = fs.readFileSync(originalManuScriptPath);
const ScriptInput = JSON.parse(ScriptStr);
let {
duration,
Script,
contents,
intro,
outro,
playListsIDs
} = GenerateScript(ScriptInput)
console.log('Running "npm run bundle"...');
execSync('npm run bundle', { stdio: 'inherit' });
console.log('"npm run bundle" completed.');
console.log('Running "npm run skip-font-warn"...');
execSync('npm run skip-font-warn', { stdio: 'inherit' });
console.log('"npm run skip-font-warn" completed.');
let renderConfig = {
composition: {
id: compositionId,
fps: Script.meta.fps,
height: Script.meta?.resolution?.height,
width: Script.meta?.resolution?.width,
durationInFrames: duration,
defaultProps: Object.assign(Script, {
bgMusic: RenderUtils.getFileName(Script.bgMusic),
contents: contents,
intro: intro,
outro: outro
}),
props: Object.assign(Script, {
bgMusic: RenderUtils.getFileName(Script.bgMusic),
contents: contents,
intro: intro,
outro: outro
}),
},
serveUrl: bundleLocation,
output: path.join(frameOutputDir, 'output.jpg'),
// additional renderer options
audioCodec: 'mp3',
imageFormat: 'jpeg',
enableMultiProcessOnLinux: true,
jpegQuality: 70,
timeoutInMilliseconds: 60000,
concurrency: 1,
gl: 'angle'
}
const fps = Script.meta?.fps;
if (!fs.existsSync(frameOutputDir)) {
fs.mkdirSync(frameOutputDir, { recursive: true });
}
const frameNamePattern = 'frame-[frame].[ext]';
const formatMsToETA = (ms) => {
const totalSeconds = Math.ceil(ms / 1000);
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
const parts = [];
if (hours) parts.push(`${hours}h`);
if (minutes) parts.push(`${minutes}min`);
if (seconds || parts.length === 0) parts.push(`${seconds}sec`);
return parts.join(' ');
};
let result = await renderFrames({
...renderConfig,
onBrowserLog: (info) => {
console.log(`${info.type}: ${info.text}`);
console.log(
info.stackTrace
.map((stack) => {
return ` ${stack.url}:${stack.lineNumber}:${stack.columnNumber}`;
})
.join('\n'),
);
},
imageSequencePattern: frameNamePattern,
outputDir: frameOutputDir,
frameRange: [startFrame ?? 0, endFrame ?? duration - 1],
onFrameUpdate: (rendered, frameIndex, timeTakenPerFrameMs) => {
if (rendered % 10 !== 0 && frameIndex !== duration - 1) return;
const remainingFrames = Math.max(0, duration - frameIndex);
const estimatedMsLeft = remainingFrames * (timeTakenPerFrameMs || 0);
const timeLeftSeconds = formatMsToETA(estimatedMsLeft);
console.log(`Rendered ${frameIndex} of ${duration} in ${timeTakenPerFrameMs?.toFixed(0)}ms Time left ${timeLeftSeconds}`);
},
cancelSignal: (callback) => {
controller.stop = () => {
console.log('Stopping render studio ssr renderframes process');
callback();
}
}
});
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
const resultVideo = await stitchFramesToVideo({
assetsInfo: result.assetsInfo,
...renderConfig,
...renderConfig.composition,
audioCodec: 'mp3',
outputLocation: outFile,
verbose: true,
cancelSignal: (callback) => {
controller.stop = () => {
console.log('Stopping render studio ssr stitchFramesToVideo process');
callback();
}
}
})
console.log('resultVideo', resultVideo)
}