UNPKG

@remotion/renderer

Version:

Render Remotion videos using Node.js or Bun

183 lines (182 loc) • 7.78 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.startCompositor = exports.startLongRunningCompositor = void 0; const streaming_1 = require("@remotion/streaming"); const node_child_process_1 = require("node:child_process"); const node_path_1 = __importDefault(require("node:path")); const log_level_1 = require("../log-level"); const logger_1 = require("../logger"); const get_executable_path_1 = require("./get-executable-path"); const make_file_executable_1 = require("./make-file-executable"); const make_nonce_1 = require("./make-nonce"); const serialize_command_1 = require("./serialize-command"); const startLongRunningCompositor = ({ maximumFrameCacheItemsInBytes, logLevel, indent, binariesDirectory, extraThreads, }) => { return (0, exports.startCompositor)({ type: 'StartLongRunningProcess', payload: { concurrency: extraThreads, maximum_frame_cache_size_in_bytes: maximumFrameCacheItemsInBytes, verbose: (0, log_level_1.isEqualOrBelowLogLevel)(logLevel, 'verbose'), }, logLevel, indent, binariesDirectory, }); }; exports.startLongRunningCompositor = startLongRunningCompositor; const startCompositor = ({ type, payload, logLevel, indent, binariesDirectory = null, }) => { var _a; const bin = (0, get_executable_path_1.getExecutablePath)({ type: 'compositor', indent, logLevel, binariesDirectory, }); (0, make_file_executable_1.makeFileExecutableIfItIsNot)(bin); const fullCommand = (0, serialize_command_1.serializeCommand)(type, payload); const cwd = node_path_1.default.dirname(bin); const child = (0, node_child_process_1.spawn)(bin, [JSON.stringify(fullCommand)], { cwd, env: process.platform === 'darwin' ? { // Should work out of the box, but sometimes it doesn't // https://github.com/remotion-dev/remotion/issues/3862 DYLD_LIBRARY_PATH: cwd, } : undefined, }); let stderrChunks = []; const waiters = new Map(); const onMessage = (statusType, nonce, data) => { // Nonce '0' just means that the message should be logged if (nonce === '0') { logger_1.Log.verbose({ indent, logLevel, tag: 'compositor' }, new TextDecoder('utf8').decode(data)); } else if (waiters.has(nonce)) { if (statusType === 'error') { try { const parsed = JSON.parse(new TextDecoder('utf8').decode(data)); waiters.get(nonce).reject(new Error(`Compositor error: ${parsed.error}\n${parsed.backtrace}`)); } catch (_a) { waiters.get(nonce).reject(new Error(new TextDecoder('utf8').decode(data))); } } else { waiters.get(nonce).resolve(data); } waiters.delete(nonce); } }; const { onData, getOutputBuffer, clear } = (0, streaming_1.makeStreamer)(onMessage); let runningStatus = { type: 'running' }; child.stdout.on('data', onData); child.stderr.on('data', (data) => { stderrChunks.push(data); }); let resolve = null; let reject = null; child.on('close', (code, signal) => { const waitersToKill = Array.from(waiters.values()); if (code === 0) { runningStatus = { type: 'quit-without-error', signal }; resolve === null || resolve === void 0 ? void 0 : resolve(); for (const waiter of waitersToKill) { waiter.reject(new Error(`Compositor quit${signal ? ` with signal ${signal}` : ''}`)); } waiters.clear(); } else { const errorMessage = Buffer.concat(stderrChunks).toString('utf-8') + new TextDecoder('utf-8').decode(getOutputBuffer()); runningStatus = { type: 'quit-with-error', error: errorMessage, signal }; logger_1.Log.verbose({ indent, logLevel }, `Compositor exited with code ${code} and signal ${signal}`); const error = code === null ? new Error(`Compositor exited with signal ${signal}`) : new Error(`Compositor exited with code ${code}: ${errorMessage}`); for (const waiter of waitersToKill) { waiter.reject(error); } waiters.clear(); reject === null || reject === void 0 ? void 0 : reject(error); } // Need to manually free up memory clear(); stderrChunks = []; }); return { waitForDone: () => { return new Promise((res, rej) => { if (runningStatus.type === 'quit-without-error') { rej(new Error(`Compositor quit${runningStatus.signal ? ` with signal ${runningStatus.signal}` : ''}`)); return; } if (runningStatus.type === 'quit-with-error') { rej(new Error(`Compositor quit${runningStatus.signal ? ` with signal ${runningStatus.signal}` : ''}: ${runningStatus.error}`)); return; } resolve = res; reject = rej; }); }, finishCommands: () => { if (runningStatus.type === 'quit-with-error') { return Promise.reject(new Error(`Compositor quit${runningStatus.signal ? ` with signal ${runningStatus.signal}` : ''}: ${runningStatus.error}`)); } if (runningStatus.type === 'quit-without-error') { return Promise.reject(new Error(`Compositor quit${runningStatus.signal ? ` with signal ${runningStatus.signal}` : ''}`)); } return new Promise((res, rej) => { child.stdin.write('EOF\n', (e) => { if (e) { rej(e); return; } res(); }); }); }, executeCommand: (command, params) => { return new Promise((_resolve, _reject) => { if (runningStatus.type === 'quit-without-error') { _reject(new Error(`Compositor quit${runningStatus.signal ? ` with signal ${runningStatus.signal}` : ''}`)); return; } if (runningStatus.type === 'quit-with-error') { _reject(new Error(`Compositor quit${runningStatus.signal ? ` with signal ${runningStatus.signal}` : ''}: ${runningStatus.error.trim()}`)); return; } const nonce = (0, make_nonce_1.makeNonce)(); const composed = { nonce, payload: { type: command, params, }, }; child.stdin.write(JSON.stringify(composed) + '\n', (e) => { if (e) { _reject(e); } }); waiters.set(nonce, { resolve: _resolve, reject: _reject, }); }); }, pid: (_a = child.pid) !== null && _a !== void 0 ? _a : null, }; }; exports.startCompositor = startCompositor;