"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.renderVideoFlow = void 0; const renderer_1 = require("@remotion/renderer"); const client_1 = require("@remotion/renderer/client"); const studio_shared_1 = require("@remotion/studio-shared"); const node_fs_1 = __importStar(require("node:fs")); const node_os_1 = __importDefault(require("node:os")); const node_path_1 = __importDefault(require("node:path")); const no_react_1 = require("remotion/no-react"); const browser_download_bar_1 = require("../browser-download-bar"); const chalk_1 = require("../chalk"); const config_1 = require("../config"); const get_cli_options_1 = require("../get-cli-options"); const get_composition_with_dimension_override_1 = require("../get-composition-with-dimension-override"); const get_filename_1 = require("../get-filename"); const make_link_1 = require("../hyperlinks/make-link"); const image_formats_1 = require("../image-formats"); const log_1 = require("../log"); const make_on_download_1 = require("../make-on-download"); const on_artifact_1 = require("../on-artifact"); const parsed_cli_1 = require("../parsed-cli"); const progress_bar_1 = require("../progress-bar"); const setup_cache_1 = require("../setup-cache"); const should_use_non_overlaying_logger_1 = require("../should-use-non-overlaying-logger"); const truthy_1 = require("../truthy"); const user_passed_output_location_1 = require("../user-passed-output-location"); const add_log_to_aggregate_progress_1 = require("./add-log-to-aggregate-progress"); const renderVideoFlow = async ({ remotionRoot, fullEntryPoint, indent, logLevel, browserExecutable, browser, chromiumOptions, scale, shouldOutputImageSequence, publicDir, envVariables, puppeteerTimeout, port, height, width, remainingArgs, compositionIdFromUi, entryPointReason, overwrite, quiet, concurrency, frameRange, everyNthFrame, outputLocationFromUI, jpegQuality, onProgress, addCleanupCallback, cancelSignal, crf, uiCodec, uiImageFormat, ffmpegOverride, audioBitrate, muted, enforceAudioTrack, proResProfile, x264Preset, pixelFormat, videoBitrate, encodingMaxRate, encodingBufferSize, numberOfGifLoops, audioCodec, serializedInputPropsWithCustomSchema, disallowParallelEncoding, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, colorSpace, repro, binariesDirectory, forSeamlessAacConcatenation, separateAudioTo, publicPath, metadata, hardwareAcceleration, chromeMode, audioLatencyHint, imageSequencePattern, mediaCacheSizeInBytes, askAIEnabled, experimentalClientSideRenderingEnabled, keyboardShortcutsEnabled, }) => { var _a; let bundlingProgress = null; let renderingProgress = null; let stitchingProgress = null; let browserState = { progress: 0, doneIn: 0, alreadyAvailable: true, }; let copyingState = { bytes: 0, doneIn: null, }; const logsProgress = []; let artifactState = { received: [] }; const isVerbose = renderer_1.RenderInternals.isEqualOrBelowLogLevel(logLevel, 'verbose'); (0, progress_bar_1.printFact)('verbose')({ indent, logLevel, left: 'Entry point', right: [fullEntryPoint, isVerbose ? `(${entryPointReason})` : null] .filter(truthy_1.truthy) .join(' '), color: 'gray', }); const downloads = []; const onBrowserDownload = (0, browser_download_bar_1.defaultBrowserDownloadProgress)({ indent, onProgress: updateBrowserProgress, logLevel, quiet: (0, parsed_cli_1.quietFlagProvided)(), }); await renderer_1.RenderInternals.internalEnsureBrowser({ browserExecutable, indent, logLevel, onBrowserDownload, chromeMode, }); const browserInstance = renderer_1.RenderInternals.internalOpenBrowser({ browser, browserExecutable, chromiumOptions, forceDeviceScaleFactor: scale, indent, viewport: null, logLevel, onBrowserDownload, chromeMode, }); let isUsingParallelEncoding = false; const updatesDontOverwrite = (0, should_use_non_overlaying_logger_1.shouldUseNonOverlayingLogger)({ logLevel }); const renderProgress = (0, progress_bar_1.createOverwriteableCliOutput)({ quiet, cancelSignal, updatesDontOverwrite, indent, }); function updateBrowserProgress(progress) { browserState = progress; const aggregateRenderProgress = { browser: browserState, rendering: renderingProgress, stitching: shouldOutputImageSequence ? null : stitchingProgress, downloads, bundling: bundlingProgress, copyingState, artifactState, logs: logsProgress, }; onProgress({ message: `Downloading ${chromeMode === 'chrome-for-testing' ? 'Chrome for Testing' : 'Headless Shell'} ${Math.round(progress.progress * 100)}%`, value: 0, ...aggregateRenderProgress, }); } const updateRenderProgress = ({ newline, printToConsole, }) => { const aggregateRenderProgress = { rendering: renderingProgress, stitching: shouldOutputImageSequence ? null : stitchingProgress, downloads, browser: browserState, bundling: bundlingProgress, copyingState, artifactState, logs: logsProgress, }; const { output, message, progress } = (0, progress_bar_1.makeRenderingAndStitchingProgress)({ prog: aggregateRenderProgress, isUsingParallelEncoding, }); onProgress({ message, value: progress, ...aggregateRenderProgress }); if (printToConsole) { renderProgress.update(updatesDontOverwrite ? message : output, newline); } }; const { urlOrBundle, cleanup: cleanupBundle } = await (0, setup_cache_1.bundleOnCliOrTakeServeUrl)({ fullPath: fullEntryPoint, remotionRoot, publicDir, onProgress: ({ bundling, copying }) => { bundlingProgress = bundling; copyingState = copying; updateRenderProgress({ newline: false, printToConsole: true }); }, indentOutput: indent, logLevel, onDirectoryCreated: (dir) => { addCleanupCallback(`Delete ${dir}`, () => renderer_1.RenderInternals.deleteDirectory(dir)); }, quietProgress: updatesDontOverwrite, quietFlag: (0, parsed_cli_1.quietFlagProvided)(), outDir: null, // Not needed for render gitSource: null, bufferStateDelayInMilliseconds: null, maxTimelineTracks: null, publicPath, audioLatencyHint, experimentalClientSideRenderingEnabled, askAIEnabled, keyboardShortcutsEnabled, }); addCleanupCallback(`Cleanup bundle`, () => cleanupBundle()); const onDownload = (0, make_on_download_1.makeOnDownload)({ downloads, indent, logLevel, updateRenderProgress, updatesDontOverwrite, isUsingParallelEncoding, }); const puppeteerInstance = await browserInstance; addCleanupCallback(`Closing browser instance`, () => puppeteerInstance.close({ silent: false })); const resolvedConcurrency = renderer_1.RenderInternals.resolveConcurrency(concurrency); const server = await renderer_1.RenderInternals.prepareServer({ offthreadVideoThreads: offthreadVideoThreads !== null && offthreadVideoThreads !== void 0 ? offthreadVideoThreads : renderer_1.RenderInternals.DEFAULT_RENDER_FRAMES_OFFTHREAD_VIDEO_THREADS, indent, port, remotionRoot, logLevel, webpackConfigOrServeUrl: urlOrBundle, offthreadVideoCacheSizeInBytes, binariesDirectory, forceIPv4: false, }); addCleanupCallback(`Close server`, () => server.closeServer(false)); const { compositionId, config, reason, argsAfterComposition } = await (0, get_composition_with_dimension_override_1.getCompositionWithDimensionOverride)({ height, width, args: remainingArgs, compositionIdFromUi, browserExecutable, chromiumOptions, envVariables, indent, serializedInputPropsWithCustomSchema, port, puppeteerInstance, serveUrlOrWebpackUrl: urlOrBundle, timeoutInMilliseconds: puppeteerTimeout, logLevel, server, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, binariesDirectory, onBrowserDownload, chromeMode, mediaCacheSizeInBytes, }); const { onArtifact } = (0, on_artifact_1.handleOnArtifact)({ artifactState, onProgress: (progress) => { artifactState = progress; updateRenderProgress({ newline: false, printToConsole: !updatesDontOverwrite, }); }, compositionId, }); const { value: codec, source: codecReason } = client_1.BrowserSafeApis.options.videoCodecOption.getValue({ commandLine: parsed_cli_1.parsedCli, }, { configFile: (_a = config_1.ConfigInternals.getOutputCodecOrUndefined()) !== null && _a !== void 0 ? _a : null, downloadName: null, outName: (0, user_passed_output_location_1.getUserPassedOutputLocation)(argsAfterComposition, outputLocationFromUI), uiCodec, compositionCodec: config.defaultCodec, }); renderer_1.RenderInternals.validateEvenDimensionsWithCodec({ width: config.width, height: config.height, codec, scale, wantsImageSequence: shouldOutputImageSequence, indent, logLevel, }); const relativeOutputLocation = (0, get_filename_1.getOutputFilename)({ imageSequence: shouldOutputImageSequence, compositionName: compositionId, compositionDefaultOutName: config.defaultOutName, defaultExtension: renderer_1.RenderInternals.getFileExtensionFromCodec(codec, audioCodec), args: argsAfterComposition, indent, fromUi: outputLocationFromUI, logLevel, }); (0, progress_bar_1.printFact)('info')({ indent, logLevel, left: 'Composition', right: [compositionId, isVerbose ? `(${reason})` : null] .filter(truthy_1.truthy) .join(' '), color: 'gray', link: 'https://www.remotion.dev/docs/terminology/composition', }); (0, progress_bar_1.printFact)('info')({ indent, logLevel, left: 'Codec', link: 'https://www.remotion.dev/docs/encoding', right: [codec, isVerbose ? `(${codecReason})` : null] .filter(truthy_1.truthy) .join(' '), color: 'gray', }); (0, progress_bar_1.printFact)('info')({ indent, logLevel, left: 'Output', right: relativeOutputLocation, color: 'gray', }); (0, progress_bar_1.printFact)('info')({ indent, logLevel, left: 'Concurrency', link: 'https://www.remotion.dev/docs/terminology/concurrency', right: `${resolvedConcurrency}x`, color: 'gray', }); const absoluteOutputFile = (0, get_cli_options_1.getAndValidateAbsoluteOutputFile)(relativeOutputLocation, overwrite, logLevel); const absoluteSeparateAudioTo = separateAudioTo === null ? null : node_path_1.default.resolve(separateAudioTo); const exists = (0, node_fs_1.existsSync)(absoluteOutputFile); const audioExists = absoluteSeparateAudioTo ? (0, node_fs_1.existsSync)(absoluteSeparateAudioTo) : false; const realFrameRange = renderer_1.RenderInternals.getRealFrameRange(config.durationInFrames, frameRange); const totalFrames = renderer_1.RenderInternals.getFramesToRender(realFrameRange, everyNthFrame); renderingProgress = { frames: 0, totalFrames: totalFrames.length, doneIn: null, timeRemainingInMilliseconds: null, }; const imageFormat = (0, image_formats_1.getVideoImageFormat)({ codec: shouldOutputImageSequence ? undefined : codec, uiImageFormat, }); const onLog = ({ logLevel: logLogLevel, previewString, tag }) => { (0, add_log_to_aggregate_progress_1.addLogToAggregateProgress)({ logs: logsProgress, logLogLevel, previewString, tag, logLevel, }); if (!updatesDontOverwrite) { updateRenderProgress({ newline: false, printToConsole: !updatesDontOverwrite, }); } else { log_1.Log[logLogLevel]({ indent, logLevel, tag, }, previewString); } }; if (shouldOutputImageSequence) { node_fs_1.default.mkdirSync(absoluteOutputFile, { recursive: true, }); if (imageFormat === 'none') { throw new Error(`Cannot render an image sequence with a codec that renders no images. codec = ${codec}, imageFormat = ${imageFormat}`); } const outputDir = shouldOutputImageSequence ? absoluteOutputFile : await node_fs_1.default.promises.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'react-motion-render')); log_1.Log.verbose({ indent, logLevel }, 'Output dir', outputDir); await renderer_1.RenderInternals.internalRenderFrames({ imageFormat, serializedInputPropsWithCustomSchema, onFrameUpdate: (rendered) => { renderingProgress.frames = rendered; updateRenderProgress({ newline: false, printToConsole: true }); }, onStart: ({ parallelEncoding }) => { isUsingParallelEncoding = parallelEncoding; }, onDownload, cancelSignal: cancelSignal !== null && cancelSignal !== void 0 ? cancelSignal : undefined, outputDir, webpackBundleOrServeUrl: urlOrBundle, everyNthFrame, envVariables, frameRange, concurrency: resolvedConcurrency, puppeteerInstance, jpegQuality: jpegQuality !== null && jpegQuality !== void 0 ? jpegQuality : renderer_1.RenderInternals.DEFAULT_JPEG_QUALITY, timeoutInMilliseconds: puppeteerTimeout, chromiumOptions, scale, browserExecutable, port, composition: config, server, indent, muted, onBrowserLog: null, onFrameBuffer: null, logLevel, serializedResolvedPropsWithCustomSchema: no_react_1.NoReactInternals.serializeJSONWithSpecialTypes({ indent: undefined, staticBase: null, data: config.props, }).serializedString, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, parallelEncodingEnabled: isUsingParallelEncoding, binariesDirectory, compositionStart: 0, forSeamlessAacConcatenation, onBrowserDownload, onArtifact, chromeMode, imageSequencePattern, mediaCacheSizeInBytes, onLog, }); if (!updatesDontOverwrite) { updateRenderProgress({ newline: true, printToConsole: true }); } log_1.Log.info({ indent, logLevel }, chalk_1.chalk.blue(`${(exists ? '○' : '+').padEnd(progress_bar_1.LABEL_WIDTH)} ${(0, make_link_1.makeHyperlink)({ url: `file://${absoluteOutputFile}`, text: relativeOutputLocation, fallback: relativeOutputLocation })}`)); return; } stitchingProgress = { doneIn: null, frames: 0, stage: 'encoding', totalFrames: totalFrames.length, codec, }; const { slowestFrames } = await renderer_1.RenderInternals.internalRenderMedia({ outputLocation: absoluteOutputFile, composition: { ...config, width: width !== null && width !== void 0 ? width : config.width, height: height !== null && height !== void 0 ? height : config.height, }, crf: crf !== null && crf !== void 0 ? crf : null, envVariables, frameRange, serializedInputPropsWithCustomSchema, overwrite, pixelFormat, proResProfile, x264Preset: x264Preset !== null && x264Preset !== void 0 ? x264Preset : null, jpegQuality: jpegQuality !== null && jpegQuality !== void 0 ? jpegQuality : renderer_1.RenderInternals.DEFAULT_JPEG_QUALITY, chromiumOptions, timeoutInMilliseconds: puppeteerTimeout, scale, port, numberOfGifLoops, everyNthFrame, logLevel, muted, enforceAudioTrack, browserExecutable, ffmpegOverride, concurrency, serveUrl: urlOrBundle, codec, audioBitrate, videoBitrate, encodingMaxRate, encodingBufferSize, onProgress: (update) => { stitchingProgress.doneIn = update.encodedDoneIn; stitchingProgress.frames = update.encodedFrames; stitchingProgress.stage = update.stitchStage; renderingProgress.doneIn = update.renderedDoneIn; renderingProgress.frames = update.renderedFrames; renderingProgress.timeRemainingInMilliseconds = update.renderEstimatedTime; updateRenderProgress({ newline: false, printToConsole: true }); }, puppeteerInstance, onDownload, onCtrlCExit: addCleanupCallback, indent, server, cancelSignal: cancelSignal !== null && cancelSignal !== void 0 ? cancelSignal : undefined, audioCodec, preferLossless: false, imageFormat, disallowParallelEncoding, onBrowserLog: null, onStart: () => undefined, serializedResolvedPropsWithCustomSchema: no_react_1.NoReactInternals.serializeJSONWithSpecialTypes({ data: config.props, indent: undefined, staticBase: null, }).serializedString, offthreadVideoCacheSizeInBytes, offthreadVideoThreads, colorSpace, repro: repro !== null && repro !== void 0 ? repro : false, binariesDirectory, separateAudioTo: absoluteSeparateAudioTo, forSeamlessAacConcatenation, compositionStart: 0, onBrowserDownload, onArtifact, metadata: metadata !== null && metadata !== void 0 ? metadata : null, hardwareAcceleration, chromeMode, mediaCacheSizeInBytes, onLog, licenseKey: null, isProduction: null, }); if (!updatesDontOverwrite) { updateRenderProgress({ newline: true, printToConsole: true }); } if (absoluteSeparateAudioTo) { const relativeAudio = node_path_1.default.relative(process.cwd(), absoluteSeparateAudioTo); const audioSize = node_fs_1.default.statSync(absoluteSeparateAudioTo).size; log_1.Log.info({ indent, logLevel }, chalk_1.chalk.blue(`${(audioExists ? '○' : '+').padEnd(progress_bar_1.LABEL_WIDTH, ' ')} ${(0, make_link_1.makeHyperlink)({ url: `file://${absoluteSeparateAudioTo}`, text: relativeAudio, fallback: absoluteSeparateAudioTo })}`), chalk_1.chalk.gray(`${(0, studio_shared_1.formatBytes)(audioSize)}`)); } const { size } = node_fs_1.default.statSync(absoluteOutputFile); log_1.Log.info({ indent, logLevel }, chalk_1.chalk.blue(`${(exists ? '○' : '+').padEnd(progress_bar_1.LABEL_WIDTH)} ${(0, make_link_1.makeHyperlink)({ url: `file://${absoluteOutputFile}`, text: relativeOutputLocation, fallback: relativeOutputLocation })}`), chalk_1.chalk.gray(`${(0, studio_shared_1.formatBytes)(size)}`)); log_1.Log.verbose({ indent, logLevel }, `Slowest frames:`); slowestFrames.forEach(({ frame, time }) => { log_1.Log.verbose({ indent, logLevel }, ` Frame ${frame} (${time.toFixed(3)}ms)`); }); for (const line of renderer_1.RenderInternals.perf.getPerf()) { log_1.Log.verbose({ indent, logLevel }, line); } }; exports.renderVideoFlow = renderVideoFlow;