Spaces:
Running
Running
Commit ·
9a00d8d
1
Parent(s): a49c134
Add proxy render
Browse files- package.ci.json +1 -1
- proxy-renderer.js +73 -52
- renderer.js +2 -2
- routes.js +3 -2
- ssr.js +2 -2
- start.sh +0 -32
package.ci.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
| 9 |
"scripts": {
|
| 10 |
"submodule": "git submodule init && git submodule update",
|
| 11 |
"ci": "echo Runnin on CI",
|
| 12 |
-
"start": "
|
| 13 |
"bundle": "echo -n | remotion bundle",
|
| 14 |
"skip-font-warn": "sed -E -i \"s/\\bfontsLoaded[[:space:]]*>[[:space:]]*20\\b/fontsLoaded > 20000/g\" build/bundle.js",
|
| 15 |
"extract32": "set MODIFY_FILES=1 && node app.js",
|
|
|
|
| 9 |
"scripts": {
|
| 10 |
"submodule": "git submodule init && git submodule update",
|
| 11 |
"ci": "echo Runnin on CI",
|
| 12 |
+
"start": "node server.js",
|
| 13 |
"bundle": "echo -n | remotion bundle",
|
| 14 |
"skip-font-warn": "sed -E -i \"s/\\bfontsLoaded[[:space:]]*>[[:space:]]*20\\b/fontsLoaded > 20000/g\" build/bundle.js",
|
| 15 |
"extract32": "set MODIFY_FILES=1 && node app.js",
|
proxy-renderer.js
CHANGED
|
@@ -2,69 +2,90 @@ import { renderFrames, renderStill, stitchFramesToVideo } from '@remotion/render
|
|
| 2 |
import { bundle } from '@remotion/bundler';
|
| 3 |
import path from 'path';
|
| 4 |
import fs from 'fs';
|
| 5 |
-
|
|
|
|
|
|
|
| 6 |
const { RenderUtils } = await import('./src/RenderUtils.cjs');
|
| 7 |
const { GenerateScript } = await import('./src/GenerateScript.cjs');
|
| 8 |
|
| 9 |
const originalManuScriptPath = path.join(process.cwd(), 'public/original_manuscript.json');
|
| 10 |
-
|
| 11 |
-
|
|
|
|
| 12 |
const ScriptStr = fs.readFileSync(originalManuScriptPath);
|
| 13 |
const ScriptInput = JSON.parse(ScriptStr);
|
| 14 |
let {
|
| 15 |
duration,
|
| 16 |
Script,
|
| 17 |
-
contents,
|
| 18 |
-
intro,
|
| 19 |
-
outro,
|
| 20 |
-
playListsIDs
|
| 21 |
} = GenerateScript(ScriptInput)
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
outro: outro
|
| 34 |
-
}),
|
| 35 |
-
props: Object.assign(Script, {
|
| 36 |
-
bgMusic: RenderUtils.getFileName(Script.bgMusic),
|
| 37 |
-
contents: contents,
|
| 38 |
-
intro: intro,
|
| 39 |
-
outro: outro
|
| 40 |
-
}),
|
| 41 |
-
},
|
| 42 |
-
serveUrl: bundleLocation,
|
| 43 |
-
output: path.join(frameOutputDir, 'output.jpg'),
|
| 44 |
-
// additional renderer options
|
| 45 |
-
audioCodec: 'mp3',
|
| 46 |
-
imageFormat: 'jpeg',
|
| 47 |
-
enableMultiProcessOnLinux: true,
|
| 48 |
-
jpegQuality: 70,
|
| 49 |
-
timeoutInMilliseconds: 60000,
|
| 50 |
-
concurrency: 1,
|
| 51 |
-
gl: 'angle'
|
| 52 |
-
}
|
| 53 |
-
|
| 54 |
-
if (!fs.existsSync(outputDir)) {
|
| 55 |
-
fs.mkdirSync(outputDir, { recursive: true });
|
| 56 |
-
}
|
| 57 |
|
| 58 |
-
const resultVideo = await stitchFramesToVideo({
|
| 59 |
-
assetsInfo: result.assetsInfo,
|
| 60 |
-
...renderConfig,
|
| 61 |
-
...renderConfig.composition,
|
| 62 |
-
audioCodec: 'mp3',
|
| 63 |
-
outputLocation: outFile,
|
| 64 |
-
verbose: true
|
| 65 |
-
})
|
| 66 |
-
|
| 67 |
-
console.log('resultVideo', resultVideo)
|
| 68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
}
|
|
|
|
| 2 |
import { bundle } from '@remotion/bundler';
|
| 3 |
import path from 'path';
|
| 4 |
import fs from 'fs';
|
| 5 |
+
import axios from 'axios';
|
| 6 |
+
import os from 'os'
|
| 7 |
+
import { exec } from 'child_process';
|
| 8 |
const { RenderUtils } = await import('./src/RenderUtils.cjs');
|
| 9 |
const { GenerateScript } = await import('./src/GenerateScript.cjs');
|
| 10 |
|
| 11 |
const originalManuScriptPath = path.join(process.cwd(), 'public/original_manuscript.json');
|
| 12 |
+
let cmd = `npm run preview`;
|
| 13 |
+
const childProcess = exec(cmd);
|
| 14 |
+
export const renderProxy = async (outFile, options) => {
|
| 15 |
const ScriptStr = fs.readFileSync(originalManuScriptPath);
|
| 16 |
const ScriptInput = JSON.parse(ScriptStr);
|
| 17 |
let {
|
| 18 |
duration,
|
| 19 |
Script,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
} = GenerateScript(ScriptInput)
|
| 21 |
+
const composition = ScriptInput?.meta?.renderComposition;
|
| 22 |
+
return new Promise((resolve, reject) => {
|
| 23 |
+
const renderOptions = {
|
| 24 |
+
compositionId: composition,
|
| 25 |
+
startFrame: options?.startFrame ?? 0,
|
| 26 |
+
endFrame: options?.endFrame ?? duration - 1,
|
| 27 |
+
logLevel: options?.logLevel ?? "verbose",
|
| 28 |
+
type: options?.type ?? "video",
|
| 29 |
+
outName: outFile ?? "out/output.mp4",
|
| 30 |
+
imageFormat: options?.imageFormat ?? "jpeg",
|
| 31 |
+
jpegQuality: options?.jpegQuality ?? 70,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
+
scale: options?.scale ?? 1,
|
| 35 |
+
codec: options?.codec ?? "h264",
|
| 36 |
+
concurrency: options?.concurrency ?? os.cpus().length,
|
| 37 |
+
crf: options?.crf ?? 18,
|
| 38 |
+
muted: options?.muted ?? false,
|
| 39 |
+
enforceAudioTrack: options?.enforceAudioTrack ?? false,
|
| 40 |
+
proResProfile: options?.proResProfile ?? null,
|
| 41 |
+
x264Preset: options?.x264Preset ?? "medium",
|
| 42 |
+
pixelFormat: options?.pixelFormat ?? "yuv420p",
|
| 43 |
+
audioBitrate: options?.audioBitrate ?? null,
|
| 44 |
+
videoBitrate: options?.videoBitrate ?? null,
|
| 45 |
+
everyNthFrame: options?.everyNthFrame ?? 1,
|
| 46 |
+
numberOfGifLoops: options?.numberOfGifLoops ?? null,
|
| 47 |
+
delayRenderTimeout: options?.delayRenderTimeout ?? 300000,
|
| 48 |
+
audioCodec: options?.audioCodec ?? "aac",
|
| 49 |
+
disallowParallelEncoding: options?.disallowParallelEncoding ?? false,
|
| 50 |
+
chromiumOptions: {
|
| 51 |
+
headless: options?.chromiumOptions?.headless ?? true,
|
| 52 |
+
disableWebSecurity: options?.chromiumOptions?.disableWebSecurity ?? false,
|
| 53 |
+
ignoreCertificateErrors: options?.chromiumOptions?.ignoreCertificateErrors ?? false,
|
| 54 |
+
gl: options?.chromiumOptions?.gl ?? null,
|
| 55 |
+
userAgent: options?.chromiumOptions?.userAgent ?? null,
|
| 56 |
+
enableMultiProcessOnLinux: options?.chromiumOptions?.enableMultiProcessOnLinux ?? true
|
| 57 |
+
},
|
| 58 |
+
envVariables: options?.envVariables ?? {},
|
| 59 |
+
serializedInputPropsWithCustomSchema: options?.serializedInputPropsWithCustomSchema ?? JSON.stringify(Script),
|
| 60 |
+
offthreadVideoCacheSizeInBytes: options?.offthreadVideoCacheSizeInBytes ?? null,
|
| 61 |
+
offthreadVideoThreads: options?.offthreadVideoThreads ?? null,
|
| 62 |
+
colorSpace: options?.colorSpace ?? "default",
|
| 63 |
+
multiProcessOnLinux: options?.multiProcessOnLinux ?? true,
|
| 64 |
+
encodingBufferSize: options?.encodingBufferSize ?? null,
|
| 65 |
+
encodingMaxRate: options?.encodingMaxRate ?? null,
|
| 66 |
+
beepOnFinish: options?.beepOnFinish ?? false,
|
| 67 |
+
repro: options?.repro ?? false,
|
| 68 |
+
forSeamlessAacConcatenation: options?.forSeamlessAacConcatenation ?? false,
|
| 69 |
+
separateAudioTo: options?.separateAudioTo ?? null,
|
| 70 |
+
hardwareAcceleration: options?.hardwareAcceleration ?? "disable",
|
| 71 |
+
chromeMode: options?.chromeMode ?? "headless-shell"
|
| 72 |
+
};
|
| 73 |
+
console.log('Invoking studio with', renderOptions)
|
| 74 |
+
axios.post('http://localhost:3000/api/render', renderOptions).then(resp => {
|
| 75 |
+
console.log('Studio started render', resp.data)
|
| 76 |
|
| 77 |
+
childProcess.stdout.on('data', (data) => {
|
| 78 |
+
console.log(data)
|
| 79 |
+
if (data.includes('Cleanup: Closing browser instance')) {
|
| 80 |
+
resolve(outFile)
|
| 81 |
+
}
|
| 82 |
+
});
|
| 83 |
+
childProcess.stderr.on('data', (data) => {
|
| 84 |
+
console.error(data)
|
| 85 |
+
if (data.includes('Failed to render')) {
|
| 86 |
+
reject(data)
|
| 87 |
+
}
|
| 88 |
+
});
|
| 89 |
+
}).catch(reject)
|
| 90 |
+
})
|
| 91 |
}
|
renderer.js
CHANGED
|
@@ -51,12 +51,12 @@ export function getNpmScript(mediaType) {
|
|
| 51 |
export async function doRender(jobId, composition, sendToObserver, target = 'render', ssrOptions, proxyOptions) {
|
| 52 |
let outFile = path.join(process.cwd(), `out`, `${jobId}-video.mp4`);
|
| 53 |
if (ssrOptions) {
|
| 54 |
-
await renderSSR(outFile, ssrOptions.
|
| 55 |
sendToObserver(jobId, 'completed');
|
| 56 |
return outFile
|
| 57 |
}
|
| 58 |
else if (proxyOptions) {
|
| 59 |
-
await renderProxy(outFile, proxyOptions
|
| 60 |
sendToObserver(jobId, 'completed');
|
| 61 |
return outFile
|
| 62 |
}
|
|
|
|
| 51 |
export async function doRender(jobId, composition, sendToObserver, target = 'render', ssrOptions, proxyOptions) {
|
| 52 |
let outFile = path.join(process.cwd(), `out`, `${jobId}-video.mp4`);
|
| 53 |
if (ssrOptions) {
|
| 54 |
+
await renderSSR(outFile, ssrOptions.startFrame, ssrOptions.endFrame)
|
| 55 |
sendToObserver(jobId, 'completed');
|
| 56 |
return outFile
|
| 57 |
}
|
| 58 |
else if (proxyOptions) {
|
| 59 |
+
await renderProxy(outFile, proxyOptions)
|
| 60 |
sendToObserver(jobId, 'completed');
|
| 61 |
return outFile
|
| 62 |
}
|
routes.js
CHANGED
|
@@ -58,7 +58,7 @@ RenderRouter.post('/api/render-sync', async (req, res) => {
|
|
| 58 |
let pref = new PerformanceRecorder();
|
| 59 |
const dir = path.join(__dirname, 'public')
|
| 60 |
const zipFile = path.join(dir, `exported-${jobId}.zip`)
|
| 61 |
-
if (!existsSync(zipFile)) {
|
| 62 |
if (!skipClear) {
|
| 63 |
clear();
|
| 64 |
}
|
|
@@ -78,7 +78,8 @@ RenderRouter.post('/api/render-sync', async (req, res) => {
|
|
| 78 |
logs.push(log);
|
| 79 |
},
|
| 80 |
getNpmScript(req.query.media_type),
|
| 81 |
-
req.body.
|
|
|
|
| 82 |
);
|
| 83 |
}
|
| 84 |
const uploader = new FileUploader('oracle', {
|
|
|
|
| 58 |
let pref = new PerformanceRecorder();
|
| 59 |
const dir = path.join(__dirname, 'public')
|
| 60 |
const zipFile = path.join(dir, `exported-${jobId}.zip`)
|
| 61 |
+
if (!existsSync(zipFile) || req.body.force || req.query.force) {
|
| 62 |
if (!skipClear) {
|
| 63 |
clear();
|
| 64 |
}
|
|
|
|
| 78 |
logs.push(log);
|
| 79 |
},
|
| 80 |
getNpmScript(req.query.media_type),
|
| 81 |
+
req.body.ssr,
|
| 82 |
+
req.body.proxy
|
| 83 |
);
|
| 84 |
}
|
| 85 |
const uploader = new FileUploader('oracle', {
|
ssr.js
CHANGED
|
@@ -28,7 +28,7 @@ function updateAssetInfo(assetInfoNew) {
|
|
| 28 |
fs.writeFileSync(assetsInfoFile, JSON.stringify(assetInfoNew))
|
| 29 |
}
|
| 30 |
|
| 31 |
-
export const renderSSR = async (outFile,
|
| 32 |
const ScriptStr = fs.readFileSync(originalManuScriptPath);
|
| 33 |
const ScriptInput = JSON.parse(ScriptStr);
|
| 34 |
let {
|
|
@@ -103,7 +103,7 @@ export const renderSSR = async (outFile, frameRange) => {
|
|
| 103 |
},
|
| 104 |
imageSequencePattern: frameNamePattern,
|
| 105 |
outputDir: frameOutputDir,
|
| 106 |
-
frameRange:
|
| 107 |
onFrameUpdate: (rendered, frameIndex, timeTakenPerFrameMs) => {
|
| 108 |
if (rendered % 10 !== 0 && frameIndex !== duration - 1) return;
|
| 109 |
const remainingFrames = Math.max(0, duration - frameIndex);
|
|
|
|
| 28 |
fs.writeFileSync(assetsInfoFile, JSON.stringify(assetInfoNew))
|
| 29 |
}
|
| 30 |
|
| 31 |
+
export const renderSSR = async (outFile, startFrame, endFrame) => {
|
| 32 |
const ScriptStr = fs.readFileSync(originalManuScriptPath);
|
| 33 |
const ScriptInput = JSON.parse(ScriptStr);
|
| 34 |
let {
|
|
|
|
| 103 |
},
|
| 104 |
imageSequencePattern: frameNamePattern,
|
| 105 |
outputDir: frameOutputDir,
|
| 106 |
+
frameRange: [startFrame ?? 0, endFrame ?? duration - 1],
|
| 107 |
onFrameUpdate: (rendered, frameIndex, timeTakenPerFrameMs) => {
|
| 108 |
if (rendered % 10 !== 0 && frameIndex !== duration - 1) return;
|
| 109 |
const remainingFrames = Math.max(0, duration - frameIndex);
|
start.sh
CHANGED
|
@@ -1,45 +1,13 @@
|
|
| 1 |
#!/usr/bin/env bash
|
| 2 |
set -e
|
| 3 |
|
| 4 |
-
# Optional: redirect logs to file /var/log/app.log if you like
|
| 5 |
-
# Start your app in background. Assumes `npm start` binds to 0.0.0.0:3000 (and 8083 for renderer).
|
| 6 |
-
# If your app needs env vars set, ensure they are available to the container at runtime.
|
| 7 |
-
|
| 8 |
echo "Starting app (npm start) in background..."
|
| 9 |
npm start &
|
| 10 |
|
| 11 |
APP_PID=$!
|
| 12 |
echo "App PID: $APP_PID"
|
| 13 |
|
| 14 |
-
# Give the app a short moment to start and bind sockets (adjust if needed)
|
| 15 |
sleep 1
|
| 16 |
|
| 17 |
-
# Start nginx in foreground to keep the container alive
|
| 18 |
echo "Starting nginx (foreground)..."
|
| 19 |
nginx -g 'daemon off;'
|
| 20 |
-
|
| 21 |
-
# when nginx exits, container exits (and child app will be killed)
|
| 22 |
-
# export DISPLAY=:10.0 && is_pm2=1 node server.js
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
# sudo nano /etc/systemd/system/media-render-farm.service
|
| 26 |
-
# sudo systemctl status media-render-farm.service
|
| 27 |
-
# sudo systemctl restart media-render-farm.service
|
| 28 |
-
# sudo systemctl daemon-reload
|
| 29 |
-
# sudo systemctl restart media-render-farm.service
|
| 30 |
-
# journalctl -u media-render-farm.service -f
|
| 31 |
-
|
| 32 |
-
# [Unit]
|
| 33 |
-
# Description=Semibit Media Render Farm
|
| 34 |
-
# After=network.target
|
| 35 |
-
|
| 36 |
-
# [Service]
|
| 37 |
-
# ExecStart=/home/ubuntu/apps/node/semibit-media-render-farm/sstart.sh
|
| 38 |
-
# WorkingDirectory=/home/ubuntu/apps/node/semibit-media-render-farm
|
| 39 |
-
# Restart=always
|
| 40 |
-
# User=ubuntu
|
| 41 |
-
# Group=ubuntu
|
| 42 |
-
# Environment="NODE_ENV=production"
|
| 43 |
-
|
| 44 |
-
# [Install]
|
| 45 |
-
# WantedBy=multi-user.target
|
|
|
|
| 1 |
#!/usr/bin/env bash
|
| 2 |
set -e
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
echo "Starting app (npm start) in background..."
|
| 5 |
npm start &
|
| 6 |
|
| 7 |
APP_PID=$!
|
| 8 |
echo "App PID: $APP_PID"
|
| 9 |
|
|
|
|
| 10 |
sleep 1
|
| 11 |
|
|
|
|
| 12 |
echo "Starting nginx (foreground)..."
|
| 13 |
nginx -g 'daemon off;'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|