UNPKG

@remotion/renderer

Version:

Render Remotion videos using Node.js or Bun

384 lines (383 loc) • 18.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.renderStill = exports.internalRenderStill = void 0; const licensing_1 = require("@remotion/licensing"); const node_fs_1 = __importStar(require("node:fs")); const node_path_1 = __importDefault(require("node:path")); const no_react_1 = require("remotion/no-react"); const browser_1 = require("./browser"); const TimeoutSettings_1 = require("./browser/TimeoutSettings"); const browser_download_progress_bar_1 = require("./browser/browser-download-progress-bar"); const collect_assets_1 = require("./collect-assets"); const convert_to_positive_frame_index_1 = require("./convert-to-positive-frame-index"); const default_on_log_1 = require("./default-on-log"); const ensure_output_directory_1 = require("./ensure-output-directory"); const handle_javascript_exception_1 = require("./error-handling/handle-javascript-exception"); const filter_asset_types_1 = require("./filter-asset-types"); const find_closest_package_json_1 = require("./find-closest-package-json"); const image_format_1 = require("./image-format"); const jpeg_quality_1 = require("./jpeg-quality"); const logger_1 = require("./logger"); const make_cancel_signal_1 = require("./make-cancel-signal"); const get_available_memory_1 = require("./memory/get-available-memory"); const open_browser_1 = require("./open-browser"); const overwrite_1 = require("./overwrite"); const prepare_server_1 = require("./prepare-server"); const puppeteer_evaluate_1 = require("./puppeteer-evaluate"); const seek_to_frame_1 = require("./seek-to-frame"); const set_props_and_env_1 = require("./set-props-and-env"); const take_frame_1 = require("./take-frame"); const validate_1 = require("./validate"); const validate_puppeteer_timeout_1 = require("./validate-puppeteer-timeout"); const validate_scale_1 = require("./validate-scale"); const wrap_with_error_handling_1 = require("./wrap-with-error-handling"); const innerRenderStill = async ({ composition, imageFormat = image_format_1.DEFAULT_STILL_IMAGE_FORMAT, serveUrl, puppeteerInstance, onError, serializedInputPropsWithCustomSchema, envVariables, output, frame = 0, overwrite, browserExecutable, timeoutInMilliseconds, chromiumOptions, scale, proxyPort, cancelSignal, jpegQuality, onBrowserLog, sourceMapGetter, logLevel, indent, serializedResolvedPropsWithCustomSchema, onBrowserDownload, onArtifact, chromeMode, mediaCacheSizeInBytes, onLog, }) => { var _a; (0, validate_1.validateDimension)(composition.height, 'height', 'in the `config` object passed to `renderStill()`'); (0, validate_1.validateDimension)(composition.width, 'width', 'in the `config` object passed to `renderStill()`'); (0, validate_1.validateFps)(composition.fps, 'in the `config` object of `renderStill()`', false); (0, validate_1.validateDurationInFrames)(composition.durationInFrames, { component: 'in the `config` object passed to `renderStill()`', allowFloats: false, }); (0, image_format_1.validateStillImageFormat)(imageFormat); no_react_1.NoReactInternals.validateFrame({ frame, durationInFrames: composition.durationInFrames, allowFloats: false, }); const stillFrame = (0, convert_to_positive_frame_index_1.convertToPositiveFrameIndex)({ durationInFrames: composition.durationInFrames, frame, }); (0, validate_puppeteer_timeout_1.validatePuppeteerTimeout)(timeoutInMilliseconds); (0, validate_scale_1.validateScale)(scale); output = typeof output === 'string' ? node_path_1.default.resolve(process.cwd(), output) : null; (0, jpeg_quality_1.validateJpegQuality)(jpegQuality); if (output) { if (node_fs_1.default.existsSync(output)) { if (!overwrite) { throw new Error(`Cannot render still - "overwrite" option was set to false, but the output destination ${output} already exists.`); } const stat = (0, node_fs_1.statSync)(output); if (!stat.isFile()) { throw new Error(`The output location ${output} already exists, but is not a file, but something else (e.g. folder). Cannot save to it.`); } } (0, ensure_output_directory_1.ensureOutputDirectory)(output); } const browserInstance = puppeteerInstance !== null && puppeteerInstance !== void 0 ? puppeteerInstance : (await (0, open_browser_1.internalOpenBrowser)({ browser: browser_1.DEFAULT_BROWSER, browserExecutable, chromiumOptions, forceDeviceScaleFactor: scale, indent, viewport: null, logLevel, onBrowserDownload, chromeMode, })); const page = await browserInstance.newPage({ context: sourceMapGetter, logLevel, indent, pageIndex: 0, onBrowserLog, onLog, }); await page.setViewport({ width: composition.width, height: composition.height, deviceScaleFactor: scale, }); const errorCallback = (err) => { onError(err); cleanup(); }; const cleanUpJSException = (0, handle_javascript_exception_1.handleJavascriptException)({ page, onError: errorCallback, frame: null, }); const cleanup = async () => { cleanUpJSException(); if (puppeteerInstance) { await page.close(); } else { browserInstance.close({ silent: true }).catch((err) => { logger_1.Log.error({ indent, logLevel }, 'Unable to close browser', err); }); } }; cancelSignal === null || cancelSignal === void 0 ? void 0 : cancelSignal(() => { cleanup(); }); await (0, set_props_and_env_1.setPropsAndEnv)({ serializedInputPropsWithCustomSchema, envVariables, page, serveUrl, initialFrame: stillFrame, timeoutInMilliseconds, proxyPort, retriesRemaining: 2, audioEnabled: false, videoEnabled: true, indent, logLevel, onServeUrlVisited: () => undefined, isMainTab: true, mediaCacheSizeInBytes, initialMemoryAvailable: (0, get_available_memory_1.getAvailableMemory)(logLevel), darkMode: (_a = chromiumOptions.darkMode) !== null && _a !== void 0 ? _a : false, }); await (0, puppeteer_evaluate_1.puppeteerEvaluateWithCatch)({ // eslint-disable-next-line max-params pageFunction: (id, props, durationInFrames, fps, height, width, defaultCodec, defaultOutName, defaultVideoImageFormat, defaultPixelFormat, defaultProResProfile) => { window.remotion_setBundleMode({ type: 'composition', compositionName: id, serializedResolvedPropsWithSchema: props, compositionDurationInFrames: durationInFrames, compositionFps: fps, compositionHeight: height, compositionWidth: width, compositionDefaultCodec: defaultCodec, compositionDefaultOutName: defaultOutName, compositionDefaultVideoImageFormat: defaultVideoImageFormat, compositionDefaultPixelFormat: defaultPixelFormat, compositionDefaultProResProfile: defaultProResProfile, }); }, args: [ composition.id, serializedResolvedPropsWithCustomSchema, composition.durationInFrames, composition.fps, composition.height, composition.width, composition.defaultCodec, composition.defaultOutName, composition.defaultVideoImageFormat, composition.defaultPixelFormat, composition.defaultProResProfile, ], frame: null, page, timeoutInMilliseconds, }); await (0, seek_to_frame_1.seekToFrame)({ frame: stillFrame, page, composition: composition.id, timeoutInMilliseconds, indent, logLevel, attempt: 0, }); const [buffer, collectedAssets] = await Promise.all([ (0, take_frame_1.takeFrame)({ freePage: page, height: composition.height, width: composition.width, imageFormat, scale, output, jpegQuality, wantsBuffer: !output, timeoutInMilliseconds, }), (0, collect_assets_1.collectAssets)({ frame, freePage: page, timeoutInMilliseconds, }), ]); const artifactAssets = (0, filter_asset_types_1.onlyArtifact)({ assets: collectedAssets, frameBuffer: buffer, }); const previousArtifactAssets = []; for (const artifact of artifactAssets) { for (const previousArtifact of previousArtifactAssets) { if (artifact.filename === previousArtifact.filename) { throw new Error(`An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}. Artifacts must have unique names. https://remotion.dev/docs/artifacts`); } } previousArtifactAssets.push(artifact); onArtifact === null || onArtifact === void 0 ? void 0 : onArtifact(artifact); } await cleanup(); return { buffer: output ? null : buffer }; }; const internalRenderStillRaw = (options) => { const cleanup = []; const happyPath = new Promise((resolve, reject) => { var _a; const onError = (err) => reject(err); (0, prepare_server_1.makeOrReuseServer)(options.server, { webpackConfigOrServeUrl: options.serveUrl, port: options.port, remotionRoot: (0, find_closest_package_json_1.findRemotionRoot)(), offthreadVideoThreads: (_a = options.offthreadVideoThreads) !== null && _a !== void 0 ? _a : 2, logLevel: options.logLevel, indent: options.indent, offthreadVideoCacheSizeInBytes: options.offthreadVideoCacheSizeInBytes, binariesDirectory: options.binariesDirectory, forceIPv4: false, }, { onDownload: options.onDownload, }) .then(({ server, cleanupServer }) => { cleanup.push(() => cleanupServer(false)); const { serveUrl, offthreadPort, sourceMap: sourceMapGetter } = server; return innerRenderStill({ ...options, serveUrl, onError, proxyPort: offthreadPort, sourceMapGetter, }); }) .then((res) => { if (options.apiKey === null) { resolve(res); return; } (0, licensing_1.registerUsageEvent)({ apiKey: options.apiKey, event: 'cloud-render', host: null, succeeded: true, }) .then(() => { logger_1.Log.verbose(options, 'Usage event sent successfully'); }) .catch((err) => { logger_1.Log.error(options, 'Failed to send usage event'); logger_1.Log.error(options, err); }) .finally(() => { resolve(res); }); }) .catch((err) => reject(err)) .finally(() => { cleanup.forEach((c) => { c().catch((err) => { logger_1.Log.error(options, 'Cleanup error:', err); }); }); }); }); return Promise.race([ happyPath, new Promise((_resolve, reject) => { var _a; (_a = options.cancelSignal) === null || _a === void 0 ? void 0 : _a.call(options, () => { reject(new Error(make_cancel_signal_1.cancelErrorMessages.renderStill)); }); }), ]); }; exports.internalRenderStill = (0, wrap_with_error_handling_1.wrapWithErrorHandling)(internalRenderStillRaw); /* * @description Renders a single frame to an image and writes it to the specified output location. * @see [Documentation](https://www.remotion.dev/docs/renderer/render-still) */ const renderStill = (options) => { var _a, _b; const { composition, serveUrl, browserExecutable, cancelSignal, chromiumOptions, dumpBrowserLogs, envVariables, frame, imageFormat, inputProps, jpegQuality, onBrowserLog, onDownload, output, overwrite, port, puppeteerInstance, scale, timeoutInMilliseconds, verbose, quality, offthreadVideoCacheSizeInBytes, logLevel: passedLogLevel, binariesDirectory, onBrowserDownload, onArtifact, chromeMode, offthreadVideoThreads, mediaCacheSizeInBytes, apiKey, } = options; if (typeof jpegQuality !== 'undefined' && imageFormat !== 'jpeg') { throw new Error("You can only pass the `quality` option if `imageFormat` is 'jpeg'."); } const indent = false; const logLevel = passedLogLevel !== null && passedLogLevel !== void 0 ? passedLogLevel : (verbose || dumpBrowserLogs ? 'verbose' : 'info'); if (quality) { logger_1.Log.warn({ indent, logLevel }, 'Passing `quality()` to `renderStill` is deprecated. Use `jpegQuality` instead.'); } return (0, exports.internalRenderStill)({ composition, browserExecutable: browserExecutable !== null && browserExecutable !== void 0 ? browserExecutable : null, cancelSignal: cancelSignal !== null && cancelSignal !== void 0 ? cancelSignal : null, chromiumOptions: chromiumOptions !== null && chromiumOptions !== void 0 ? chromiumOptions : {}, envVariables: envVariables !== null && envVariables !== void 0 ? envVariables : {}, frame: frame !== null && frame !== void 0 ? frame : 0, imageFormat: imageFormat !== null && imageFormat !== void 0 ? imageFormat : image_format_1.DEFAULT_STILL_IMAGE_FORMAT, indent, serializedInputPropsWithCustomSchema: no_react_1.NoReactInternals.serializeJSONWithSpecialTypes({ staticBase: null, indent: undefined, data: inputProps !== null && inputProps !== void 0 ? inputProps : {}, }).serializedString, jpegQuality: (_a = jpegQuality !== null && jpegQuality !== void 0 ? jpegQuality : quality) !== null && _a !== void 0 ? _a : jpeg_quality_1.DEFAULT_JPEG_QUALITY, onBrowserLog: onBrowserLog !== null && onBrowserLog !== void 0 ? onBrowserLog : null, onDownload: onDownload !== null && onDownload !== void 0 ? onDownload : null, output: output !== null && output !== void 0 ? output : null, overwrite: overwrite !== null && overwrite !== void 0 ? overwrite : overwrite_1.DEFAULT_OVERWRITE, port: port !== null && port !== void 0 ? port : null, puppeteerInstance: puppeteerInstance !== null && puppeteerInstance !== void 0 ? puppeteerInstance : null, scale: scale !== null && scale !== void 0 ? scale : 1, server: undefined, serveUrl, timeoutInMilliseconds: timeoutInMilliseconds !== null && timeoutInMilliseconds !== void 0 ? timeoutInMilliseconds : TimeoutSettings_1.DEFAULT_TIMEOUT, logLevel, serializedResolvedPropsWithCustomSchema: no_react_1.NoReactInternals.serializeJSONWithSpecialTypes({ indent: undefined, staticBase: null, data: (_b = composition.props) !== null && _b !== void 0 ? _b : {}, }).serializedString, offthreadVideoCacheSizeInBytes: offthreadVideoCacheSizeInBytes !== null && offthreadVideoCacheSizeInBytes !== void 0 ? offthreadVideoCacheSizeInBytes : null, binariesDirectory: binariesDirectory !== null && binariesDirectory !== void 0 ? binariesDirectory : null, onBrowserDownload: onBrowserDownload !== null && onBrowserDownload !== void 0 ? onBrowserDownload : (0, browser_download_progress_bar_1.defaultBrowserDownloadProgress)({ indent, logLevel, api: 'renderStill()', }), onArtifact: onArtifact !== null && onArtifact !== void 0 ? onArtifact : null, chromeMode: chromeMode !== null && chromeMode !== void 0 ? chromeMode : 'headless-shell', offthreadVideoThreads: offthreadVideoThreads !== null && offthreadVideoThreads !== void 0 ? offthreadVideoThreads : null, mediaCacheSizeInBytes: mediaCacheSizeInBytes !== null && mediaCacheSizeInBytes !== void 0 ? mediaCacheSizeInBytes : null, apiKey: apiKey !== null && apiKey !== void 0 ? apiKey : null, onLog: default_on_log_1.defaultOnLog, }); }; exports.renderStill = renderStill;