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) }