From 95229f3caa520b7daf572ce5eb59b16b83602972 Mon Sep 17 00:00:00 2001 From: Jonny Burger Date: Thu, 19 Feb 2026 16:02:27 +0100 Subject: [PATCH 01/21] Migrate CLI flags to proper RemotionOption definitions - Convert `out-dir`, `package-manager` to `AnyRemotionOption` with `getValue()`/`setConfig()`, register in options index, and wire up Config setters and docs (``). - In lambda render.ts, destructure `audioCodecOption`/`videoCodecOption` from `BrowserSafeApis.options` and use `.getValue()` instead of reading `parsedCli[...cliFlag]` directly. Co-authored-by: Cursor --- packages/cli/src/bundle.ts | 6 ++- packages/cli/src/config/index.ts | 7 +++ packages/cli/src/index.ts | 11 ++++- packages/cli/src/parse-command-line.ts | 6 ++- packages/docs/docs/cli/add.mdx | 4 +- packages/docs/docs/cli/bundle.mdx | 2 +- packages/docs/docs/cli/upgrade.mdx | 4 +- packages/docs/docs/config.mdx | 12 +++++ .../lambda/src/cli/commands/render/render.ts | 33 +++++++------ packages/renderer/src/options/index.tsx | 4 ++ packages/renderer/src/options/out-dir.tsx | 46 ++++++++++++++++++ .../renderer/src/options/package-manager.tsx | 48 +++++++++++++++++++ 12 files changed, 155 insertions(+), 28 deletions(-) create mode 100644 packages/renderer/src/options/out-dir.tsx create mode 100644 packages/renderer/src/options/package-manager.tsx diff --git a/packages/cli/src/bundle.ts b/packages/cli/src/bundle.ts index 71b99ea3a48..6c85fefca7d 100644 --- a/packages/cli/src/bundle.ts +++ b/packages/cli/src/bundle.ts @@ -21,6 +21,7 @@ const { askAIOption, experimentalClientSideRenderingOption, keyboardShortcutsOption, + outDirOption, } = BrowserSafeApis.options; export const bundleCommand = async ( @@ -90,8 +91,9 @@ export const bundleCommand = async ( commandLine: parsedCli, }).value; - const outputPath = parsedCli['out-dir'] - ? path.resolve(process.cwd(), parsedCli['out-dir']) + const outDir = outDirOption.getValue({commandLine: parsedCli}).value; + const outputPath = outDir + ? path.resolve(process.cwd(), outDir) : path.join(remotionRoot, 'build'); const gitignoreFolder = BundlerInternals.findClosestFolderWithItem( diff --git a/packages/cli/src/config/index.ts b/packages/cli/src/config/index.ts index 2256a33a51b..e0ab20d8d33 100644 --- a/packages/cli/src/config/index.ts +++ b/packages/cli/src/config/index.ts @@ -119,6 +119,7 @@ const { overrideWidthOption, overrideFpsOption, overrideDurationOption, + outDirOption, } = BrowserSafeApis.options; declare global { @@ -587,6 +588,11 @@ type FlatConfig = RemotionConfigObject & * Default: false */ setIPv4: (ipv4: boolean) => void; + /** + * Define the output directory for `npx remotion bundle`. + * Default: `build` in the Remotion root. + */ + setOutDir: (outDir: string | null) => void; /** * Choose between using Chrome Headless Shell or Chrome for Testing */ @@ -738,6 +744,7 @@ export const Config: FlatConfig = { setPublicLicenseKey: publicLicenseKeyOption.setConfig, setForceNewStudioEnabled: forceNewStudioOption.setConfig, setIPv4: ipv4Option.setConfig, + setOutDir: outDirOption.setConfig, }; export const ConfigInternals = { diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 3ed5e42cd84..7b019029043 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,4 +1,5 @@ import {RenderInternals} from '@remotion/renderer'; +import {BrowserSafeApis} from '@remotion/renderer/client'; import {StudioServerInternals} from '@remotion/studio-server'; import minimist from 'minimist'; import {addCommand} from './add'; @@ -49,9 +50,15 @@ import { versionsCommand, } from './versions'; +const {packageManagerOption} = BrowserSafeApis.options; + export const cli = async () => { const [command, ...args] = parsedCli._; + const packageManager = packageManagerOption.getValue({ + commandLine: parsedCli, + }).value; + const remotionRoot = RenderInternals.findRemotionRoot(); if (command !== VERSIONS_COMMAND && !parsedCli.help) { await validateVersionsBeforeCommand(remotionRoot, 'info'); @@ -111,7 +118,7 @@ export const cli = async () => { } else if (command === 'upgrade') { await upgradeCommand({ remotionRoot, - packageManager: parsedCli['package-manager'], + packageManager: packageManager ?? undefined, version: parsedCli.version, logLevel, args, @@ -130,7 +137,7 @@ export const cli = async () => { await addCommand({ remotionRoot, - packageManager: parsedCli['package-manager'], + packageManager: packageManager ?? undefined, packageNames, logLevel, args: additionalArgs, diff --git a/packages/cli/src/parse-command-line.ts b/packages/cli/src/parse-command-line.ts index a47faa45ce0..4cecca6fd31 100644 --- a/packages/cli/src/parse-command-line.ts +++ b/packages/cli/src/parse-command-line.ts @@ -46,6 +46,8 @@ const { overrideWidthOption, overrideFpsOption, overrideDurationOption, + outDirOption, + packageManagerOption, } = BrowserSafeApis.options; export type CommandLineOptions = { @@ -121,13 +123,13 @@ export type CommandLineOptions = { concurrencies: string; [enforceAudioOption.cliFlag]: TypeOfOption; gl: OpenGlRenderer; - ['package-manager']: string; + [packageManagerOption.cliFlag]: TypeOfOption; ['webpack-poll']: number; ['no-open']: boolean; ['browser']: string; ['browser-args']: string; [userAgentOption.cliFlag]: TypeOfOption; - ['out-dir']: string; + [outDirOption.cliFlag]: TypeOfOption; [audioLatencyHintOption.cliFlag]: AudioContextLatencyCategory; [ipv4Option.cliFlag]: TypeOfOption; [deleteAfterOption.cliFlag]: TypeOfOption; diff --git a/packages/docs/docs/cli/add.mdx b/packages/docs/docs/cli/add.mdx index dd9948b215a..e89a50f5710 100644 --- a/packages/docs/docs/cli/add.mdx +++ b/packages/docs/docs/cli/add.mdx @@ -38,9 +38,7 @@ This command will: ### `--package-manager` -Forces a specific package manager to be used. By default, Remotion will auto-detect the package manager based on your lockfile. - -Acceptable values are `npm`, `yarn` and `pnpm` + ## Package manager support diff --git a/packages/docs/docs/cli/bundle.mdx b/packages/docs/docs/cli/bundle.mdx index 98b5de3c550..9693cf2d54b 100644 --- a/packages/docs/docs/cli/bundle.mdx +++ b/packages/docs/docs/cli/bundle.mdx @@ -32,7 +32,7 @@ Specify a location for the Remotion config file. ### `--out-dir` -Define the location of the resulting bundle. By default it is a folder called `./build`, adjacent to the [Remotion Root](/docs/terminology/remotion-root). + ### `--public-path` diff --git a/packages/docs/docs/cli/upgrade.mdx b/packages/docs/docs/cli/upgrade.mdx index 4e821bdc503..6cf8a9d5f8d 100644 --- a/packages/docs/docs/cli/upgrade.mdx +++ b/packages/docs/docs/cli/upgrade.mdx @@ -17,9 +17,7 @@ npx remotion upgrade ### `--package-manager` -Forces a specific package manager to be used. This is useful if you are using Remotion in a monorepo and you want to upgrade all packages at once. By default, Remotion will auto-detect the package manager. - -Acceptable values are `npm`, `yarn` and `pnpm` + ### `--version` diff --git a/packages/docs/docs/config.mdx b/packages/docs/docs/config.mdx index 5782e446832..155c5f7c61e 100644 --- a/packages/docs/docs/config.mdx +++ b/packages/docs/docs/config.mdx @@ -87,6 +87,18 @@ Config.setPublicDir('./custom-public-dir'); The [command line flag](/docs/cli/render#--public-dir) `--public-dir` will take precedence over this option. +## `setOutDir()` + + + +```ts twoslash title="remotion.config.ts" +import {Config} from '@remotion/cli/config'; +// ---cut--- +Config.setOutDir('./custom-build-dir'); +``` + +The [command line flag](/docs/cli/bundle#--out-dir) `--out-dir` will take precedence over this option. + ## `setEntryPoint()` Sets the Remotion [entry point](/docs/terminology/entry-point), you don't have to specify it for CLI commands. diff --git a/packages/lambda/src/cli/commands/render/render.ts b/packages/lambda/src/cli/commands/render/render.ts index 2ee5686bd68..a714d952d6d 100644 --- a/packages/lambda/src/cli/commands/render/render.ts +++ b/packages/lambda/src/cli/commands/render/render.ts @@ -63,6 +63,8 @@ const { userAgentOption, disableWebSecurityOption, ignoreCertificateErrorsOption, + audioCodecOption, + videoCodecOption, } = BrowserSafeApis.options; export const renderCommand = async ({ @@ -206,6 +208,9 @@ export const renderCommand = async ({ const darkMode = darkModeOption.getValue({ commandLine: CliInternals.parsedCli, }).value; + const audioCodec = audioCodecOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value; const chromiumOptions: Required = { disableWebSecurity, @@ -289,19 +294,18 @@ export const renderCommand = async ({ const outName = parsedLambdaCli['out-name']; const downloadName = args[2] ?? null; - const {value: codec, source: reason} = - BrowserSafeApis.options.videoCodecOption.getValue( - { - commandLine: CliInternals.parsedCli, - }, - { - downloadName, - outName: outName ?? null, - configFile: ConfigInternals.getOutputCodecOrUndefined() ?? null, - uiCodec: null, - compositionCodec: null, - }, - ); + const {value: codec, source: reason} = videoCodecOption.getValue( + { + commandLine: CliInternals.parsedCli, + }, + { + downloadName, + outName: outName ?? null, + configFile: ConfigInternals.getOutputCodecOrUndefined() ?? null, + uiCodec: null, + compositionCodec: null, + }, + ); const imageFormat = CliInternals.getVideoImageFormat({ codec, @@ -366,8 +370,7 @@ export const renderCommand = async ({ : null, rendererFunctionName: parsedLambdaCli['renderer-function-name'] ?? null, forceBucketName: parsedLambdaCli['force-bucket-name'] ?? null, - audioCodec: - CliInternals.parsedCli[BrowserSafeApis.options.audioCodecOption.cliFlag], + audioCodec, deleteAfter: deleteAfter ?? null, colorSpace, downloadBehavior: {type: 'play-in-browser'}, diff --git a/packages/renderer/src/options/index.tsx b/packages/renderer/src/options/index.tsx index a669ff7611d..9495f5a6c1a 100644 --- a/packages/renderer/src/options/index.tsx +++ b/packages/renderer/src/options/index.tsx @@ -42,6 +42,8 @@ import {mutedOption} from './mute'; import {numberOfGifLoopsOption} from './number-of-gif-loops'; import {numberOfSharedAudioTagsOption} from './number-of-shared-audio-tags'; import {offthreadVideoCacheSizeInBytesOption} from './offthreadvideo-cache-size'; +import {outDirOption} from './out-dir'; +import {packageManagerOption} from './package-manager'; import {offthreadVideoThreadsOption} from './offthreadvideo-threads'; import {onBrowserDownloadOption} from './on-browser-download'; import type {AnyRemotionOption} from './option'; @@ -141,6 +143,8 @@ export const allOptions = { overrideWidthOption, overrideFpsOption, overrideDurationOption, + outDirOption, + packageManagerOption, }; export type AvailableOptions = keyof typeof allOptions; diff --git a/packages/renderer/src/options/out-dir.tsx b/packages/renderer/src/options/out-dir.tsx new file mode 100644 index 00000000000..b7bbd65c481 --- /dev/null +++ b/packages/renderer/src/options/out-dir.tsx @@ -0,0 +1,46 @@ +import type {AnyRemotionOption} from './option'; + +const cliFlag = 'out-dir' as const; + +let currentOutDir: string | null = null; + +export const outDirOption = { + name: 'Output Directory', + cliFlag, + description: () => { + return ( + <> + Define the location of the resulting bundle. By default it is a folder + called build, adjacent to the{' '} + Remotion Root. + + ); + }, + ssrName: 'outDir' as const, + docLink: 'https://www.remotion.dev/docs/cli/bundle#--out-dir', + getValue: ({commandLine}) => { + if (commandLine[cliFlag] !== undefined) { + return { + source: 'cli', + value: commandLine[cliFlag] as string, + }; + } + + if (currentOutDir !== null) { + return { + source: 'config', + value: currentOutDir, + }; + } + + return { + source: 'default', + value: null, + }; + }, + setConfig: (value: string | null) => { + currentOutDir = value; + }, + type: '' as string | null, + id: cliFlag, +} satisfies AnyRemotionOption; diff --git a/packages/renderer/src/options/package-manager.tsx b/packages/renderer/src/options/package-manager.tsx new file mode 100644 index 00000000000..c727d783c7c --- /dev/null +++ b/packages/renderer/src/options/package-manager.tsx @@ -0,0 +1,48 @@ +import type {AnyRemotionOption} from './option'; + +const cliFlag = 'package-manager' as const; + +let currentPackageManager: string | null = null; + +export const packageManagerOption = { + name: 'Package Manager', + cliFlag, + description: () => { + return ( + <> + Forces a specific package manager to be used. By default, Remotion will + auto-detect the package manager based on your lockfile. +
+ Acceptable values are npm, yarn,{' '} + pnpm and bun. + + ); + }, + ssrName: 'packageManager' as const, + docLink: 'https://www.remotion.dev/docs/cli/upgrade#--package-manager', + getValue: ({commandLine}) => { + if (commandLine[cliFlag] !== undefined) { + return { + source: 'cli', + value: commandLine[cliFlag] as string, + }; + } + + if (currentPackageManager !== null) { + return { + source: 'config', + value: currentPackageManager, + }; + } + + return { + source: 'default', + value: null, + }; + }, + setConfig: (value: string | null) => { + currentPackageManager = value; + }, + type: '' as string | null, + id: cliFlag, +} satisfies AnyRemotionOption; From 104f1fdda4481cb0062f6b93350e5d519da9fab1 Mon Sep 17 00:00:00 2001 From: Jonny Burger Date: Thu, 19 Feb 2026 16:15:36 +0100 Subject: [PATCH 02/21] Migrate webpack-poll, disable-keyboard-shortcuts, enable-experimental-client-side-rendering, image-sequence-pattern to RemotionOption pattern - Create webpackPollOption with getValue()/setConfig(), replacing the imperative setWebpackPollingInMilliseconds in parseCommandLine(). - Update parse-command-line.ts types to use option cliFlag references for all 4 flags. - Update parsed-cli.ts boolean flags for keyboard-shortcuts and experimental-client-side-rendering. - Update studio.ts to use webpackPollOption.getValue() instead of ConfigInternals.getWebpackPolling(). - Replace hardcoded descriptions in docs with . Co-authored-by: Cursor --- packages/cli/src/config/index.ts | 8 +-- packages/cli/src/parse-command-line.ts | 22 ++++--- packages/cli/src/parsed-cli.ts | 3 +- packages/cli/src/studio.ts | 3 +- packages/docs/docs/cli/render.mdx | 9 +-- packages/docs/docs/cli/studio.mdx | 7 +-- packages/docs/docs/config.mdx | 25 +++----- packages/renderer/src/options/index.tsx | 2 + .../renderer/src/options/webpack-poll.tsx | 57 +++++++++++++++++++ 9 files changed, 93 insertions(+), 43 deletions(-) create mode 100644 packages/renderer/src/options/webpack-poll.tsx diff --git a/packages/cli/src/config/index.ts b/packages/cli/src/config/index.ts index e0ab20d8d33..b32ef9bf303 100644 --- a/packages/cli/src/config/index.ts +++ b/packages/cli/src/config/index.ts @@ -52,10 +52,7 @@ import type {WebpackOverrideFn} from './override-webpack'; import {overrideWebpackConfig} from './override-webpack'; import {setPort, setRendererPort, setStudioPort} from './preview-server'; import {setWebpackCaching} from './webpack-caching'; -import { - getWebpackPolling, - setWebpackPollingInMilliseconds, -} from './webpack-poll'; +import {getWebpackPolling} from './webpack-poll'; export type {Concurrency, WebpackConfiguration, WebpackOverrideFn}; @@ -120,6 +117,7 @@ const { overrideFpsOption, overrideDurationOption, outDirOption, + webpackPollOption, } = BrowserSafeApis.options; declare global { @@ -659,7 +657,7 @@ export const Config: FlatConfig = { setExperimentalClientSideRenderingEnabled: experimentalClientSideRenderingOption.setConfig, setNumberOfSharedAudioTags: numberOfSharedAudioTagsOption.setConfig, - setWebpackPollingInMilliseconds, + setWebpackPollingInMilliseconds: webpackPollOption.setConfig, setShouldOpenBrowser, setBufferStateDelayInMilliseconds, overrideWebpackConfig, diff --git a/packages/cli/src/parse-command-line.ts b/packages/cli/src/parse-command-line.ts index 4cecca6fd31..db40bf2de3d 100644 --- a/packages/cli/src/parse-command-line.ts +++ b/packages/cli/src/parse-command-line.ts @@ -48,6 +48,10 @@ const { overrideDurationOption, outDirOption, packageManagerOption, + webpackPollOption, + keyboardShortcutsOption, + experimentalClientSideRenderingOption, + imageSequencePatternOption, } = BrowserSafeApis.options; export type CommandLineOptions = { @@ -112,8 +116,12 @@ export type CommandLineOptions = { port: number; frame: string | number; ['disable-headless']: boolean; - ['disable-keyboard-shortcuts']: boolean; - ['enable-experimental-client-side-rendering']: boolean; + [keyboardShortcutsOption.cliFlag]: TypeOfOption< + typeof keyboardShortcutsOption + >; + [experimentalClientSideRenderingOption.cliFlag]: TypeOfOption< + typeof experimentalClientSideRenderingOption + >; muted: boolean; [overrideHeightOption.cliFlag]: TypeOfOption; [overrideWidthOption.cliFlag]: TypeOfOption; @@ -124,7 +132,7 @@ export type CommandLineOptions = { [enforceAudioOption.cliFlag]: TypeOfOption; gl: OpenGlRenderer; [packageManagerOption.cliFlag]: TypeOfOption; - ['webpack-poll']: number; + [webpackPollOption.cliFlag]: TypeOfOption; ['no-open']: boolean; ['browser']: string; ['browser-args']: string; @@ -138,7 +146,9 @@ export type CommandLineOptions = { typeof enableMultiprocessOnLinuxOption >; repro: boolean; - 'image-sequence-pattern': string; + [imageSequencePatternOption.cliFlag]: TypeOfOption< + typeof imageSequencePatternOption + >; 'license-key': string; [publicLicenseKeyOption.cliFlag]: string; [forceNewStudioOption.cliFlag]: TypeOfOption; @@ -173,8 +183,4 @@ export const parseCommandLine = () => { ) { Config.setPublicLicenseKey(parsedCli['license-key']); } - - if (typeof parsedCli['webpack-poll'] !== 'undefined') { - Config.setWebpackPollingInMilliseconds(parsedCli['webpack-poll']); - } }; diff --git a/packages/cli/src/parsed-cli.ts b/packages/cli/src/parsed-cli.ts index 76a1675fcff..378ea316d20 100644 --- a/packages/cli/src/parsed-cli.ts +++ b/packages/cli/src/parsed-cli.ts @@ -23,7 +23,8 @@ export const BooleanFlags = [ BrowserSafeApis.options.darkModeOption.cliFlag, BrowserSafeApis.options.ignoreCertificateErrorsOption.cliFlag, 'disable-headless', - 'disable-keyboard-shortcuts', + BrowserSafeApis.options.keyboardShortcutsOption.cliFlag, + BrowserSafeApis.options.experimentalClientSideRenderingOption.cliFlag, 'default-only', 'no-open', BrowserSafeApis.options.ipv4Option.cliFlag, diff --git a/packages/cli/src/studio.ts b/packages/cli/src/studio.ts index 1bf1f5aa377..e7531549cd4 100644 --- a/packages/cli/src/studio.ts +++ b/packages/cli/src/studio.ts @@ -42,6 +42,7 @@ const { numberOfSharedAudioTagsOption, audioLatencyHintOption, ipv4Option, + webpackPollOption, } = BrowserSafeApis.options; export const studioCommand = async ( @@ -158,7 +159,7 @@ export const studioCommand = async ( remotionRoot, relativePublicDir, webpackOverride: ConfigInternals.getWebpackOverrideFn(), - poll: ConfigInternals.getWebpackPolling(), + poll: webpackPollOption.getValue({commandLine: parsedCli}).value, getRenderDefaults, getRenderQueue, numberOfAudioTags: numberOfSharedAudioTagsOption.getValue({ diff --git a/packages/docs/docs/cli/render.mdx b/packages/docs/docs/cli/render.mdx index c54eb2d826c..3bf3a77db41 100644 --- a/packages/docs/docs/cli/render.mdx +++ b/packages/docs/docs/cli/render.mdx @@ -61,14 +61,7 @@ Inline JSON string isn't supported on Windows shells because it removes the `"` ### `--image-sequence-pattern` -[Pattern for naming image sequence files. Supports `[frame]` for the zero-padded frame number and `[ext]` for the file extension.] - -**Example:** - -```bash -npx remotion render ... --sequence --image-sequence-pattern='frame_[frame]_custom.[ext]' -# Produces: frame_0001_custom.jpeg, frame_0002_custom.jpeg, ... -``` + ### `--config` diff --git a/packages/docs/docs/cli/studio.mdx b/packages/docs/docs/cli/studio.mdx index 81907c98eba..ff45d58038e 100644 --- a/packages/docs/docs/cli/studio.mdx +++ b/packages/docs/docs/cli/studio.mdx @@ -52,16 +52,15 @@ Specify a location for a dotenv file - Default `.env`. [Read about how environme ### `--disable-keyboard-shortcuts` -[Disables all keyboard shortcuts in the Studio](/docs/config#setkeyboardshortcutsenabled). + ### `--enable-experimental-client-side-rendering` -[Enables experimental client-side rendering in the Studio](/docs/config#setexperimentalclientsiderenderingenabled). Default is `false`. + ### `--webpack-poll` -[Enables Webpack polling](/docs/config#setwebpackpollinginmilliseconds) instead of the file system event listeners for hot reloading. This is useful if you are inside a virtual machine or have a remote file system. -Pass a value in milliseconds, for example `--webpack-poll=1000`. + ### `--no-open` diff --git a/packages/docs/docs/config.mdx b/packages/docs/docs/config.mdx index 155c5f7c61e..455d7121995 100644 --- a/packages/docs/docs/config.mdx +++ b/packages/docs/docs/config.mdx @@ -141,7 +141,7 @@ Config.setMaxTimelineTracks(20); ## `setKeyboardShortcutsEnabled()` -Whether the Studio should react to keyboard shortcuts. Default `true`. + ```ts twoslash title="remotion.config.ts" import {Config} from '@remotion/cli/config'; @@ -153,7 +153,7 @@ The [command line flag](/docs/cli/studio#--disable-keyboard-shortcuts) `--disabl ## `setExperimentalClientSideRenderingEnabled()` -Enable experimental [client-side rendering](/docs/client-side-rendering) in the Remotion Studio. Default `false`. + ```ts twoslash title="remotion.config.ts" import {Config} from '@remotion/cli/config'; @@ -165,8 +165,7 @@ The [command line flag](/docs/cli/studio#--enable-experimental-client-side-rende ## `setWebpackPollingInMilliseconds()` -Enables Webpack polling instead of the file system event listeners for hot reloading. -This is useful if you are inside a virtual machine or have a remote file system. + ```ts twoslash title="remotion.config.ts" import {Config} from '@remotion/cli/config'; @@ -1105,20 +1104,14 @@ The old way is deprecated, but will work for the foreseeable future. - [Encoding guide](/docs/encoding) -### setImageSequencePattern(pattern) +### `setImageSequencePattern()` -Set the pattern for naming image sequence files when rendering an image sequence. - -- `[frame]` will be replaced with the zero-padded frame number (e.g. 0001, 0002, ...) -- `[ext]` will be replaced with the image format extension (e.g. jpeg, png) - -**Example:** - -```js -// remotion.config.ts -import {Config} from 'remotion'; + +```ts twoslash title="remotion.config.ts" +import {Config} from '@remotion/cli/config'; +// ---cut--- Config.setImageSequencePattern('frame_[frame]_custom.[ext]'); ``` -This will produce files like `frame_0001_custom.jpeg`, `frame_0002_custom.jpeg`, ... +The [command line flag](/docs/cli/render#--image-sequence-pattern) `--image-sequence-pattern` will take precedence over this option. diff --git a/packages/renderer/src/options/index.tsx b/packages/renderer/src/options/index.tsx index 9495f5a6c1a..716bcb7faa1 100644 --- a/packages/renderer/src/options/index.tsx +++ b/packages/renderer/src/options/index.tsx @@ -70,6 +70,7 @@ import {mediaCacheSizeInBytesOption} from './video-cache-size'; import {videoCodecOption} from './video-codec'; import {videoImageFormatOption} from './video-image-format'; import {webhookCustomDataOption} from './webhook-custom-data'; +import {webpackPollOption} from './webpack-poll'; import {x264Option} from './x264-preset'; export const allOptions = { @@ -145,6 +146,7 @@ export const allOptions = { overrideDurationOption, outDirOption, packageManagerOption, + webpackPollOption, }; export type AvailableOptions = keyof typeof allOptions; diff --git a/packages/renderer/src/options/webpack-poll.tsx b/packages/renderer/src/options/webpack-poll.tsx new file mode 100644 index 00000000000..ceb35bcbe64 --- /dev/null +++ b/packages/renderer/src/options/webpack-poll.tsx @@ -0,0 +1,57 @@ +import type {AnyRemotionOption} from './option'; + +const cliFlag = 'webpack-poll' as const; + +let webpackPolling: number | null = null; + +export const webpackPollOption = { + name: 'Webpack Polling', + cliFlag, + description: () => ( + <> + Enables Webpack polling instead of the file system event listeners for hot + reloading. This is useful if you are inside a virtual machine or have a + remote file system. Pass a value in milliseconds. + + ), + ssrName: null, + docLink: 'https://www.remotion.dev/docs/config#setwebpackpollinginmilliseconds', + getValue: ({commandLine}) => { + if (commandLine[cliFlag] !== undefined) { + const val = commandLine[cliFlag]; + if (typeof val !== 'number') { + throw new TypeError( + `Webpack polling must be a number, got ${JSON.stringify(val)}`, + ); + } + + return { + source: 'cli', + value: val, + }; + } + + if (webpackPolling !== null) { + return { + source: 'config', + value: webpackPolling, + }; + } + + return { + source: 'default', + value: null, + }; + }, + setConfig: (value: number | null) => { + if (typeof value !== 'number' && value !== null) { + throw new TypeError( + `Polling must be a number or null, got ${JSON.stringify(value)} instead.`, + ); + } + + webpackPolling = value; + }, + type: 0 as number | null, + id: cliFlag, +} satisfies AnyRemotionOption; From 17748e09ff4fa363eb0ade9b69d90681511ff4ad Mon Sep 17 00:00:00 2001 From: Jonny Burger Date: Thu, 19 Feb 2026 16:32:21 +0100 Subject: [PATCH 03/21] Migrate frame, sequence, scale, overwrite, crf, log, version, codec, bundle-cache, env-file to RemotionOption pattern New option definitions for: stillFrameOption, imageSequenceOption, versionFlagOption, bundleCacheOption, envFileOption. Existing options (scaleOption, overwriteOption, crfOption, logLevelOption, videoCodecOption) get their CommandLineOptions types updated to use the option's cliFlag. - Remove imperative bundle-cache/frame/sequence handling from parseCommandLine(); consumers now use getValue() directly. - Update get-cli-options.ts, get-env.ts, setup-cache.ts, and lambda/cloudrun sites/create.ts to read via option getValue(). - Replace hardcoded descriptions in docs with . Co-authored-by: Cursor --- packages/cli/src/config/index.ts | 12 ++--- packages/cli/src/get-cli-options.ts | 9 +++- packages/cli/src/get-env.ts | 13 ++++-- packages/cli/src/index.ts | 4 +- packages/cli/src/parse-command-line.ts | 43 +++++++++--------- packages/cli/src/parsed-cli.ts | 4 +- packages/cli/src/setup-cache.ts | 5 ++- .../cloudrun/src/cli/commands/sites/create.ts | 4 +- packages/docs/docs/cli/compositions.mdx | 4 +- packages/docs/docs/cli/render.mdx | 6 +-- packages/docs/docs/cli/still.mdx | 7 ++- packages/docs/docs/cli/studio.mdx | 2 +- packages/docs/docs/cli/upgrade.mdx | 2 +- packages/docs/docs/cloudrun/cli/render.mdx | 2 +- packages/docs/docs/cloudrun/cli/still.mdx | 2 +- packages/docs/docs/config.mdx | 6 +-- packages/docs/docs/lambda/cli.mdx | 2 +- .../docs/docs/lambda/cli/compositions.mdx | 2 +- packages/docs/docs/lambda/cli/render.mdx | 2 +- packages/docs/docs/lambda/cli/still.mdx | 2 +- .../lambda/src/cli/commands/sites/create.ts | 4 +- .../renderer/src/options/bundle-cache.tsx | 36 +++++++++++++++ packages/renderer/src/options/env-file.tsx | 42 ++++++++++++++++++ .../renderer/src/options/image-sequence.tsx | 40 +++++++++++++++++ packages/renderer/src/options/index.tsx | 10 +++++ packages/renderer/src/options/still-frame.tsx | 44 +++++++++++++++++++ .../renderer/src/options/version-flag.tsx | 31 +++++++++++++ 27 files changed, 278 insertions(+), 62 deletions(-) create mode 100644 packages/renderer/src/options/bundle-cache.tsx create mode 100644 packages/renderer/src/options/env-file.tsx create mode 100644 packages/renderer/src/options/image-sequence.tsx create mode 100644 packages/renderer/src/options/still-frame.tsx create mode 100644 packages/renderer/src/options/version-flag.tsx diff --git a/packages/cli/src/config/index.ts b/packages/cli/src/config/index.ts index b32ef9bf303..94f52a1b26c 100644 --- a/packages/cli/src/config/index.ts +++ b/packages/cli/src/config/index.ts @@ -38,20 +38,17 @@ import { } from './buffer-state-delay-in-milliseconds'; import type {Concurrency} from './concurrency'; import {getEntryPoint, setEntryPoint} from './entry-point'; -import {setDotEnvLocation} from './env-file'; import { getFfmpegOverrideFunction, setFfmpegOverrideFunction, } from './ffmpeg-override'; import {setFrameRange} from './frame-range'; -import {setImageSequence} from './image-sequence'; import {getMetadata, setMetadata} from './metadata'; import {getShouldOpenBrowser, setShouldOpenBrowser} from './open-browser'; import {setOutputLocation} from './output-location'; import type {WebpackOverrideFn} from './override-webpack'; import {overrideWebpackConfig} from './override-webpack'; import {setPort, setRendererPort, setStudioPort} from './preview-server'; -import {setWebpackCaching} from './webpack-caching'; import {getWebpackPolling} from './webpack-poll'; export type {Concurrency, WebpackConfiguration, WebpackOverrideFn}; @@ -118,6 +115,9 @@ const { overrideDurationOption, outDirOption, webpackPollOption, + imageSequenceOption, + bundleCacheOption, + envFileOption, } = BrowserSafeApis.options; declare global { @@ -661,7 +661,7 @@ export const Config: FlatConfig = { setShouldOpenBrowser, setBufferStateDelayInMilliseconds, overrideWebpackConfig, - setCachingEnabled: setWebpackCaching, + setCachingEnabled: bundleCacheOption.setConfig, setPort, setStudioPort, setRendererPort, @@ -677,7 +677,7 @@ export const Config: FlatConfig = { setChromiumHeadlessMode: headlessOption.setConfig, setChromiumOpenGlRenderer: glOption.setConfig, setChromiumUserAgent: userAgentOption.setConfig, - setDotEnvLocation, + setDotEnvLocation: envFileOption.setConfig, setConcurrency: concurrencyOption.setConfig, setChromiumMultiProcessOnLinux: enableMultiprocessOnLinuxOption.setConfig, setChromiumDarkMode: darkModeOption.setConfig, @@ -709,7 +709,7 @@ export const Config: FlatConfig = { setPixelFormat: pixelFormatOption.setConfig, setCodec: videoCodecOption.setConfig, setCrf: crfOption.setConfig, - setImageSequence, + setImageSequence: imageSequenceOption.setConfig, setProResProfile: proResProfileOption.setConfig, setX264Preset: x264Option.setConfig, setAudioBitrate: audioBitrateOption.setConfig, diff --git a/packages/cli/src/get-cli-options.ts b/packages/cli/src/get-cli-options.ts index 4109df43438..1802190d201 100644 --- a/packages/cli/src/get-cli-options.ts +++ b/packages/cli/src/get-cli-options.ts @@ -56,9 +56,12 @@ export const getCliOptions = (options: { }) => { const frameRange = getAndValidateFrameRange(options.logLevel, false); + const imageSequence = BrowserSafeApis.options.imageSequenceOption.getValue({ + commandLine: parsedCli, + }).value; const shouldOutputImageSequence = options.isStill ? true - : ConfigInternals.getShouldOutputImageSequence(frameRange); + : imageSequence || typeof frameRange === 'number'; const concurrency = BrowserSafeApis.options.concurrencyOption.getValue({ commandLine: parsedCli, @@ -94,7 +97,9 @@ export const getCliOptions = (options: { options.logLevel, options.indent, ), - stillFrame: ConfigInternals.getStillFrame(), + stillFrame: BrowserSafeApis.options.stillFrameOption.getValue({ + commandLine: parsedCli, + }).value ?? 0, ffmpegOverride: ConfigInternals.getFfmpegOverrideFunction(), height, width, diff --git a/packages/cli/src/get-env.ts b/packages/cli/src/get-env.ts index 23474036bb8..a8bd8af6c79 100644 --- a/packages/cli/src/get-env.ts +++ b/packages/cli/src/get-env.ts @@ -1,15 +1,17 @@ import type {LogLevel} from '@remotion/renderer'; import {RenderInternals} from '@remotion/renderer'; +import {BrowserSafeApis} from '@remotion/renderer/client'; import {StudioServerInternals} from '@remotion/studio-server'; import dotenv from 'dotenv'; import fs, {readFileSync} from 'node:fs'; import path from 'node:path'; import {chalk} from './chalk'; -import {ConfigInternals} from './config'; import {makeHyperlink} from './hyperlinks/make-link'; import {Log} from './log'; import {parsedCli} from './parsed-cli'; +const {envFileOption} = BrowserSafeApis.options; + function getProcessEnv(): Record { const env: Record = {}; @@ -141,8 +143,11 @@ export const getEnvironmentVariables = ( ): Record => { const processEnv = getProcessEnv(); - if (parsedCli['env-file']) { - const envFile = path.resolve(process.cwd(), parsedCli['env-file']); + const {value: envFileValue, source: envFileSource} = envFileOption.getValue({ + commandLine: parsedCli, + }); + if (envFileValue && envFileSource === 'cli') { + const envFile = path.resolve(process.cwd(), envFileValue); if (!fs.existsSync(envFile)) { Log.error( {indent: false, logLevel}, @@ -165,7 +170,7 @@ export const getEnvironmentVariables = ( const remotionRoot = RenderInternals.findRemotionRoot(); - const configFileSetting = ConfigInternals.getDotEnvLocation(); + const configFileSetting = envFileValue && envFileSource === 'config' ? envFileValue : null; if (configFileSetting) { const envFile = path.resolve(remotionRoot, configFileSetting); if (!fs.existsSync(envFile)) { diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 7b019029043..dd110d3ef18 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -50,7 +50,7 @@ import { versionsCommand, } from './versions'; -const {packageManagerOption} = BrowserSafeApis.options; +const {packageManagerOption, versionFlagOption} = BrowserSafeApis.options; export const cli = async () => { const [command, ...args] = parsedCli._; @@ -119,7 +119,7 @@ export const cli = async () => { await upgradeCommand({ remotionRoot, packageManager: packageManager ?? undefined, - version: parsedCli.version, + version: versionFlagOption.getValue({commandLine: parsedCli}).value ?? undefined, logLevel, args, }); diff --git a/packages/cli/src/parse-command-line.ts b/packages/cli/src/parse-command-line.ts index db40bf2de3d..5e62cdd0702 100644 --- a/packages/cli/src/parse-command-line.ts +++ b/packages/cli/src/parse-command-line.ts @@ -1,6 +1,5 @@ import type { AudioCodec, - Codec, OpenGlRenderer, StillImageFormat, VideoImageFormat, @@ -52,6 +51,16 @@ const { keyboardShortcutsOption, experimentalClientSideRenderingOption, imageSequencePatternOption, + scaleOption, + overwriteOption, + crfOption, + logLevelOption, + videoCodecOption, + stillFrameOption, + imageSequenceOption, + versionFlagOption, + bundleCacheOption, + envFileOption, } = BrowserSafeApis.options; export type CommandLineOptions = { @@ -62,8 +71,8 @@ export type CommandLineOptions = { ['image-format']: VideoImageFormat | StillImageFormat; [proResProfileOption.cliFlag]: TypeOfOption; [x264Option.cliFlag]: TypeOfOption; - ['bundle-cache']: string; - ['env-file']: string; + [bundleCacheOption.cliFlag]: TypeOfOption; + [envFileOption.cliFlag]: TypeOfOption; [ignoreCertificateErrorsOption.cliFlag]: TypeOfOption< typeof ignoreCertificateErrorsOption >; @@ -84,8 +93,8 @@ export type CommandLineOptions = { typeof disallowParallelEncodingOption >; [beepOnFinishOption.cliFlag]: TypeOfOption; - version: string; - codec: Codec; + [versionFlagOption.cliFlag]: TypeOfOption; + [videoCodecOption.cliFlag]: TypeOfOption; [concurrencyOption.cliFlag]: TypeOfOption; timeout: number; config: string; @@ -98,23 +107,23 @@ export type CommandLineOptions = { [encodingMaxRateOption.cliFlag]: TypeOfOption; [audioCodecOption.cliFlag]: AudioCodec; [publicPathOption.cliFlag]: string; - crf: number; + [crfOption.cliFlag]: TypeOfOption; force: boolean; output: string | undefined; - overwrite: boolean; + [overwriteOption.cliFlag]: TypeOfOption; png: boolean; props: string; quality: number; [jpegQualityOption.cliFlag]: TypeOfOption; frames: string | number; - scale: number; - sequence: boolean; + [scaleOption.cliFlag]: TypeOfOption; + [imageSequenceOption.cliFlag]: TypeOfOption; quiet: boolean; q: boolean; - log: string; + [logLevelOption.cliFlag]: TypeOfOption; help: boolean; port: number; - frame: string | number; + [stillFrameOption.cliFlag]: TypeOfOption; ['disable-headless']: boolean; [keyboardShortcutsOption.cliFlag]: TypeOfOption< typeof keyboardShortcutsOption @@ -155,28 +164,16 @@ export type CommandLineOptions = { }; export const parseCommandLine = () => { - if (typeof parsedCli['bundle-cache'] !== 'undefined') { - Config.setCachingEnabled(parsedCli['bundle-cache'] !== 'false'); - } - if (parsedCli.frames) { ConfigInternals.setFrameRangeFromCli(parsedCli.frames); } - if (parsedCli.frame) { - ConfigInternals.setStillFrame(Number(parsedCli.frame)); - } - if (parsedCli.png) { throw new Error( 'The --png flag has been removed. Use --sequence --image-format=png from now on.', ); } - if (parsedCli.sequence) { - Config.setImageSequence(true); - } - if ( parsedCli['license-key'] && parsedCli['license-key'].startsWith('rm_pub_') diff --git a/packages/cli/src/parsed-cli.ts b/packages/cli/src/parsed-cli.ts index 378ea316d20..7643b867381 100644 --- a/packages/cli/src/parsed-cli.ts +++ b/packages/cli/src/parsed-cli.ts @@ -3,9 +3,9 @@ import minimist from 'minimist'; import type {CommandLineOptions} from './parse-command-line'; export const BooleanFlags = [ - 'overwrite', + BrowserSafeApis.options.overwriteOption.cliFlag, 'force', - 'sequence', + BrowserSafeApis.options.imageSequenceOption.cliFlag, 'help', 'quiet', 'q', diff --git a/packages/cli/src/setup-cache.ts b/packages/cli/src/setup-cache.ts index 781319f92d3..7701c8383fe 100644 --- a/packages/cli/src/setup-cache.ts +++ b/packages/cli/src/setup-cache.ts @@ -2,6 +2,7 @@ import type {MandatoryLegacyBundleOptions} from '@remotion/bundler'; import {BundlerInternals} from '@remotion/bundler'; import type {LogLevel} from '@remotion/renderer'; import {RenderInternals} from '@remotion/renderer'; +import {BrowserSafeApis} from '@remotion/renderer/client'; import type {BundlingState, CopyingState} from '@remotion/studio-server'; import type {GitSource} from '@remotion/studio-shared'; import {existsSync} from 'fs'; @@ -150,7 +151,9 @@ export const bundleOnCli = async ({ keyboardShortcutsEnabled: boolean; askAIEnabled: boolean; }) => { - const shouldCache = ConfigInternals.getWebpackCaching(); + const shouldCache = BrowserSafeApis.options.bundleCacheOption.getValue({ + commandLine: {}, + }).value; const symlinkState: SymbolicLinksState = { symlinks: [], diff --git a/packages/cloudrun/src/cli/commands/sites/create.ts b/packages/cloudrun/src/cli/commands/sites/create.ts index b47f751407d..fbdfa6ca191 100644 --- a/packages/cloudrun/src/cli/commands/sites/create.ts +++ b/packages/cloudrun/src/cli/commands/sites/create.ts @@ -189,7 +189,9 @@ export const sitesCreateSubcommand = async ( }; updateProgress(false); }, - enableCaching: ConfigInternals.getWebpackCaching(), + enableCaching: BrowserSafeApis.options.bundleCacheOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value, webpackOverride: ConfigInternals.getWebpackOverrideFn() ?? ((f) => f), gitSource, bypassBucketNameValidation: true, diff --git a/packages/docs/docs/cli/compositions.mdx b/packages/docs/docs/cli/compositions.mdx index 44d97d94e12..fe2a9d6d5d9 100644 --- a/packages/docs/docs/cli/compositions.mdx +++ b/packages/docs/docs/cli/compositions.mdx @@ -34,11 +34,11 @@ Specify a location for the Remotion config file. ### `--env-file` -Specify a location for a dotenv file - Default `.env`. [Read about how environment variables work in Remotion.](/docs/env-variables) + ### `--bundle-cache` -[Enable or disable Webpack caching](/docs/config#setcachingenabled). This flag is enabled by default, use `--bundle-cache=false` to disable caching. + ### `--log` diff --git a/packages/docs/docs/cli/render.mdx b/packages/docs/docs/cli/render.mdx index 3bf3a77db41..03ce7785b00 100644 --- a/packages/docs/docs/cli/render.mdx +++ b/packages/docs/docs/cli/render.mdx @@ -69,7 +69,7 @@ Specify a location for the Remotion config file. ### `--env-file` -Specify a location for a dotenv file. Default `.env`. + ### `--jpeg-quality` @@ -89,7 +89,7 @@ Sets the output file path, as an alternative to the `output-location` positional ### `--sequence` -[Pass this flag if you want an image sequence as the output instead of a video.](/docs/config#setimagesequence) + ### `--codec` @@ -179,7 +179,7 @@ Disallows the renderer from doing rendering frames and encoding at the same time ### `--bundle-cache` -[Enable or disable Webpack caching](/docs/config#setcachingenabled). This flag is enabled by default, use `--bundle-cache=false` to disable caching. + ### `--log` diff --git a/packages/docs/docs/cli/still.mdx b/packages/docs/docs/cli/still.mdx index 7d8504149d3..b745899b208 100644 --- a/packages/docs/docs/cli/still.mdx +++ b/packages/docs/docs/cli/still.mdx @@ -41,7 +41,7 @@ Specify a location for the Remotion config file. ### `--env-file` -Specify a location for a dotenv file - Default `.env`. [Read about how environment variables work in Remotion.](/docs/env-variables) + ### `--jpeg-quality` @@ -69,12 +69,11 @@ Sets the output file path, as an alternative to the `output-location` positional ### `--frame` -Which frame should be rendered. Example `--frame=10`. Default `0`. -From v3.2.27, negative values are allowed, with `-1` being the last frame. + ### `--bundle-cache` -[Enable or disable Webpack caching](/docs/config#setcachingenabled). This flag is enabled by default, use `--bundle-cache=false` to disable caching. + ### `--log` diff --git a/packages/docs/docs/cli/studio.mdx b/packages/docs/docs/cli/studio.mdx index ff45d58038e..a6facfd0fc6 100644 --- a/packages/docs/docs/cli/studio.mdx +++ b/packages/docs/docs/cli/studio.mdx @@ -36,7 +36,7 @@ Specify a location for the Remotion config file. ### `--env-file` -Specify a location for a dotenv file - Default `.env`. [Read about how environment variables work in Remotion.](/docs/env-variables) + ### `--log` diff --git a/packages/docs/docs/cli/upgrade.mdx b/packages/docs/docs/cli/upgrade.mdx index 6cf8a9d5f8d..71f30551d6a 100644 --- a/packages/docs/docs/cli/upgrade.mdx +++ b/packages/docs/docs/cli/upgrade.mdx @@ -21,7 +21,7 @@ npx remotion upgrade ### `--version` -Install a specific version. Also enables downgrading to an older version. + ## Package manager support diff --git a/packages/docs/docs/cloudrun/cli/render.mdx b/packages/docs/docs/cloudrun/cli/render.mdx index 9265804c6a8..b769e5d6319 100644 --- a/packages/docs/docs/cloudrun/cli/render.mdx +++ b/packages/docs/docs/cloudrun/cli/render.mdx @@ -95,7 +95,7 @@ Before v4.0.76, this was "100%" by default. It is now aligned to the other serve ### `--env-file` -Specify a location for a dotenv file - Default `.env`. [Read about how environment variables work in Remotion.](/docs/env-variables) + ### `--out-name` diff --git a/packages/docs/docs/cloudrun/cli/still.mdx b/packages/docs/docs/cloudrun/cli/still.mdx index d7e14c66c99..e394caf6491 100644 --- a/packages/docs/docs/cloudrun/cli/still.mdx +++ b/packages/docs/docs/cloudrun/cli/still.mdx @@ -87,7 +87,7 @@ Specify a specific bucket name to be used for the output. The resulting Google C ### `--env-file` -Specify a location for a dotenv file - Default `.env`. [Read about how environment variables work in Remotion.](/docs/env-variables) + ### `--out-name` diff --git a/packages/docs/docs/config.mdx b/packages/docs/docs/config.mdx index 455d7121995..43a6eb1c8d9 100644 --- a/packages/docs/docs/config.mdx +++ b/packages/docs/docs/config.mdx @@ -41,7 +41,7 @@ Config.overrideWebpackConfig((currentConfiguration) => { ## `setCachingEnabled()` -Enable or disable Webpack caching. Default is `true` which will make the Webpack step in the first run a bit slower but will massively speed up subsequent runs. + ```ts twoslash title="remotion.config.ts" import {Config} from '@remotion/cli/config'; @@ -465,7 +465,7 @@ The [command line flag](/docs/cli/render#--jpeg-quality) `--jpeg-quality` will t ## `setDotEnvLocation()` -Set a custom location for a [`.env`](https://www.npmjs.com/package/dotenv) file. You can specify an absolute path or a relative path in which case it gets resolved based on the current working directory. + ```ts twoslash title="remotion.config.ts" import {Config} from '@remotion/cli/config'; @@ -609,7 +609,7 @@ The [command line flag](/docs/cli/render#--x264-preset) `--x264-preset` will tak ## `setImageSequence()` -Set to true if you want to output an image sequence instead of a video. + ```ts twoslash title="remotion.config.ts" import {Config} from '@remotion/cli/config'; diff --git a/packages/docs/docs/lambda/cli.mdx b/packages/docs/docs/lambda/cli.mdx index fd17b44262a..4a7e5f08a3f 100644 --- a/packages/docs/docs/lambda/cli.mdx +++ b/packages/docs/docs/lambda/cli.mdx @@ -43,4 +43,4 @@ Prints the minimal amount of logs. ### `--env-file` -Specify a location for a dotenv file - Default `.env`. [Read about how environment variables work in Remotion.](/docs/env-variables) + diff --git a/packages/docs/docs/lambda/cli/compositions.mdx b/packages/docs/docs/lambda/cli/compositions.mdx index 78e52866a42..0faefd61943 100644 --- a/packages/docs/docs/lambda/cli/compositions.mdx +++ b/packages/docs/docs/lambda/cli/compositions.mdx @@ -81,7 +81,7 @@ Specify a location for the Remotion config file. ### `--env-file` -Specify a location for a dotenv file - Default `.env`. [Read about how environment variables work in Remotion.](/docs/env-variables) + ### `--log` diff --git a/packages/docs/docs/lambda/cli/render.mdx b/packages/docs/docs/lambda/cli/render.mdx index 5f625774841..987ef428455 100644 --- a/packages/docs/docs/lambda/cli/render.mdx +++ b/packages/docs/docs/lambda/cli/render.mdx @@ -186,7 +186,7 @@ Renamed to `jpegQuality` in `v4.0.0`. ### `--env-file` -Specify a location for a dotenv file - Default `.env`. [Read about how environment variables work in Remotion.](/docs/env-variables) + ### `--frames` diff --git a/packages/docs/docs/lambda/cli/still.mdx b/packages/docs/docs/lambda/cli/still.mdx index 533e0f1c657..67bfe43a050 100644 --- a/packages/docs/docs/lambda/cli/still.mdx +++ b/packages/docs/docs/lambda/cli/still.mdx @@ -53,7 +53,7 @@ npx remotion lambda still testbed my-comp out.png ### `--frame` -Render a specific frame of a composition. Default `0` + ### `--region` diff --git a/packages/lambda/src/cli/commands/sites/create.ts b/packages/lambda/src/cli/commands/sites/create.ts index 3f514157240..fb1ab935307 100644 --- a/packages/lambda/src/cli/commands/sites/create.ts +++ b/packages/lambda/src/cli/commands/sites/create.ts @@ -221,7 +221,9 @@ export const sitesCreateSubcommand = async ( }; updateProgress(false); }, - enableCaching: ConfigInternals.getWebpackCaching(), + enableCaching: BrowserSafeApis.options.bundleCacheOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value, webpackOverride: ConfigInternals.getWebpackOverrideFn() ?? ((f) => f), bypassBucketNameValidation: Boolean(parsedLambdaCli['force-bucket-name']), askAIEnabled, diff --git a/packages/renderer/src/options/bundle-cache.tsx b/packages/renderer/src/options/bundle-cache.tsx new file mode 100644 index 00000000000..575df3cbf60 --- /dev/null +++ b/packages/renderer/src/options/bundle-cache.tsx @@ -0,0 +1,36 @@ +import type {AnyRemotionOption} from './option'; + +const cliFlag = 'bundle-cache' as const; + +let cachingEnabled = true; + +export const bundleCacheOption = { + name: 'Webpack Bundle Caching', + cliFlag, + description: () => ( + <> + Enable or disable Webpack caching. This flag is enabled by default, use{' '} + --bundle-cache=false to disable caching. + + ), + ssrName: null, + docLink: 'https://www.remotion.dev/docs/config#setcachingenabled', + getValue: ({commandLine}) => { + if (commandLine[cliFlag] !== undefined) { + return { + source: 'cli', + value: commandLine[cliFlag] !== 'false', + }; + } + + return { + source: cachingEnabled ? 'default' : 'config', + value: cachingEnabled, + }; + }, + setConfig: (value: boolean) => { + cachingEnabled = value; + }, + type: true as boolean, + id: cliFlag, +} satisfies AnyRemotionOption; diff --git a/packages/renderer/src/options/env-file.tsx b/packages/renderer/src/options/env-file.tsx new file mode 100644 index 00000000000..17c171af868 --- /dev/null +++ b/packages/renderer/src/options/env-file.tsx @@ -0,0 +1,42 @@ +import type {AnyRemotionOption} from './option'; + +const cliFlag = 'env-file' as const; + +let envFileLocation: string | null = null; + +export const envFileOption = { + name: 'Env File', + cliFlag, + description: () => ( + <> + Specify a location for a dotenv file. Default .env. + + ), + ssrName: null, + docLink: 'https://www.remotion.dev/docs/cli/render#--env-file', + getValue: ({commandLine}) => { + if (commandLine[cliFlag] !== undefined) { + return { + source: 'cli', + value: commandLine[cliFlag] as string, + }; + } + + if (envFileLocation !== null) { + return { + source: 'config', + value: envFileLocation, + }; + } + + return { + source: 'default', + value: null, + }; + }, + setConfig: (value: string | null) => { + envFileLocation = value; + }, + type: '' as string | null, + id: cliFlag, +} satisfies AnyRemotionOption; diff --git a/packages/renderer/src/options/image-sequence.tsx b/packages/renderer/src/options/image-sequence.tsx new file mode 100644 index 00000000000..be10338ace2 --- /dev/null +++ b/packages/renderer/src/options/image-sequence.tsx @@ -0,0 +1,40 @@ +import type {AnyRemotionOption} from './option'; + +const cliFlag = 'sequence' as const; + +let imageSequence = false; + +export const imageSequenceOption = { + name: 'Image Sequence', + cliFlag, + description: () => ( + <> + Pass this flag to output an image sequence instead of a video. The default + image format is JPEG. See{' '} + + setImageSequence() + {' '} + for more details. + + ), + ssrName: null, + docLink: 'https://www.remotion.dev/docs/config#setimagesequence', + getValue: ({commandLine}) => { + if (commandLine[cliFlag] !== undefined) { + return { + source: 'cli', + value: Boolean(commandLine[cliFlag]), + }; + } + + return { + source: imageSequence ? 'config' : 'default', + value: imageSequence, + }; + }, + setConfig: (value: boolean) => { + imageSequence = value; + }, + type: false as boolean, + id: cliFlag, +} satisfies AnyRemotionOption; diff --git a/packages/renderer/src/options/index.tsx b/packages/renderer/src/options/index.tsx index 716bcb7faa1..7174d4e30c2 100644 --- a/packages/renderer/src/options/index.tsx +++ b/packages/renderer/src/options/index.tsx @@ -4,6 +4,7 @@ import {audioBitrateOption} from './audio-bitrate'; import {audioCodecOption} from './audio-codec'; import {beepOnFinishOption} from './beep-on-finish'; import {binariesDirectoryOption} from './binaries-directory'; +import {bundleCacheOption} from './bundle-cache'; import {browserExecutableOption} from './browser-executable'; import {chromeModeOption} from './chrome-mode'; import {colorSpaceOption} from './color-space'; @@ -16,6 +17,7 @@ import {disableGitSourceOption} from './disable-git-source'; import {disableWebSecurityOption} from './disable-web-security'; import {disallowParallelEncodingOption} from './disallow-parallel-encoding'; import {enableLambdaInsights} from './enable-lambda-insights'; +import {envFileOption} from './env-file'; import {enableMultiprocessOnLinuxOption} from './enable-multiprocess-on-linux'; import {encodingBufferSizeOption} from './encoding-buffer-size'; import {encodingMaxRateOption} from './encoding-max-rate'; @@ -27,6 +29,7 @@ import {forSeamlessAacConcatenationOption} from './for-seamless-aac-concatenatio import {forceNewStudioOption} from './force-new-studio'; import {glOption} from './gl'; import {hardwareAccelerationOption} from './hardware-acceleration'; +import {imageSequenceOption} from './image-sequence'; import {headlessOption} from './headless'; import {ignoreCertificateErrorsOption} from './ignore-certificate-errors'; import {imageSequencePatternOption} from './image-sequence-pattern'; @@ -60,11 +63,13 @@ import {publicLicenseKeyOption} from './public-license-key'; import {publicPathOption} from './public-path'; import {reproOption} from './repro'; import {scaleOption} from './scale'; +import {stillFrameOption} from './still-frame'; import {separateAudioOption} from './separate-audio'; import {stillImageFormatOption} from './still-image-format'; import {throwIfSiteExistsOption} from './throw-if-site-exists'; import {delayRenderTimeoutInMillisecondsOption} from './timeout'; import {userAgentOption} from './user-agent'; +import {versionFlagOption} from './version-flag'; import {videoBitrateOption} from './video-bitrate'; import {mediaCacheSizeInBytesOption} from './video-cache-size'; import {videoCodecOption} from './video-codec'; @@ -147,6 +152,11 @@ export const allOptions = { outDirOption, packageManagerOption, webpackPollOption, + stillFrameOption, + imageSequenceOption, + versionFlagOption, + bundleCacheOption, + envFileOption, }; export type AvailableOptions = keyof typeof allOptions; diff --git a/packages/renderer/src/options/still-frame.tsx b/packages/renderer/src/options/still-frame.tsx new file mode 100644 index 00000000000..1e4b332c18a --- /dev/null +++ b/packages/renderer/src/options/still-frame.tsx @@ -0,0 +1,44 @@ +import type {AnyRemotionOption} from './option'; + +const cliFlag = 'frame' as const; + +let currentFrame: number | null = null; + +export const stillFrameOption = { + name: 'Frame', + cliFlag, + description: () => ( + <> + Which frame should be rendered when rendering a still. Default{' '} + 0. From v3.2.27, negative values are allowed, with{' '} + -1 being the last frame. + + ), + ssrName: 'frame' as const, + docLink: 'https://www.remotion.dev/docs/cli/still#--frame', + getValue: ({commandLine}) => { + if (commandLine[cliFlag] !== undefined) { + return { + source: 'cli', + value: Number(commandLine[cliFlag]), + }; + } + + if (currentFrame !== null) { + return { + source: 'config', + value: currentFrame, + }; + } + + return { + source: 'default', + value: null, + }; + }, + setConfig: (value: number | null) => { + currentFrame = value; + }, + type: 0 as number | null, + id: cliFlag, +} satisfies AnyRemotionOption; diff --git a/packages/renderer/src/options/version-flag.tsx b/packages/renderer/src/options/version-flag.tsx new file mode 100644 index 00000000000..deb6d5bff96 --- /dev/null +++ b/packages/renderer/src/options/version-flag.tsx @@ -0,0 +1,31 @@ +import type {AnyRemotionOption} from './option'; + +const cliFlag = 'version' as const; + +export const versionFlagOption = { + name: 'Version', + cliFlag, + description: () => ( + <>Install a specific version. Also enables downgrading to an older version. + ), + ssrName: null, + docLink: 'https://www.remotion.dev/docs/cli/upgrade#--version', + getValue: ({commandLine}) => { + if (commandLine[cliFlag] !== undefined) { + return { + source: 'cli', + value: String(commandLine[cliFlag]), + }; + } + + return { + source: 'default', + value: null, + }; + }, + setConfig: () => { + throw new Error('Cannot set version via config file'); + }, + type: '' as string | null, + id: cliFlag, +} satisfies AnyRemotionOption; From 70b8ab65fd893feb7c957b49fd89beab7459c085 Mon Sep 17 00:00:00 2001 From: Jonny Burger Date: Thu, 19 Feb 2026 18:01:54 +0100 Subject: [PATCH 04/21] Rename setOutDir to setBundleOutDir Co-authored-by: Cursor --- packages/cli/src/config/index.ts | 4 ++-- packages/docs/docs/config.mdx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/config/index.ts b/packages/cli/src/config/index.ts index 94f52a1b26c..99eabbaaeef 100644 --- a/packages/cli/src/config/index.ts +++ b/packages/cli/src/config/index.ts @@ -590,7 +590,7 @@ type FlatConfig = RemotionConfigObject & * Define the output directory for `npx remotion bundle`. * Default: `build` in the Remotion root. */ - setOutDir: (outDir: string | null) => void; + setBundleOutDir: (outDir: string | null) => void; /** * Choose between using Chrome Headless Shell or Chrome for Testing */ @@ -742,7 +742,7 @@ export const Config: FlatConfig = { setPublicLicenseKey: publicLicenseKeyOption.setConfig, setForceNewStudioEnabled: forceNewStudioOption.setConfig, setIPv4: ipv4Option.setConfig, - setOutDir: outDirOption.setConfig, + setBundleOutDir: outDirOption.setConfig, }; export const ConfigInternals = { diff --git a/packages/docs/docs/config.mdx b/packages/docs/docs/config.mdx index 43a6eb1c8d9..d05d00ab59f 100644 --- a/packages/docs/docs/config.mdx +++ b/packages/docs/docs/config.mdx @@ -87,14 +87,14 @@ Config.setPublicDir('./custom-public-dir'); The [command line flag](/docs/cli/render#--public-dir) `--public-dir` will take precedence over this option. -## `setOutDir()` +## `setBundleOutDir()` ```ts twoslash title="remotion.config.ts" import {Config} from '@remotion/cli/config'; // ---cut--- -Config.setOutDir('./custom-build-dir'); +Config.setBundleOutDir('./custom-build-dir'); ``` The [command line flag](/docs/cli/bundle#--out-dir) `--out-dir` will take precedence over this option. From d0b08548d63e54269002b43b1157baee480daa92 Mon Sep 17 00:00:00 2001 From: Jonny Burger Date: Thu, 19 Feb 2026 18:05:46 +0100 Subject: [PATCH 05/21] Merge duplicate env-file resolution blocks in get-env.ts Co-authored-by: Cursor --- packages/cli/src/config/index.ts | 5 ++--- packages/cli/src/get-env.ts | 32 +++++++------------------------- 2 files changed, 9 insertions(+), 28 deletions(-) diff --git a/packages/cli/src/config/index.ts b/packages/cli/src/config/index.ts index 99eabbaaeef..413aa72c484 100644 --- a/packages/cli/src/config/index.ts +++ b/packages/cli/src/config/index.ts @@ -722,9 +722,8 @@ export const Config: FlatConfig = { overrideDuration: overrideDurationOption.setConfig, overrideFfmpegCommand: setFfmpegOverrideFunction, setAudioCodec: audioCodecOption.setConfig, - setOffthreadVideoCacheSizeInBytes: (size) => { - offthreadVideoCacheSizeInBytesOption.setConfig(size); - }, + setOffthreadVideoCacheSizeInBytes: + offthreadVideoCacheSizeInBytesOption.setConfig, setDeleteAfter: deleteAfterOption.setConfig, setColorSpace: colorSpaceOption.setConfig, setDisallowParallelEncoding: disallowParallelEncodingOption.setConfig, diff --git a/packages/cli/src/get-env.ts b/packages/cli/src/get-env.ts index a8bd8af6c79..62fbc9f8559 100644 --- a/packages/cli/src/get-env.ts +++ b/packages/cli/src/get-env.ts @@ -146,37 +146,19 @@ export const getEnvironmentVariables = ( const {value: envFileValue, source: envFileSource} = envFileOption.getValue({ commandLine: parsedCli, }); - if (envFileValue && envFileSource === 'cli') { - const envFile = path.resolve(process.cwd(), envFileValue); - if (!fs.existsSync(envFile)) { - Log.error( - {indent: false, logLevel}, - 'You passed a --env-file but it could not be found.', - ); - Log.error( - {indent: false, logLevel}, - 'We looked for the file at:', - envFile, - ); - Log.error( - {indent: false, logLevel}, - 'Check that your path is correct and try again.', - ); - process.exit(1); - } - - return getEnvForEnvFile({processEnv, envFile, onUpdate, logLevel, indent}); - } const remotionRoot = RenderInternals.findRemotionRoot(); - const configFileSetting = envFileValue && envFileSource === 'config' ? envFileValue : null; - if (configFileSetting) { - const envFile = path.resolve(remotionRoot, configFileSetting); + if (envFileValue && envFileSource !== 'default') { + const baseDir = + envFileSource === 'cli' ? process.cwd() : remotionRoot; + const envFile = path.resolve(baseDir, envFileValue); if (!fs.existsSync(envFile)) { Log.error( {indent: false, logLevel}, - 'You specified a custom .env file using `Config.setDotEnvLocation()` in the config file but it could not be found', + envFileSource === 'cli' + ? 'You passed a --env-file but it could not be found.' + : 'You specified a custom .env file using `Config.setDotEnvLocation()` in the config file but it could not be found', ); Log.error( {indent: false, logLevel}, From 46aca612df228d0625129350cc665e7845130f20 Mon Sep 17 00:00:00 2001 From: Jonny Burger Date: Thu, 19 Feb 2026 18:10:33 +0100 Subject: [PATCH 06/21] Fix bundle-cache: register as boolean flag with null default minimist now properly handles --bundle-cache, --no-bundle-cache, and absence of the flag (null) so getValue can distinguish "not passed" from "explicitly false". Co-authored-by: Cursor --- packages/cli/src/parsed-cli.ts | 2 ++ packages/renderer/src/options/bundle-cache.tsx | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/parsed-cli.ts b/packages/cli/src/parsed-cli.ts index 7643b867381..a454c84f845 100644 --- a/packages/cli/src/parsed-cli.ts +++ b/packages/cli/src/parsed-cli.ts @@ -38,12 +38,14 @@ export const BooleanFlags = [ 'onlyAllocateCpuDuringRequestProcessing', BrowserSafeApis.options.isProductionOption.cliFlag, BrowserSafeApis.options.forceNewStudioOption.cliFlag, + BrowserSafeApis.options.bundleCacheOption.cliFlag, ]; export const parsedCli = minimist(process.argv.slice(2), { boolean: BooleanFlags, default: { overwrite: true, + [BrowserSafeApis.options.bundleCacheOption.cliFlag]: null, muted: null, }, }) as CommandLineOptions & { diff --git a/packages/renderer/src/options/bundle-cache.tsx b/packages/renderer/src/options/bundle-cache.tsx index 575df3cbf60..22301c64743 100644 --- a/packages/renderer/src/options/bundle-cache.tsx +++ b/packages/renderer/src/options/bundle-cache.tsx @@ -16,10 +16,10 @@ export const bundleCacheOption = { ssrName: null, docLink: 'https://www.remotion.dev/docs/config#setcachingenabled', getValue: ({commandLine}) => { - if (commandLine[cliFlag] !== undefined) { + if (commandLine[cliFlag] !== undefined && commandLine[cliFlag] !== null) { return { source: 'cli', - value: commandLine[cliFlag] !== 'false', + value: Boolean(commandLine[cliFlag]), }; } From a39988142791d636163739a449e2f2b182a56ba7 Mon Sep 17 00:00:00 2001 From: Jonny Burger Date: Thu, 19 Feb 2026 18:16:50 +0100 Subject: [PATCH 07/21] Add frame validation back to stillFrameOption Co-authored-by: Cursor --- packages/renderer/src/options/still-frame.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/renderer/src/options/still-frame.tsx b/packages/renderer/src/options/still-frame.tsx index 1e4b332c18a..172c315820e 100644 --- a/packages/renderer/src/options/still-frame.tsx +++ b/packages/renderer/src/options/still-frame.tsx @@ -1,9 +1,18 @@ +import {NoReactInternals} from 'remotion/no-react'; import type {AnyRemotionOption} from './option'; const cliFlag = 'frame' as const; let currentFrame: number | null = null; +const validate = (frame: number) => { + NoReactInternals.validateFrame({ + frame, + durationInFrames: Infinity, + allowFloats: false, + }); +}; + export const stillFrameOption = { name: 'Frame', cliFlag, @@ -18,9 +27,11 @@ export const stillFrameOption = { docLink: 'https://www.remotion.dev/docs/cli/still#--frame', getValue: ({commandLine}) => { if (commandLine[cliFlag] !== undefined) { + const frame = Number(commandLine[cliFlag]); + validate(frame); return { source: 'cli', - value: Number(commandLine[cliFlag]), + value: frame, }; } @@ -37,6 +48,10 @@ export const stillFrameOption = { }; }, setConfig: (value: number | null) => { + if (value !== null) { + validate(value); + } + currentFrame = value; }, type: 0 as number | null, From bc798f1a1f40d340d25077a771c0c69647c43f07 Mon Sep 17 00:00:00 2001 From: Jonny Burger Date: Thu, 19 Feb 2026 18:27:31 +0100 Subject: [PATCH 08/21] Remove height, width, fps, durationInFrames, concurrency from getCliOptions Move concurrency validation into concurrencyOption.getValue(). All callers now get these values directly via option.getValue() instead of through getCliOptions. Also fix cloudrun still.ts which was passing isStill: false instead of isStill: true. Co-authored-by: Cursor --- packages/cli/src/benchmark.ts | 24 +++++++++++---- packages/cli/src/get-cli-options.ts | 30 ------------------- packages/cli/src/render.tsx | 24 +++++++++++---- packages/cli/src/still.ts | 25 ++++++++++------ .../cloudrun/src/cli/commands/render/index.ts | 27 +++++++++++------ packages/cloudrun/src/cli/commands/still.ts | 29 +++++++++++------- .../lambda/src/cli/commands/render/render.ts | 27 +++++++++++------ packages/lambda/src/cli/commands/still.ts | 23 ++++++++------ packages/renderer/src/options/concurrency.tsx | 10 ++++++- 9 files changed, 132 insertions(+), 87 deletions(-) diff --git a/packages/cli/src/benchmark.ts b/packages/cli/src/benchmark.ts index 1cb4c77a617..3dd6e8246f3 100644 --- a/packages/cli/src/benchmark.ts +++ b/packages/cli/src/benchmark.ts @@ -67,6 +67,11 @@ const { userAgentOption, disableWebSecurityOption, ignoreCertificateErrorsOption, + concurrencyOption, + overrideHeightOption, + overrideWidthOption, + overrideFpsOption, + overrideDurationOption, } = BrowserSafeApis.options; const getValidConcurrency = (cliConcurrency: number | string | null) => { @@ -209,17 +214,26 @@ export const benchmarkCommand = async ( envVariables, frameRange: defaultFrameRange, ffmpegOverride, - height, - width, - fps, - durationInFrames, - concurrency: unparsedConcurrency, } = getCliOptions({ isStill: false, logLevel, indent: false, }); + const unparsedConcurrency = concurrencyOption.getValue({ + commandLine: parsedCli, + }).value; + const height = overrideHeightOption.getValue({ + commandLine: parsedCli, + }).value; + const width = overrideWidthOption.getValue({ + commandLine: parsedCli, + }).value; + const fps = overrideFpsOption.getValue({commandLine: parsedCli}).value; + const durationInFrames = overrideDurationOption.getValue({ + commandLine: parsedCli, + }).value; + const pixelFormat = pixelFormatOption.getValue({ commandLine: parsedCli, }).value; diff --git a/packages/cli/src/get-cli-options.ts b/packages/cli/src/get-cli-options.ts index 1802190d201..740ca87bc6b 100644 --- a/packages/cli/src/get-cli-options.ts +++ b/packages/cli/src/get-cli-options.ts @@ -1,5 +1,4 @@ import type {LogLevel} from '@remotion/renderer'; -import {RenderInternals} from '@remotion/renderer'; import {BrowserSafeApis} from '@remotion/renderer/client'; import fs from 'node:fs'; import path from 'node:path'; @@ -63,32 +62,7 @@ export const getCliOptions = (options: { ? true : imageSequence || typeof frameRange === 'number'; - const concurrency = BrowserSafeApis.options.concurrencyOption.getValue({ - commandLine: parsedCli, - }).value; - - const height = BrowserSafeApis.options.overrideHeightOption.getValue({ - commandLine: parsedCli, - }).value; - const width = BrowserSafeApis.options.overrideWidthOption.getValue({ - commandLine: parsedCli, - }).value; - const fps = BrowserSafeApis.options.overrideFpsOption.getValue({ - commandLine: parsedCli, - }).value; - const durationInFrames = - BrowserSafeApis.options.overrideDurationOption.getValue({ - commandLine: parsedCli, - }).value; - - RenderInternals.validateConcurrency({ - value: concurrency, - setting: 'concurrency', - checkIfValidForCurrentMachine: false, - }); - return { - concurrency, frameRange, shouldOutputImageSequence, inputProps: getInputProps(null, options.logLevel), @@ -101,9 +75,5 @@ export const getCliOptions = (options: { commandLine: parsedCli, }).value ?? 0, ffmpegOverride: ConfigInternals.getFfmpegOverrideFunction(), - height, - width, - fps, - durationInFrames, }; }; diff --git a/packages/cli/src/render.tsx b/packages/cli/src/render.tsx index 940cccd5881..f34715dc387 100644 --- a/packages/cli/src/render.tsx +++ b/packages/cli/src/render.tsx @@ -55,6 +55,11 @@ const { userAgentOption, disableWebSecurityOption, ignoreCertificateErrorsOption, + concurrencyOption, + overrideHeightOption, + overrideWidthOption, + overrideFpsOption, + overrideDurationOption, } = BrowserSafeApis.options; export const render = async ( @@ -95,15 +100,10 @@ export const render = async ( } const { - concurrency, frameRange, shouldOutputImageSequence, inputProps, envVariables, - height, - width, - fps, - durationInFrames, ffmpegOverride, } = getCliOptions({ isStill: false, @@ -111,6 +111,20 @@ export const render = async ( indent: false, }); + const concurrency = concurrencyOption.getValue({ + commandLine: parsedCli, + }).value; + const height = overrideHeightOption.getValue({ + commandLine: parsedCli, + }).value; + const width = overrideWidthOption.getValue({ + commandLine: parsedCli, + }).value; + const fps = overrideFpsOption.getValue({commandLine: parsedCli}).value; + const durationInFrames = overrideDurationOption.getValue({ + commandLine: parsedCli, + }).value; + const pixelFormat = pixelFormatOption.getValue({ commandLine: parsedCli, }).value; diff --git a/packages/cli/src/still.ts b/packages/cli/src/still.ts index a6669ff9334..c8b03d0b419 100644 --- a/packages/cli/src/still.ts +++ b/packages/cli/src/still.ts @@ -34,6 +34,10 @@ const { userAgentOption, disableWebSecurityOption, ignoreCertificateErrorsOption, + overrideHeightOption, + overrideWidthOption, + overrideFpsOption, + overrideDurationOption, } = BrowserSafeApis.options; export const still = async ( @@ -73,20 +77,23 @@ export const still = async ( process.exit(1); } - const { - envVariables, - height, - inputProps, - stillFrame, - width, - fps, - durationInFrames, - } = getCliOptions({ + const {envVariables, inputProps, stillFrame} = getCliOptions({ isStill: true, logLevel, indent: false, }); + const height = overrideHeightOption.getValue({ + commandLine: parsedCli, + }).value; + const width = overrideWidthOption.getValue({ + commandLine: parsedCli, + }).value; + const fps = overrideFpsOption.getValue({commandLine: parsedCli}).value; + const durationInFrames = overrideDurationOption.getValue({ + commandLine: parsedCli, + }).value; + const browserExecutable = browserExecutableOption.getValue({ commandLine: parsedCli, }).value; diff --git a/packages/cloudrun/src/cli/commands/render/index.ts b/packages/cloudrun/src/cli/commands/render/index.ts index 7972396fa99..6e7331aa251 100644 --- a/packages/cloudrun/src/cli/commands/render/index.ts +++ b/packages/cloudrun/src/cli/commands/render/index.ts @@ -45,6 +45,10 @@ const { userAgentOption, disableWebSecurityOption, ignoreCertificateErrorsOption, + overrideHeightOption, + overrideWidthOption, + overrideFpsOption, + overrideDurationOption, } = BrowserSafeApis.options; export const renderCommand = async ( @@ -84,20 +88,25 @@ export const renderCommand = async ( commandLine: CliInternals.parsedCli, }).value; - const { - envVariables, - frameRange, - inputProps, - height, - width, - fps, - durationInFrames, - } = CliInternals.getCliOptions({ + const {envVariables, frameRange, inputProps} = CliInternals.getCliOptions({ isStill: false, logLevel, indent: false, }); + const height = overrideHeightOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value; + const width = overrideWidthOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value; + const fps = overrideFpsOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value; + const durationInFrames = overrideDurationOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value; + const pixelFormat = pixelFormatOption.getValue({ commandLine: CliInternals.parsedCli, }).value; diff --git a/packages/cloudrun/src/cli/commands/still.ts b/packages/cloudrun/src/cli/commands/still.ts index 78198a7a4b9..1a3d1e323bd 100644 --- a/packages/cloudrun/src/cli/commands/still.ts +++ b/packages/cloudrun/src/cli/commands/still.ts @@ -28,6 +28,10 @@ const { userAgentOption, disableWebSecurityOption, ignoreCertificateErrorsOption, + overrideHeightOption, + overrideWidthOption, + overrideFpsOption, + overrideDurationOption, } = BrowserSafeApis.options; export const stillCommand = async ( @@ -45,20 +49,25 @@ export const stillCommand = async ( region, } = await renderArgsCheck(STILL_COMMAND, args, logLevel); - const { - envVariables, - inputProps, - stillFrame, - height, - width, - fps, - durationInFrames, - } = CliInternals.getCliOptions({ - isStill: false, + const {envVariables, inputProps, stillFrame} = CliInternals.getCliOptions({ + isStill: true, logLevel, indent: false, }); + const height = overrideHeightOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value; + const width = overrideWidthOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value; + const fps = overrideFpsOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value; + const durationInFrames = overrideDurationOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value; + const browserExecutable = browserExecutableOption.getValue({ commandLine: CliInternals.parsedCli, }).value; diff --git a/packages/lambda/src/cli/commands/render/render.ts b/packages/lambda/src/cli/commands/render/render.ts index a714d952d6d..d435064cf4a 100644 --- a/packages/lambda/src/cli/commands/render/render.ts +++ b/packages/lambda/src/cli/commands/render/render.ts @@ -65,6 +65,10 @@ const { ignoreCertificateErrorsOption, audioCodecOption, videoCodecOption, + overrideHeightOption, + overrideWidthOption, + overrideFpsOption, + overrideDurationOption, } = BrowserSafeApis.options; export const renderCommand = async ({ @@ -95,20 +99,25 @@ export const renderCommand = async ({ const region = getAwsRegion(); - const { - envVariables, - frameRange, - inputProps, - height, - width, - fps, - durationInFrames, - } = CliInternals.getCliOptions({ + const {envVariables, frameRange, inputProps} = CliInternals.getCliOptions({ isStill: false, logLevel, indent: false, }); + const height = overrideHeightOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value; + const width = overrideWidthOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value; + const fps = overrideFpsOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value; + const durationInFrames = overrideDurationOption.getValue({ + commandLine: CliInternals.parsedCli, + }).value; + const pixelFormat = pixelFormatOption.getValue({ commandLine: CliInternals.parsedCli, }).value; diff --git a/packages/lambda/src/cli/commands/still.ts b/packages/lambda/src/cli/commands/still.ts index e29ad658efb..4012c3623fd 100644 --- a/packages/lambda/src/cli/commands/still.ts +++ b/packages/lambda/src/cli/commands/still.ts @@ -39,6 +39,10 @@ const { userAgentOption, disableWebSecurityOption, ignoreCertificateErrorsOption, + overrideHeightOption, + overrideWidthOption, + overrideFpsOption, + overrideDurationOption, } = BrowserSafeApis.options; const { @@ -79,20 +83,21 @@ export const stillCommand = async ({ quit(1); } - const { - envVariables, - inputProps, - stillFrame, - height, - width, - fps, - durationInFrames, - } = getCliOptions({ + const {envVariables, inputProps, stillFrame} = getCliOptions({ isStill: true, logLevel, indent: false, }); + const height = overrideHeightOption.getValue({ + commandLine: parsedCli, + }).value; + const width = overrideWidthOption.getValue({commandLine: parsedCli}).value; + const fps = overrideFpsOption.getValue({commandLine: parsedCli}).value; + const durationInFrames = overrideDurationOption.getValue({ + commandLine: parsedCli, + }).value; + const browserExecutable = browserExecutableOption.getValue({ commandLine: parsedCli, }).value; diff --git a/packages/renderer/src/options/concurrency.tsx b/packages/renderer/src/options/concurrency.tsx index 81314bd273d..3243305bdb4 100644 --- a/packages/renderer/src/options/concurrency.tsx +++ b/packages/renderer/src/options/concurrency.tsx @@ -1,4 +1,5 @@ import type {AnyRemotionOption} from './option'; +import {validateConcurrency} from '../validate-concurrency'; export type Concurrency = number | string | null; @@ -21,9 +22,16 @@ export const concurrencyOption = { type: null as Concurrency, getValue: ({commandLine}) => { if (commandLine[cliFlag] !== undefined) { + const value = commandLine[cliFlag] as Concurrency; + validateConcurrency({ + value, + setting: 'concurrency', + checkIfValidForCurrentMachine: false, + }); + return { source: 'cli', - value: commandLine[cliFlag] as Concurrency, + value, }; } From 696037fb1835892e32642673ba0ebae3df4e7091 Mon Sep 17 00:00:00 2001 From: Jonny Burger Date: Thu, 19 Feb 2026 20:54:26 +0100 Subject: [PATCH 09/21] Update packages/renderer/src/options/bundle-cache.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/renderer/src/options/bundle-cache.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/renderer/src/options/bundle-cache.tsx b/packages/renderer/src/options/bundle-cache.tsx index 22301c64743..f84831f6500 100644 --- a/packages/renderer/src/options/bundle-cache.tsx +++ b/packages/renderer/src/options/bundle-cache.tsx @@ -29,6 +29,11 @@ export const bundleCacheOption = { }; }, setConfig: (value: boolean) => { + if (typeof value !== 'boolean') { + throw new TypeError( + `Value for "${cliFlag}" must be a boolean, but got ${typeof value}.`, + ); + } cachingEnabled = value; }, type: true as boolean, From 37b0918a7890e1131537ad0cb0243e3174814593 Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Thu, 19 Feb 2026 20:59:48 +0100 Subject: [PATCH 10/21] pass through --- packages/cli/src/benchmark.ts | 5 +++++ packages/cli/src/bundle.ts | 5 +++++ packages/cli/src/compositions.ts | 5 +++++ packages/cli/src/render-flows/render.ts | 3 +++ packages/cli/src/render-flows/still.ts | 3 +++ packages/cli/src/render-queue/process-still.ts | 5 +++++ packages/cli/src/render-queue/process-video.ts | 5 +++++ packages/cli/src/render.tsx | 5 +++++ packages/cli/src/setup-cache.ts | 10 +++++----- packages/cli/src/still.ts | 5 +++++ packages/renderer/src/options/concurrency.tsx | 6 ++++++ 11 files changed, 52 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/benchmark.ts b/packages/cli/src/benchmark.ts index 3dd6e8246f3..2df9a952621 100644 --- a/packages/cli/src/benchmark.ts +++ b/packages/cli/src/benchmark.ts @@ -72,6 +72,7 @@ const { overrideWidthOption, overrideFpsOption, overrideDurationOption, + bundleCacheOption, } = BrowserSafeApis.options; const getValidConcurrency = (cliConcurrency: number | string | null) => { @@ -280,6 +281,9 @@ export const benchmarkCommand = async ( const keyboardShortcutsEnabled = keyboardShortcutsOption.getValue({ commandLine: parsedCli, }).value; + const shouldCache = bundleCacheOption.getValue({ + commandLine: parsedCli, + }).value; if (experimentalClientSideRenderingEnabled) { Log.warn( @@ -352,6 +356,7 @@ export const benchmarkCommand = async ( experimentalClientSideRenderingEnabled, askAIEnabled, keyboardShortcutsEnabled, + shouldCache, }); registerCleanupJob(`Deleting bundle`, () => cleanupBundle()); diff --git a/packages/cli/src/bundle.ts b/packages/cli/src/bundle.ts index 6c85fefca7d..ff0e56b4a6b 100644 --- a/packages/cli/src/bundle.ts +++ b/packages/cli/src/bundle.ts @@ -22,6 +22,7 @@ const { experimentalClientSideRenderingOption, keyboardShortcutsOption, outDirOption, + bundleCacheOption, } = BrowserSafeApis.options; export const bundleCommand = async ( @@ -74,6 +75,9 @@ export const bundleCommand = async ( const keyboardShortcutsEnabled = keyboardShortcutsOption.getValue({ commandLine: parsedCli, }).value; + const shouldCache = bundleCacheOption.getValue({ + commandLine: parsedCli, + }).value; if (experimentalClientSideRenderingEnabled) { Log.warn( @@ -162,6 +166,7 @@ export const bundleCommand = async ( experimentalClientSideRenderingEnabled, askAIEnabled, keyboardShortcutsEnabled, + shouldCache, }); Log.info( diff --git a/packages/cli/src/compositions.ts b/packages/cli/src/compositions.ts index 0f0deb39d8b..0b6e60766c2 100644 --- a/packages/cli/src/compositions.ts +++ b/packages/cli/src/compositions.ts @@ -33,6 +33,7 @@ const { userAgentOption, disableWebSecurityOption, ignoreCertificateErrorsOption, + bundleCacheOption, } = BrowserSafeApis.options; export const listCompositionsCommand = async ( @@ -135,6 +136,9 @@ export const listCompositionsCommand = async ( const keyboardShortcutsEnabled = keyboardShortcutsOption.getValue({ commandLine: parsedCli, }).value; + const shouldCache = bundleCacheOption.getValue({ + commandLine: parsedCli, + }).value; if (experimentalClientSideRenderingEnabled) { Log.warn( @@ -168,6 +172,7 @@ export const listCompositionsCommand = async ( experimentalClientSideRenderingEnabled, askAIEnabled, keyboardShortcutsEnabled, + shouldCache, }); registerCleanupJob(`Cleanup bundle`, () => cleanupBundle()); diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts index 2aca245daf2..2fc509d7570 100644 --- a/packages/cli/src/render-flows/render.ts +++ b/packages/cli/src/render-flows/render.ts @@ -129,6 +129,7 @@ export const renderVideoFlow = async ({ askAIEnabled, experimentalClientSideRenderingEnabled, keyboardShortcutsEnabled, + shouldCache, }: { remotionRoot: string; fullEntryPoint: string; @@ -194,6 +195,7 @@ export const renderVideoFlow = async ({ askAIEnabled: boolean; experimentalClientSideRenderingEnabled: boolean; keyboardShortcutsEnabled: boolean; + shouldCache: boolean; }) => { let bundlingProgress: BundlingState | null = null; let renderingProgress: RenderingProgressInput | null = null; @@ -337,6 +339,7 @@ export const renderVideoFlow = async ({ experimentalClientSideRenderingEnabled, askAIEnabled, keyboardShortcutsEnabled, + shouldCache, }, ); diff --git a/packages/cli/src/render-flows/still.ts b/packages/cli/src/render-flows/still.ts index 7521da8cfd9..d18a3298002 100644 --- a/packages/cli/src/render-flows/still.ts +++ b/packages/cli/src/render-flows/still.ts @@ -87,6 +87,7 @@ export const renderStillFlow = async ({ askAIEnabled, experimentalClientSideRenderingEnabled, keyboardShortcutsEnabled, + shouldCache, }: { remotionRoot: string; fullEntryPoint: string; @@ -126,6 +127,7 @@ export const renderStillFlow = async ({ askAIEnabled: boolean; experimentalClientSideRenderingEnabled: boolean; keyboardShortcutsEnabled: boolean; + shouldCache: boolean; }) => { const isVerbose = RenderInternals.isEqualOrBelowLogLevel(logLevel, 'verbose'); Log.verbose( @@ -229,6 +231,7 @@ export const renderStillFlow = async ({ experimentalClientSideRenderingEnabled, askAIEnabled, keyboardShortcutsEnabled, + shouldCache, }, ); diff --git a/packages/cli/src/render-queue/process-still.ts b/packages/cli/src/render-queue/process-still.ts index 904e47ee740..8a642d4b3e9 100644 --- a/packages/cli/src/render-queue/process-still.ts +++ b/packages/cli/src/render-queue/process-still.ts @@ -11,6 +11,7 @@ const { experimentalClientSideRenderingOption, keyboardShortcutsOption, browserExecutableOption, + bundleCacheOption, } = BrowserSafeApis.options; export const processStill = async ({ @@ -45,6 +46,9 @@ export const processStill = async ({ const keyboardShortcutsEnabled = keyboardShortcutsOption.getValue({ commandLine: parsedCli, }).value; + const shouldCache = bundleCacheOption.getValue({ + commandLine: parsedCli, + }).value; const fullEntryPoint = convertEntryPointToServeUrl(entryPoint); @@ -88,5 +92,6 @@ export const processStill = async ({ askAIEnabled, experimentalClientSideRenderingEnabled, keyboardShortcutsEnabled, + shouldCache, }); }; diff --git a/packages/cli/src/render-queue/process-video.ts b/packages/cli/src/render-queue/process-video.ts index eda077a1cc8..a6618ae9b12 100644 --- a/packages/cli/src/render-queue/process-video.ts +++ b/packages/cli/src/render-queue/process-video.ts @@ -13,6 +13,7 @@ const { experimentalClientSideRenderingOption, keyboardShortcutsOption, browserExecutableOption, + bundleCacheOption, } = BrowserSafeApis.options; export const processVideoJob = async ({ @@ -41,6 +42,9 @@ export const processVideoJob = async ({ const keyboardShortcutsEnabled = keyboardShortcutsOption.getValue({ commandLine: parsedCli, }).value; + const shouldCache = bundleCacheOption.getValue({ + commandLine: parsedCli, + }).value; const {ffmpegOverride} = getCliOptions({ isStill: true, @@ -123,5 +127,6 @@ export const processVideoJob = async ({ experimentalClientSideRenderingOption.getValue({commandLine: parsedCli}) .value, keyboardShortcutsEnabled, + shouldCache, }); }; diff --git a/packages/cli/src/render.tsx b/packages/cli/src/render.tsx index f34715dc387..bcdee69c71e 100644 --- a/packages/cli/src/render.tsx +++ b/packages/cli/src/render.tsx @@ -60,6 +60,7 @@ const { overrideWidthOption, overrideFpsOption, overrideDurationOption, + bundleCacheOption, } = BrowserSafeApis.options; export const render = async ( @@ -246,6 +247,9 @@ export const render = async ( const mediaCacheSizeInBytes = mediaCacheSizeInBytesOption.getValue({ commandLine: parsedCli, }).value; + const shouldCache = bundleCacheOption.getValue({ + commandLine: parsedCli, + }).value; await renderVideoFlow({ fullEntryPoint, @@ -321,5 +325,6 @@ export const render = async ( experimentalClientSideRenderingOption.getValue({commandLine: parsedCli}) .value, keyboardShortcutsEnabled, + shouldCache, }); }; diff --git a/packages/cli/src/setup-cache.ts b/packages/cli/src/setup-cache.ts index 7701c8383fe..517758fe312 100644 --- a/packages/cli/src/setup-cache.ts +++ b/packages/cli/src/setup-cache.ts @@ -2,7 +2,6 @@ import type {MandatoryLegacyBundleOptions} from '@remotion/bundler'; import {BundlerInternals} from '@remotion/bundler'; import type {LogLevel} from '@remotion/renderer'; import {RenderInternals} from '@remotion/renderer'; -import {BrowserSafeApis} from '@remotion/renderer/client'; import type {BundlingState, CopyingState} from '@remotion/studio-server'; import type {GitSource} from '@remotion/studio-shared'; import {existsSync} from 'fs'; @@ -36,6 +35,7 @@ export const bundleOnCliOrTakeServeUrl = async ({ experimentalClientSideRenderingEnabled, askAIEnabled, keyboardShortcutsEnabled, + shouldCache, }: { fullPath: string; remotionRoot: string; @@ -58,6 +58,7 @@ export const bundleOnCliOrTakeServeUrl = async ({ experimentalClientSideRenderingEnabled: boolean; askAIEnabled: boolean; keyboardShortcutsEnabled: boolean; + shouldCache: boolean; }): Promise<{ urlOrBundle: string; cleanup: () => void; @@ -101,6 +102,7 @@ export const bundleOnCliOrTakeServeUrl = async ({ experimentalClientSideRenderingEnabled, askAIEnabled, keyboardShortcutsEnabled, + shouldCache, }); return { @@ -128,6 +130,7 @@ export const bundleOnCli = async ({ experimentalClientSideRenderingEnabled, askAIEnabled, keyboardShortcutsEnabled, + shouldCache, }: { fullPath: string; remotionRoot: string; @@ -150,11 +153,8 @@ export const bundleOnCli = async ({ experimentalClientSideRenderingEnabled: boolean; keyboardShortcutsEnabled: boolean; askAIEnabled: boolean; + shouldCache: boolean; }) => { - const shouldCache = BrowserSafeApis.options.bundleCacheOption.getValue({ - commandLine: {}, - }).value; - const symlinkState: SymbolicLinksState = { symlinks: [], }; diff --git a/packages/cli/src/still.ts b/packages/cli/src/still.ts index c8b03d0b419..3caf73681c8 100644 --- a/packages/cli/src/still.ts +++ b/packages/cli/src/still.ts @@ -38,6 +38,7 @@ const { overrideWidthOption, overrideFpsOption, overrideDurationOption, + bundleCacheOption, } = BrowserSafeApis.options; export const still = async ( @@ -154,6 +155,9 @@ export const still = async ( const keyboardShortcutsEnabled = keyboardShortcutsOption.getValue({ commandLine: parsedCli, }).value; + const shouldCache = bundleCacheOption.getValue({ + commandLine: parsedCli, + }).value; const chromiumOptions: Required = { disableWebSecurity, @@ -213,5 +217,6 @@ export const still = async ( experimentalClientSideRenderingOption.getValue({commandLine: parsedCli}) .value, keyboardShortcutsEnabled, + shouldCache, }); }; diff --git a/packages/renderer/src/options/concurrency.tsx b/packages/renderer/src/options/concurrency.tsx index 3243305bdb4..c9bb2d7c91c 100644 --- a/packages/renderer/src/options/concurrency.tsx +++ b/packages/renderer/src/options/concurrency.tsx @@ -48,6 +48,12 @@ export const concurrencyOption = { }; }, setConfig: (value) => { + validateConcurrency({ + value, + setting: 'Config.setConcurrency', + checkIfValidForCurrentMachine: false, + }); + currentConcurrency = value; }, id: cliFlag, From f6eea486ff19556d54caaf039c702861fcd66335 Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Thu, 19 Feb 2026 21:02:38 +0100 Subject: [PATCH 11/21] stylecheck --- packages/cli/src/get-cli-options.ts | 7 ++++--- packages/cli/src/get-env.ts | 3 +-- packages/cli/src/index.ts | 4 +++- packages/renderer/src/options/bundle-cache.tsx | 1 + packages/renderer/src/options/concurrency.tsx | 2 +- packages/renderer/src/options/index.tsx | 12 ++++++------ packages/renderer/src/options/version-flag.tsx | 4 +++- packages/renderer/src/options/webpack-poll.tsx | 3 ++- 8 files changed, 21 insertions(+), 15 deletions(-) diff --git a/packages/cli/src/get-cli-options.ts b/packages/cli/src/get-cli-options.ts index 740ca87bc6b..b1f606009d4 100644 --- a/packages/cli/src/get-cli-options.ts +++ b/packages/cli/src/get-cli-options.ts @@ -71,9 +71,10 @@ export const getCliOptions = (options: { options.logLevel, options.indent, ), - stillFrame: BrowserSafeApis.options.stillFrameOption.getValue({ - commandLine: parsedCli, - }).value ?? 0, + stillFrame: + BrowserSafeApis.options.stillFrameOption.getValue({ + commandLine: parsedCli, + }).value ?? 0, ffmpegOverride: ConfigInternals.getFfmpegOverrideFunction(), }; }; diff --git a/packages/cli/src/get-env.ts b/packages/cli/src/get-env.ts index 62fbc9f8559..42464d3ad89 100644 --- a/packages/cli/src/get-env.ts +++ b/packages/cli/src/get-env.ts @@ -150,8 +150,7 @@ export const getEnvironmentVariables = ( const remotionRoot = RenderInternals.findRemotionRoot(); if (envFileValue && envFileSource !== 'default') { - const baseDir = - envFileSource === 'cli' ? process.cwd() : remotionRoot; + const baseDir = envFileSource === 'cli' ? process.cwd() : remotionRoot; const envFile = path.resolve(baseDir, envFileValue); if (!fs.existsSync(envFile)) { Log.error( diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index dd110d3ef18..01464651d09 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -119,7 +119,9 @@ export const cli = async () => { await upgradeCommand({ remotionRoot, packageManager: packageManager ?? undefined, - version: versionFlagOption.getValue({commandLine: parsedCli}).value ?? undefined, + version: + versionFlagOption.getValue({commandLine: parsedCli}).value ?? + undefined, logLevel, args, }); diff --git a/packages/renderer/src/options/bundle-cache.tsx b/packages/renderer/src/options/bundle-cache.tsx index f84831f6500..dc274724f6d 100644 --- a/packages/renderer/src/options/bundle-cache.tsx +++ b/packages/renderer/src/options/bundle-cache.tsx @@ -34,6 +34,7 @@ export const bundleCacheOption = { `Value for "${cliFlag}" must be a boolean, but got ${typeof value}.`, ); } + cachingEnabled = value; }, type: true as boolean, diff --git a/packages/renderer/src/options/concurrency.tsx b/packages/renderer/src/options/concurrency.tsx index c9bb2d7c91c..da6de3942d3 100644 --- a/packages/renderer/src/options/concurrency.tsx +++ b/packages/renderer/src/options/concurrency.tsx @@ -1,5 +1,5 @@ -import type {AnyRemotionOption} from './option'; import {validateConcurrency} from '../validate-concurrency'; +import type {AnyRemotionOption} from './option'; export type Concurrency = number | string | null; diff --git a/packages/renderer/src/options/index.tsx b/packages/renderer/src/options/index.tsx index 7174d4e30c2..877b934942b 100644 --- a/packages/renderer/src/options/index.tsx +++ b/packages/renderer/src/options/index.tsx @@ -4,8 +4,8 @@ import {audioBitrateOption} from './audio-bitrate'; import {audioCodecOption} from './audio-codec'; import {beepOnFinishOption} from './beep-on-finish'; import {binariesDirectoryOption} from './binaries-directory'; -import {bundleCacheOption} from './bundle-cache'; import {browserExecutableOption} from './browser-executable'; +import {bundleCacheOption} from './bundle-cache'; import {chromeModeOption} from './chrome-mode'; import {colorSpaceOption} from './color-space'; import {concurrencyOption} from './concurrency'; @@ -17,11 +17,11 @@ import {disableGitSourceOption} from './disable-git-source'; import {disableWebSecurityOption} from './disable-web-security'; import {disallowParallelEncodingOption} from './disallow-parallel-encoding'; import {enableLambdaInsights} from './enable-lambda-insights'; -import {envFileOption} from './env-file'; import {enableMultiprocessOnLinuxOption} from './enable-multiprocess-on-linux'; import {encodingBufferSizeOption} from './encoding-buffer-size'; import {encodingMaxRateOption} from './encoding-max-rate'; import {enforceAudioOption} from './enforce-audio'; +import {envFileOption} from './env-file'; import {everyNthFrameOption} from './every-nth-frame'; import {experimentalClientSideRenderingOption} from './experimental-client-side-rendering'; import {folderExpiryOption} from './folder-expiry'; @@ -29,9 +29,9 @@ import {forSeamlessAacConcatenationOption} from './for-seamless-aac-concatenatio import {forceNewStudioOption} from './force-new-studio'; import {glOption} from './gl'; import {hardwareAccelerationOption} from './hardware-acceleration'; -import {imageSequenceOption} from './image-sequence'; import {headlessOption} from './headless'; import {ignoreCertificateErrorsOption} from './ignore-certificate-errors'; +import {imageSequenceOption} from './image-sequence'; import {imageSequencePatternOption} from './image-sequence-pattern'; import {ipv4Option} from './ipv4'; import {isProductionOption} from './is-production'; @@ -45,16 +45,16 @@ import {mutedOption} from './mute'; import {numberOfGifLoopsOption} from './number-of-gif-loops'; import {numberOfSharedAudioTagsOption} from './number-of-shared-audio-tags'; import {offthreadVideoCacheSizeInBytesOption} from './offthreadvideo-cache-size'; -import {outDirOption} from './out-dir'; -import {packageManagerOption} from './package-manager'; import {offthreadVideoThreadsOption} from './offthreadvideo-threads'; import {onBrowserDownloadOption} from './on-browser-download'; import type {AnyRemotionOption} from './option'; +import {outDirOption} from './out-dir'; import {overrideDurationOption} from './override-duration'; import {overrideFpsOption} from './override-fps'; import {overrideHeightOption} from './override-height'; import {overrideWidthOption} from './override-width'; import {overwriteOption} from './overwrite'; +import {packageManagerOption} from './package-manager'; import {pixelFormatOption} from './pixel-format'; import {preferLosslessAudioOption} from './prefer-lossless'; import {proResProfileOption} from './prores-profile'; @@ -63,8 +63,8 @@ import {publicLicenseKeyOption} from './public-license-key'; import {publicPathOption} from './public-path'; import {reproOption} from './repro'; import {scaleOption} from './scale'; -import {stillFrameOption} from './still-frame'; import {separateAudioOption} from './separate-audio'; +import {stillFrameOption} from './still-frame'; import {stillImageFormatOption} from './still-image-format'; import {throwIfSiteExistsOption} from './throw-if-site-exists'; import {delayRenderTimeoutInMillisecondsOption} from './timeout'; diff --git a/packages/renderer/src/options/version-flag.tsx b/packages/renderer/src/options/version-flag.tsx index deb6d5bff96..558023f52b8 100644 --- a/packages/renderer/src/options/version-flag.tsx +++ b/packages/renderer/src/options/version-flag.tsx @@ -6,7 +6,9 @@ export const versionFlagOption = { name: 'Version', cliFlag, description: () => ( - <>Install a specific version. Also enables downgrading to an older version. + <> + Install a specific version. Also enables downgrading to an older version. + ), ssrName: null, docLink: 'https://www.remotion.dev/docs/cli/upgrade#--version', diff --git a/packages/renderer/src/options/webpack-poll.tsx b/packages/renderer/src/options/webpack-poll.tsx index ceb35bcbe64..dbf2a9fe3a5 100644 --- a/packages/renderer/src/options/webpack-poll.tsx +++ b/packages/renderer/src/options/webpack-poll.tsx @@ -15,7 +15,8 @@ export const webpackPollOption = { ), ssrName: null, - docLink: 'https://www.remotion.dev/docs/config#setwebpackpollinginmilliseconds', + docLink: + 'https://www.remotion.dev/docs/config#setwebpackpollinginmilliseconds', getValue: ({commandLine}) => { if (commandLine[cliFlag] !== undefined) { const val = commandLine[cliFlag]; From b269996292917ff0307227c1047256938d313d7b Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Thu, 19 Feb 2026 21:08:16 +0100 Subject: [PATCH 12/21] Use browser-safe concurrency validation to fix webpack bundle error The validate-concurrency.ts import pulls in node:child_process via get-cpu-count.ts, breaking webpack bundling. Replace with an inline browser-safe validator that performs the same type/format checks. Co-Authored-By: Claude Opus 4.6 --- packages/renderer/src/options/concurrency.tsx | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/renderer/src/options/concurrency.tsx b/packages/renderer/src/options/concurrency.tsx index da6de3942d3..1a3907df03d 100644 --- a/packages/renderer/src/options/concurrency.tsx +++ b/packages/renderer/src/options/concurrency.tsx @@ -1,4 +1,3 @@ -import {validateConcurrency} from '../validate-concurrency'; import type {AnyRemotionOption} from './option'; export type Concurrency = number | string | null; @@ -7,6 +6,30 @@ let currentConcurrency: Concurrency = null; const cliFlag = 'concurrency' as const; +// Browser-safe validation that does not pull in Node.js modules +// (validate-concurrency.ts imports node:child_process via get-cpu-count.ts) +const validateConcurrencyValue = (value: unknown, setting: string) => { + if (typeof value === 'undefined' || value === null) { + return; + } + + if (typeof value !== 'number' && typeof value !== 'string') { + throw new Error(setting + ' must a number or a string but is ' + value); + } + + if (typeof value === 'number') { + if (value % 1 !== 0) { + throw new Error(setting + ' must be an integer, but is ' + value); + } + } else if (!/^\d+(\.\d+)?%$/.test(value)) { + throw new Error( + `${setting} must be a number or percentage, but is ${JSON.stringify( + value, + )}`, + ); + } +}; + export const concurrencyOption = { name: 'Concurrency', cliFlag, @@ -23,11 +46,7 @@ export const concurrencyOption = { getValue: ({commandLine}) => { if (commandLine[cliFlag] !== undefined) { const value = commandLine[cliFlag] as Concurrency; - validateConcurrency({ - value, - setting: 'concurrency', - checkIfValidForCurrentMachine: false, - }); + validateConcurrencyValue(value, 'concurrency'); return { source: 'cli', @@ -48,11 +67,7 @@ export const concurrencyOption = { }; }, setConfig: (value) => { - validateConcurrency({ - value, - setting: 'Config.setConcurrency', - checkIfValidForCurrentMachine: false, - }); + validateConcurrencyValue(value, 'Config.setConcurrency'); currentConcurrency = value; }, From 2fa1beed783108302e271b4064d201c264720253 Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Thu, 19 Feb 2026 21:12:38 +0100 Subject: [PATCH 13/21] Update render.ts --- packages/cli/src/render-flows/render.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts index 2fc509d7570..995f2a80440 100644 --- a/packages/cli/src/render-flows/render.ts +++ b/packages/cli/src/render-flows/render.ts @@ -197,6 +197,12 @@ export const renderVideoFlow = async ({ keyboardShortcutsEnabled: boolean; shouldCache: boolean; }) => { + RenderInternals.validateConcurrency({ + value: concurrency, + setting: 'concurrency', + checkIfValidForCurrentMachine: true, + }); + let bundlingProgress: BundlingState | null = null; let renderingProgress: RenderingProgressInput | null = null; let stitchingProgress: StitchingProgressInput | null = null; From ee489bea5e1f61b1d970af28bb99198a3514b932 Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Thu, 19 Feb 2026 21:24:59 +0100 Subject: [PATCH 14/21] `@remotion/cli`: Migrate runs, gl, repro, and muted CLI flags to options system Co-Authored-By: Claude Opus 4.6 --- packages/cli/src/benchmark.ts | 5 ++- packages/cli/src/parse-command-line.ts | 13 ++++--- packages/docs/docs/cli/benchmark.mdx | 4 +-- packages/renderer/src/options/index.tsx | 2 ++ packages/renderer/src/options/runs.tsx | 47 +++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 packages/renderer/src/options/runs.tsx diff --git a/packages/cli/src/benchmark.ts b/packages/cli/src/benchmark.ts index 2df9a952621..9c8d94b7a7f 100644 --- a/packages/cli/src/benchmark.ts +++ b/packages/cli/src/benchmark.ts @@ -24,8 +24,6 @@ import {shouldUseNonOverlayingLogger} from './should-use-non-overlaying-logger'; import {showMultiCompositionsPicker} from './show-compositions-picker'; import {truthy} from './truthy'; -const DEFAULT_RUNS = 3; - const { audioBitrateOption, x264Option, @@ -73,6 +71,7 @@ const { overrideFpsOption, overrideDurationOption, bundleCacheOption, + runsOption, } = BrowserSafeApis.options; const getValidConcurrency = (cliConcurrency: number | string | null) => { @@ -188,7 +187,7 @@ export const benchmarkCommand = async ( args: string[], logLevel: LogLevel, ) => { - const runs: number = parsedCli.runs ?? DEFAULT_RUNS; + const runs = runsOption.getValue({commandLine: parsedCli}).value; const {file, reason, remainingArgs} = findEntryPoint({ args, diff --git a/packages/cli/src/parse-command-line.ts b/packages/cli/src/parse-command-line.ts index 5e62cdd0702..28806252a4c 100644 --- a/packages/cli/src/parse-command-line.ts +++ b/packages/cli/src/parse-command-line.ts @@ -1,6 +1,5 @@ import type { AudioCodec, - OpenGlRenderer, StillImageFormat, VideoImageFormat, } from '@remotion/renderer'; @@ -61,6 +60,10 @@ const { versionFlagOption, bundleCacheOption, envFileOption, + glOption, + runsOption, + reproOption, + mutedOption, } = BrowserSafeApis.options; export type CommandLineOptions = { @@ -131,15 +134,15 @@ export type CommandLineOptions = { [experimentalClientSideRenderingOption.cliFlag]: TypeOfOption< typeof experimentalClientSideRenderingOption >; - muted: boolean; + [mutedOption.cliFlag]: TypeOfOption; [overrideHeightOption.cliFlag]: TypeOfOption; [overrideWidthOption.cliFlag]: TypeOfOption; [overrideFpsOption.cliFlag]: TypeOfOption; [overrideDurationOption.cliFlag]: TypeOfOption; - runs: number; + [runsOption.cliFlag]: TypeOfOption; concurrencies: string; [enforceAudioOption.cliFlag]: TypeOfOption; - gl: OpenGlRenderer; + [glOption.cliFlag]: TypeOfOption; [packageManagerOption.cliFlag]: TypeOfOption; [webpackPollOption.cliFlag]: TypeOfOption; ['no-open']: boolean; @@ -154,7 +157,7 @@ export type CommandLineOptions = { [enableMultiprocessOnLinuxOption.cliFlag]: TypeOfOption< typeof enableMultiprocessOnLinuxOption >; - repro: boolean; + [reproOption.cliFlag]: TypeOfOption; [imageSequencePatternOption.cliFlag]: TypeOfOption< typeof imageSequencePatternOption >; diff --git a/packages/docs/docs/cli/benchmark.mdx b/packages/docs/docs/cli/benchmark.mdx index 5ea0e292528..dcbaba37eee 100644 --- a/packages/docs/docs/cli/benchmark.mdx +++ b/packages/docs/docs/cli/benchmark.mdx @@ -21,7 +21,7 @@ If `composition-ids` is not passed, Remotion will let you select compositions fr ### `--runs` -Specify how many times video must be rendered. Default value is 3. + ### `--concurrencies` @@ -93,7 +93,7 @@ Inherited from [`npx remotion render`](/docs/cli/render#--disable-web-security) ### `--gl` -Inherited from [`npx remotion render`](/docs/cli/render#--gl) + ### `--chrome-mode` diff --git a/packages/renderer/src/options/index.tsx b/packages/renderer/src/options/index.tsx index 877b934942b..5e262b2aba5 100644 --- a/packages/renderer/src/options/index.tsx +++ b/packages/renderer/src/options/index.tsx @@ -62,6 +62,7 @@ import {publicDirOption} from './public-dir'; import {publicLicenseKeyOption} from './public-license-key'; import {publicPathOption} from './public-path'; import {reproOption} from './repro'; +import {runsOption} from './runs'; import {scaleOption} from './scale'; import {separateAudioOption} from './separate-audio'; import {stillFrameOption} from './still-frame'; @@ -107,6 +108,7 @@ export const allOptions = { beepOnFinishOption, numberOfGifLoopsOption, reproOption, + runsOption, pixelFormatOption, preferLosslessOption: preferLosslessAudioOption, proResProfileOption, diff --git a/packages/renderer/src/options/runs.tsx b/packages/renderer/src/options/runs.tsx new file mode 100644 index 00000000000..7118ba2ea38 --- /dev/null +++ b/packages/renderer/src/options/runs.tsx @@ -0,0 +1,47 @@ +import type {AnyRemotionOption} from './option'; + +const DEFAULT_RUNS = 3; + +let currentRuns: number = DEFAULT_RUNS; + +const cliFlag = 'runs' as const; + +export const runsOption = { + name: 'Benchmark runs', + cliFlag, + description: () => ( + <> + Specify how many times the video should be rendered during a benchmark. + Default {DEFAULT_RUNS}. + + ), + ssrName: null, + docLink: 'https://www.remotion.dev/docs/cli/benchmark#--runs', + type: DEFAULT_RUNS as number, + getValue: ({commandLine}) => { + if (commandLine[cliFlag] !== undefined) { + const value = Number(commandLine[cliFlag]); + if (isNaN(value) || value < 1) { + throw new Error( + `--runs must be a positive number, but got ${commandLine[cliFlag]}`, + ); + } + + return {value, source: 'cli'}; + } + + if (currentRuns !== DEFAULT_RUNS) { + return {value: currentRuns, source: 'config'}; + } + + return {value: DEFAULT_RUNS, source: 'default'}; + }, + setConfig: (value: number) => { + if (typeof value !== 'number' || isNaN(value) || value < 1) { + throw new Error(`Runs must be a positive number, but got ${value}`); + } + + currentRuns = value; + }, + id: cliFlag, +} satisfies AnyRemotionOption; From aeaf13277de660bfcd8cbcae4ab3adeee379e377 Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Thu, 19 Feb 2026 21:30:34 +0100 Subject: [PATCH 15/21] `@remotion/cli`: Add Config.setBenchmarkRuns() for runs option Co-Authored-By: Claude Opus 4.6 --- packages/cli/src/config/index.ts | 7 +++++++ packages/docs/docs/config.mdx | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/packages/cli/src/config/index.ts b/packages/cli/src/config/index.ts index 413aa72c484..22fdd2b0d9c 100644 --- a/packages/cli/src/config/index.ts +++ b/packages/cli/src/config/index.ts @@ -118,6 +118,7 @@ const { imageSequenceOption, bundleCacheOption, envFileOption, + runsOption, } = BrowserSafeApis.options; declare global { @@ -595,6 +596,11 @@ type FlatConfig = RemotionConfigObject & * Choose between using Chrome Headless Shell or Chrome for Testing */ setChromeMode: (chromeMode: ChromeMode) => void; + /** + * Set how many times the video should be rendered during a benchmark. + * Default: 3 + */ + setBenchmarkRuns: (runs: number) => void; /** * @deprecated 'The config format has changed. Change `Config.Bundling.*()` calls to `Config.*()` in your config file.' */ @@ -742,6 +748,7 @@ export const Config: FlatConfig = { setForceNewStudioEnabled: forceNewStudioOption.setConfig, setIPv4: ipv4Option.setConfig, setBundleOutDir: outDirOption.setConfig, + setBenchmarkRuns: runsOption.setConfig, }; export const ConfigInternals = { diff --git a/packages/docs/docs/config.mdx b/packages/docs/docs/config.mdx index d05d00ab59f..405c59e85df 100644 --- a/packages/docs/docs/config.mdx +++ b/packages/docs/docs/config.mdx @@ -99,6 +99,18 @@ Config.setBundleOutDir('./custom-build-dir'); The [command line flag](/docs/cli/bundle#--out-dir) `--out-dir` will take precedence over this option. +## `setBenchmarkRuns()` + + + +```ts twoslash title="remotion.config.ts" +import {Config} from '@remotion/cli/config'; +// ---cut--- +Config.setBenchmarkRuns(5); +``` + +The [command line flag](/docs/cli/benchmark#--runs) `--runs` will take precedence over this option. + ## `setEntryPoint()` Sets the Remotion [entry point](/docs/terminology/entry-point), you don't have to specify it for CLI commands. From e1f2997d0910d1b78ad691b4719a42b969a5ec83 Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Thu, 19 Feb 2026 21:34:57 +0100 Subject: [PATCH 16/21] okay --- packages/cli/src/parsed-cli.ts | 4 +--- packages/lambda/src/cli/args.ts | 3 --- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/cli/src/parsed-cli.ts b/packages/cli/src/parsed-cli.ts index a454c84f845..77ae5aa94c0 100644 --- a/packages/cli/src/parsed-cli.ts +++ b/packages/cli/src/parsed-cli.ts @@ -13,8 +13,6 @@ export const BooleanFlags = [ BrowserSafeApis.options.enforceAudioOption.cliFlag, // Lambda flags 'force', - 'disable-chunk-optimization', - 'save-browser-logs', 'disable-cloudwatch', 'enable-lambda-insights', 'yes', @@ -32,7 +30,7 @@ export const BooleanFlags = [ BrowserSafeApis.options.disableGitSourceOption.cliFlag, BrowserSafeApis.options.disallowParallelEncodingOption.cliFlag, BrowserSafeApis.options.forSeamlessAacConcatenationOption.cliFlag, - 'repro', + BrowserSafeApis.options.reproOption.cliFlag, 'compatible-only', 'force-path-style', 'onlyAllocateCpuDuringRequestProcessing', diff --git a/packages/lambda/src/cli/args.ts b/packages/lambda/src/cli/args.ts index 0fb709d9711..3853cf66e0a 100644 --- a/packages/lambda/src/cli/args.ts +++ b/packages/lambda/src/cli/args.ts @@ -17,10 +17,7 @@ type LambdaCommandLineOptions = { force: boolean; f: boolean; ['default-only']: boolean; - ['site-name']: string | undefined; - ['disable-chunk-optimization']: boolean; - ['save-browser-logs']: boolean; ['disable-cloudwatch']: boolean; [BrowserSafeApis.options.enableLambdaInsights.cliFlag]: boolean; ['max-retries']?: number; From c42974cf3818ee455589321a59cb069e06d060c0 Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Thu, 19 Feb 2026 21:40:06 +0100 Subject: [PATCH 17/21] `@remotion/cli`: Move lambda/cloudrun-only boolean flags to respective packages Co-Authored-By: Claude Opus 4.6 --- packages/cli/src/config/index.ts | 5 +- packages/cli/src/parse-command-line.ts | 165 ------------- packages/cli/src/parsed-cli.ts | 227 +++++++++++++++--- packages/cli/src/studio.ts | 6 +- packages/cloudrun/src/cli/args.ts | 10 +- packages/docs/docs/cli/studio.mdx | 2 +- packages/docs/docs/config.mdx | 4 +- packages/lambda/src/cli/args.ts | 14 +- packages/renderer/src/options/index.tsx | 2 + packages/renderer/src/options/no-open.tsx | 37 +++ .../studio-server/src/maybe-open-browser.ts | 50 +--- packages/studio-server/src/start-studio.ts | 12 +- 12 files changed, 275 insertions(+), 259 deletions(-) create mode 100644 packages/renderer/src/options/no-open.tsx diff --git a/packages/cli/src/config/index.ts b/packages/cli/src/config/index.ts index 22fdd2b0d9c..9075b66ed2b 100644 --- a/packages/cli/src/config/index.ts +++ b/packages/cli/src/config/index.ts @@ -44,7 +44,6 @@ import { } from './ffmpeg-override'; import {setFrameRange} from './frame-range'; import {getMetadata, setMetadata} from './metadata'; -import {getShouldOpenBrowser, setShouldOpenBrowser} from './open-browser'; import {setOutputLocation} from './output-location'; import type {WebpackOverrideFn} from './override-webpack'; import {overrideWebpackConfig} from './override-webpack'; @@ -119,6 +118,7 @@ const { bundleCacheOption, envFileOption, runsOption, + noOpenOption, } = BrowserSafeApis.options; declare global { @@ -664,7 +664,7 @@ export const Config: FlatConfig = { experimentalClientSideRenderingOption.setConfig, setNumberOfSharedAudioTags: numberOfSharedAudioTagsOption.setConfig, setWebpackPollingInMilliseconds: webpackPollOption.setConfig, - setShouldOpenBrowser, + setShouldOpenBrowser: noOpenOption.setConfig, setBufferStateDelayInMilliseconds, overrideWebpackConfig, setCachingEnabled: bundleCacheOption.setConfig, @@ -772,7 +772,6 @@ export const ConfigInternals = { getMetadata, getEntryPoint, getWebpackPolling, - getShouldOpenBrowser, getBufferStateDelayInMilliseconds, getOutputCodecOrUndefined: BrowserSafeApis.getOutputCodecOrUndefined, }; diff --git a/packages/cli/src/parse-command-line.ts b/packages/cli/src/parse-command-line.ts index 28806252a4c..4230b6693c3 100644 --- a/packages/cli/src/parse-command-line.ts +++ b/packages/cli/src/parse-command-line.ts @@ -1,171 +1,6 @@ -import type { - AudioCodec, - StillImageFormat, - VideoImageFormat, -} from '@remotion/renderer'; -import type {TypeOfOption} from '@remotion/renderer/client'; -import {BrowserSafeApis} from '@remotion/renderer/client'; import {Config, ConfigInternals} from './config'; import {parsedCli} from './parsed-cli'; -const { - beepOnFinishOption, - colorSpaceOption, - concurrencyOption, - disallowParallelEncodingOption, - offthreadVideoCacheSizeInBytesOption, - encodingBufferSizeOption, - encodingMaxRateOption, - deleteAfterOption, - folderExpiryOption, - enableMultiprocessOnLinuxOption, - numberOfGifLoopsOption, - x264Option, - enforceAudioOption, - jpegQualityOption, - audioBitrateOption, - videoBitrateOption, - audioCodecOption, - publicPathOption, - audioLatencyHintOption, - darkModeOption, - publicLicenseKeyOption, - forceNewStudioOption, - numberOfSharedAudioTagsOption, - ipv4Option, - pixelFormatOption, - browserExecutableOption, - everyNthFrameOption, - proResProfileOption, - userAgentOption, - disableWebSecurityOption, - ignoreCertificateErrorsOption, - overrideHeightOption, - overrideWidthOption, - overrideFpsOption, - overrideDurationOption, - outDirOption, - packageManagerOption, - webpackPollOption, - keyboardShortcutsOption, - experimentalClientSideRenderingOption, - imageSequencePatternOption, - scaleOption, - overwriteOption, - crfOption, - logLevelOption, - videoCodecOption, - stillFrameOption, - imageSequenceOption, - versionFlagOption, - bundleCacheOption, - envFileOption, - glOption, - runsOption, - reproOption, - mutedOption, -} = BrowserSafeApis.options; - -export type CommandLineOptions = { - [browserExecutableOption.cliFlag]: TypeOfOption< - typeof browserExecutableOption - >; - [pixelFormatOption.cliFlag]: TypeOfOption; - ['image-format']: VideoImageFormat | StillImageFormat; - [proResProfileOption.cliFlag]: TypeOfOption; - [x264Option.cliFlag]: TypeOfOption; - [bundleCacheOption.cliFlag]: TypeOfOption; - [envFileOption.cliFlag]: TypeOfOption; - [ignoreCertificateErrorsOption.cliFlag]: TypeOfOption< - typeof ignoreCertificateErrorsOption - >; - [darkModeOption.cliFlag]: TypeOfOption; - [disableWebSecurityOption.cliFlag]: TypeOfOption< - typeof disableWebSecurityOption - >; - [everyNthFrameOption.cliFlag]: TypeOfOption; - [numberOfGifLoopsOption.cliFlag]: TypeOfOption; - [numberOfSharedAudioTagsOption.cliFlag]: TypeOfOption< - typeof numberOfSharedAudioTagsOption - >; - [offthreadVideoCacheSizeInBytesOption.cliFlag]: TypeOfOption< - typeof offthreadVideoCacheSizeInBytesOption - >; - [colorSpaceOption.cliFlag]: TypeOfOption; - [disallowParallelEncodingOption.cliFlag]: TypeOfOption< - typeof disallowParallelEncodingOption - >; - [beepOnFinishOption.cliFlag]: TypeOfOption; - [versionFlagOption.cliFlag]: TypeOfOption; - [videoCodecOption.cliFlag]: TypeOfOption; - [concurrencyOption.cliFlag]: TypeOfOption; - timeout: number; - config: string; - ['public-dir']: string; - [audioBitrateOption.cliFlag]: TypeOfOption; - [videoBitrateOption.cliFlag]: TypeOfOption; - [encodingBufferSizeOption.cliFlag]: TypeOfOption< - typeof encodingBufferSizeOption - >; - [encodingMaxRateOption.cliFlag]: TypeOfOption; - [audioCodecOption.cliFlag]: AudioCodec; - [publicPathOption.cliFlag]: string; - [crfOption.cliFlag]: TypeOfOption; - force: boolean; - output: string | undefined; - [overwriteOption.cliFlag]: TypeOfOption; - png: boolean; - props: string; - quality: number; - [jpegQualityOption.cliFlag]: TypeOfOption; - frames: string | number; - [scaleOption.cliFlag]: TypeOfOption; - [imageSequenceOption.cliFlag]: TypeOfOption; - quiet: boolean; - q: boolean; - [logLevelOption.cliFlag]: TypeOfOption; - help: boolean; - port: number; - [stillFrameOption.cliFlag]: TypeOfOption; - ['disable-headless']: boolean; - [keyboardShortcutsOption.cliFlag]: TypeOfOption< - typeof keyboardShortcutsOption - >; - [experimentalClientSideRenderingOption.cliFlag]: TypeOfOption< - typeof experimentalClientSideRenderingOption - >; - [mutedOption.cliFlag]: TypeOfOption; - [overrideHeightOption.cliFlag]: TypeOfOption; - [overrideWidthOption.cliFlag]: TypeOfOption; - [overrideFpsOption.cliFlag]: TypeOfOption; - [overrideDurationOption.cliFlag]: TypeOfOption; - [runsOption.cliFlag]: TypeOfOption; - concurrencies: string; - [enforceAudioOption.cliFlag]: TypeOfOption; - [glOption.cliFlag]: TypeOfOption; - [packageManagerOption.cliFlag]: TypeOfOption; - [webpackPollOption.cliFlag]: TypeOfOption; - ['no-open']: boolean; - ['browser']: string; - ['browser-args']: string; - [userAgentOption.cliFlag]: TypeOfOption; - [outDirOption.cliFlag]: TypeOfOption; - [audioLatencyHintOption.cliFlag]: AudioContextLatencyCategory; - [ipv4Option.cliFlag]: TypeOfOption; - [deleteAfterOption.cliFlag]: TypeOfOption; - [folderExpiryOption.cliFlag]: TypeOfOption; - [enableMultiprocessOnLinuxOption.cliFlag]: TypeOfOption< - typeof enableMultiprocessOnLinuxOption - >; - [reproOption.cliFlag]: TypeOfOption; - [imageSequencePatternOption.cliFlag]: TypeOfOption< - typeof imageSequencePatternOption - >; - 'license-key': string; - [publicLicenseKeyOption.cliFlag]: string; - [forceNewStudioOption.cliFlag]: TypeOfOption; -}; - export const parseCommandLine = () => { if (parsedCli.frames) { ConfigInternals.setFrameRangeFromCli(parsedCli.frames); diff --git a/packages/cli/src/parsed-cli.ts b/packages/cli/src/parsed-cli.ts index 77ae5aa94c0..bb5722cf3b1 100644 --- a/packages/cli/src/parsed-cli.ts +++ b/packages/cli/src/parsed-cli.ts @@ -1,50 +1,207 @@ +import type { + AudioCodec, + StillImageFormat, + VideoImageFormat, +} from '@remotion/renderer'; +import type {TypeOfOption} from '@remotion/renderer/client'; import {BrowserSafeApis} from '@remotion/renderer/client'; import minimist from 'minimist'; -import type {CommandLineOptions} from './parse-command-line'; + +const { + beepOnFinishOption, + colorSpaceOption, + concurrencyOption, + disallowParallelEncodingOption, + offthreadVideoCacheSizeInBytesOption, + encodingBufferSizeOption, + encodingMaxRateOption, + deleteAfterOption, + folderExpiryOption, + enableMultiprocessOnLinuxOption, + numberOfGifLoopsOption, + x264Option, + enforceAudioOption, + jpegQualityOption, + audioBitrateOption, + videoBitrateOption, + audioCodecOption, + publicPathOption, + audioLatencyHintOption, + darkModeOption, + publicLicenseKeyOption, + forceNewStudioOption, + numberOfSharedAudioTagsOption, + ipv4Option, + pixelFormatOption, + browserExecutableOption, + everyNthFrameOption, + proResProfileOption, + userAgentOption, + disableWebSecurityOption, + ignoreCertificateErrorsOption, + overrideHeightOption, + overrideWidthOption, + overrideFpsOption, + overrideDurationOption, + outDirOption, + packageManagerOption, + webpackPollOption, + keyboardShortcutsOption, + experimentalClientSideRenderingOption, + imageSequencePatternOption, + scaleOption, + overwriteOption, + crfOption, + logLevelOption, + videoCodecOption, + stillFrameOption, + imageSequenceOption, + versionFlagOption, + bundleCacheOption, + envFileOption, + glOption, + runsOption, + reproOption, + mutedOption, + headlessOption, + disableGitSourceOption, + forSeamlessAacConcatenationOption, + isProductionOption, + noOpenOption, +} = BrowserSafeApis.options; + +export type CommandLineOptions = { + [browserExecutableOption.cliFlag]: TypeOfOption< + typeof browserExecutableOption + >; + [pixelFormatOption.cliFlag]: TypeOfOption; + ['image-format']: VideoImageFormat | StillImageFormat; + [proResProfileOption.cliFlag]: TypeOfOption; + [x264Option.cliFlag]: TypeOfOption; + [bundleCacheOption.cliFlag]: TypeOfOption; + [envFileOption.cliFlag]: TypeOfOption; + [ignoreCertificateErrorsOption.cliFlag]: TypeOfOption< + typeof ignoreCertificateErrorsOption + >; + [darkModeOption.cliFlag]: TypeOfOption; + [disableWebSecurityOption.cliFlag]: TypeOfOption< + typeof disableWebSecurityOption + >; + [everyNthFrameOption.cliFlag]: TypeOfOption; + [numberOfGifLoopsOption.cliFlag]: TypeOfOption; + [numberOfSharedAudioTagsOption.cliFlag]: TypeOfOption< + typeof numberOfSharedAudioTagsOption + >; + [offthreadVideoCacheSizeInBytesOption.cliFlag]: TypeOfOption< + typeof offthreadVideoCacheSizeInBytesOption + >; + [colorSpaceOption.cliFlag]: TypeOfOption; + [disallowParallelEncodingOption.cliFlag]: TypeOfOption< + typeof disallowParallelEncodingOption + >; + [beepOnFinishOption.cliFlag]: TypeOfOption; + [versionFlagOption.cliFlag]: TypeOfOption; + [videoCodecOption.cliFlag]: TypeOfOption; + [concurrencyOption.cliFlag]: TypeOfOption; + timeout: number; + config: string; + ['public-dir']: string; + [audioBitrateOption.cliFlag]: TypeOfOption; + [videoBitrateOption.cliFlag]: TypeOfOption; + [encodingBufferSizeOption.cliFlag]: TypeOfOption< + typeof encodingBufferSizeOption + >; + [encodingMaxRateOption.cliFlag]: TypeOfOption; + [audioCodecOption.cliFlag]: AudioCodec; + [publicPathOption.cliFlag]: string; + [crfOption.cliFlag]: TypeOfOption; + force: boolean; + output: string | undefined; + [overwriteOption.cliFlag]: TypeOfOption; + png: boolean; + props: string; + quality: number; + [jpegQualityOption.cliFlag]: TypeOfOption; + frames: string | number; + [scaleOption.cliFlag]: TypeOfOption; + [imageSequenceOption.cliFlag]: TypeOfOption; + quiet: boolean; + q: boolean; + [logLevelOption.cliFlag]: TypeOfOption; + help: boolean; + port: number; + [stillFrameOption.cliFlag]: TypeOfOption; + ['disable-headless']: boolean; + [keyboardShortcutsOption.cliFlag]: TypeOfOption< + typeof keyboardShortcutsOption + >; + [experimentalClientSideRenderingOption.cliFlag]: TypeOfOption< + typeof experimentalClientSideRenderingOption + >; + [mutedOption.cliFlag]: TypeOfOption; + [overrideHeightOption.cliFlag]: TypeOfOption; + [overrideWidthOption.cliFlag]: TypeOfOption; + [overrideFpsOption.cliFlag]: TypeOfOption; + [overrideDurationOption.cliFlag]: TypeOfOption; + [runsOption.cliFlag]: TypeOfOption; + concurrencies: string; + [enforceAudioOption.cliFlag]: TypeOfOption; + [glOption.cliFlag]: TypeOfOption; + [packageManagerOption.cliFlag]: TypeOfOption; + [webpackPollOption.cliFlag]: TypeOfOption; + [noOpenOption.cliFlag]: TypeOfOption; + ['browser']: string; + ['browser-args']: string; + [userAgentOption.cliFlag]: TypeOfOption; + [outDirOption.cliFlag]: TypeOfOption; + [audioLatencyHintOption.cliFlag]: AudioContextLatencyCategory; + [ipv4Option.cliFlag]: TypeOfOption; + [deleteAfterOption.cliFlag]: TypeOfOption; + [folderExpiryOption.cliFlag]: TypeOfOption; + [enableMultiprocessOnLinuxOption.cliFlag]: TypeOfOption< + typeof enableMultiprocessOnLinuxOption + >; + [reproOption.cliFlag]: TypeOfOption; + [imageSequencePatternOption.cliFlag]: TypeOfOption< + typeof imageSequencePatternOption + >; + 'license-key': string; + [publicLicenseKeyOption.cliFlag]: string; + [forceNewStudioOption.cliFlag]: TypeOfOption; +}; export const BooleanFlags = [ - BrowserSafeApis.options.overwriteOption.cliFlag, - 'force', - BrowserSafeApis.options.imageSequenceOption.cliFlag, + overwriteOption.cliFlag, + imageSequenceOption.cliFlag, 'help', 'quiet', 'q', - 'muted', - BrowserSafeApis.options.enforceAudioOption.cliFlag, - // Lambda flags - 'force', - 'disable-cloudwatch', - 'enable-lambda-insights', - 'yes', - 'y', - BrowserSafeApis.options.disableWebSecurityOption.cliFlag, - BrowserSafeApis.options.darkModeOption.cliFlag, - BrowserSafeApis.options.ignoreCertificateErrorsOption.cliFlag, - 'disable-headless', - BrowserSafeApis.options.keyboardShortcutsOption.cliFlag, - BrowserSafeApis.options.experimentalClientSideRenderingOption.cliFlag, - 'default-only', - 'no-open', - BrowserSafeApis.options.ipv4Option.cliFlag, - BrowserSafeApis.options.beepOnFinishOption.cliFlag, - BrowserSafeApis.options.disableGitSourceOption.cliFlag, - BrowserSafeApis.options.disallowParallelEncodingOption.cliFlag, - BrowserSafeApis.options.forSeamlessAacConcatenationOption.cliFlag, - BrowserSafeApis.options.reproOption.cliFlag, - 'compatible-only', - 'force-path-style', - 'onlyAllocateCpuDuringRequestProcessing', - BrowserSafeApis.options.isProductionOption.cliFlag, - BrowserSafeApis.options.forceNewStudioOption.cliFlag, - BrowserSafeApis.options.bundleCacheOption.cliFlag, + mutedOption.cliFlag, + enforceAudioOption.cliFlag, + disableWebSecurityOption.cliFlag, + darkModeOption.cliFlag, + ignoreCertificateErrorsOption.cliFlag, + headlessOption.cliFlag, + keyboardShortcutsOption.cliFlag, + experimentalClientSideRenderingOption.cliFlag, + ipv4Option.cliFlag, + beepOnFinishOption.cliFlag, + disableGitSourceOption.cliFlag, + disallowParallelEncodingOption.cliFlag, + forSeamlessAacConcatenationOption.cliFlag, + reproOption.cliFlag, + isProductionOption.cliFlag, + forceNewStudioOption.cliFlag, + bundleCacheOption.cliFlag, ]; export const parsedCli = minimist(process.argv.slice(2), { boolean: BooleanFlags, default: { - overwrite: true, - [BrowserSafeApis.options.bundleCacheOption.cliFlag]: null, - muted: null, + [overwriteOption.cliFlag]: true, + [bundleCacheOption.cliFlag]: null, + [mutedOption.cliFlag]: null, + [noOpenOption.cliFlag]: null, }, }) as CommandLineOptions & { _: string[]; diff --git a/packages/cli/src/studio.ts b/packages/cli/src/studio.ts index e7531549cd4..69d2b337695 100644 --- a/packages/cli/src/studio.ts +++ b/packages/cli/src/studio.ts @@ -43,6 +43,7 @@ const { audioLatencyHintOption, ipv4Option, webpackPollOption, + noOpenOption, } = BrowserSafeApis.options; export const studioCommand = async ( @@ -148,7 +149,7 @@ export const studioCommand = async ( browserArgs: parsedCli['browser-args'], browserFlag: parsedCli.browser, logLevel, - configValueShouldOpenBrowser: ConfigInternals.getShouldOpenBrowser(), + shouldOpenBrowser: !noOpenOption.getValue({commandLine: parsedCli}).value, fullEntryPath, getCurrentInputProps: () => inputProps, getEnvVariables: () => envVariables, @@ -170,9 +171,6 @@ export const studioCommand = async ( cancelJob, removeJob, }, - // Minimist quirk: Adding `--no-open` flag will result in {['no-open']: false, open: true} - // @ts-expect-error - parsedCliOpen: parsedCli.open, gitSource, bufferStateDelayInMilliseconds: ConfigInternals.getBufferStateDelayInMilliseconds(), diff --git a/packages/cloudrun/src/cli/args.ts b/packages/cloudrun/src/cli/args.ts index c7a425ca809..7e81841f31f 100644 --- a/packages/cloudrun/src/cli/args.ts +++ b/packages/cloudrun/src/cli/args.ts @@ -3,6 +3,14 @@ import {CliInternals} from '@remotion/cli'; import type {Privacy} from '../defaults'; import type {GcpRegion} from '../pricing/gcp-regions'; +const CloudrunBooleanFlags = [ + ...CliInternals.BooleanFlags, + 'force', + 'yes', + 'y', + 'onlyAllocateCpuDuringRequestProcessing', +]; + type servicesCommandLineOptions = { help: boolean; region: GcpRegion; @@ -24,7 +32,7 @@ type servicesCommandLineOptions = { export const parsedCloudrunCli = CliInternals.minimist(process.argv.slice(2), { - boolean: CliInternals.BooleanFlags, + boolean: CloudrunBooleanFlags, string: ['_'], }); diff --git a/packages/docs/docs/cli/studio.mdx b/packages/docs/docs/cli/studio.mdx index a6facfd0fc6..82a0fc0dd81 100644 --- a/packages/docs/docs/cli/studio.mdx +++ b/packages/docs/docs/cli/studio.mdx @@ -64,7 +64,7 @@ Specify a location for the Remotion config file. ### `--no-open` -[Prevents Remotion from trying to open a browser](/docs/config#setshouldopenbrowser). This is useful if you use a different browser for Remotion than the operating system default. + ### `--browser` diff --git a/packages/docs/docs/config.mdx b/packages/docs/docs/config.mdx index 405c59e85df..00dc8cc5bcd 100644 --- a/packages/docs/docs/config.mdx +++ b/packages/docs/docs/config.mdx @@ -202,7 +202,7 @@ The [command line flag](/docs/cli/studio#--number-of-shared-audio-tags) `--numbe ## `setShouldOpenBrowser()` -Whether Remotion should open a browser when starting the Studio. Default `true`. + ```ts twoslash title="remotion.config.ts" import {Config} from '@remotion/cli/config'; @@ -211,6 +211,8 @@ import {Config} from '@remotion/cli/config'; Config.setShouldOpenBrowser(false); ``` +The [command line flag](/docs/cli/studio#--no-open) `--no-open` will take precedence over this option. + ## `setBrowserExecutable()` diff --git a/packages/lambda/src/cli/args.ts b/packages/lambda/src/cli/args.ts index 3853cf66e0a..84bb0115b2f 100644 --- a/packages/lambda/src/cli/args.ts +++ b/packages/lambda/src/cli/args.ts @@ -5,6 +5,18 @@ import type {AwsRegion, DeleteAfter, RuntimePreference} from '../client'; import {StorageClass} from '@aws-sdk/client-s3'; import type {Privacy} from '@remotion/serverless'; +const LambdaBooleanFlags = [ + ...CliInternals.BooleanFlags, + 'force', + 'disable-cloudwatch', + 'enable-lambda-insights', + 'yes', + 'y', + 'default-only', + 'compatible-only', + 'force-path-style', +]; + type LambdaCommandLineOptions = { help: boolean; region: AwsRegion; @@ -46,7 +58,7 @@ type LambdaCommandLineOptions = { export const parsedLambdaCli = CliInternals.minimist( process.argv.slice(2), { - boolean: CliInternals.BooleanFlags, + boolean: LambdaBooleanFlags, string: ['_'], }, ); diff --git a/packages/renderer/src/options/index.tsx b/packages/renderer/src/options/index.tsx index 5e262b2aba5..4697db766ca 100644 --- a/packages/renderer/src/options/index.tsx +++ b/packages/renderer/src/options/index.tsx @@ -42,6 +42,7 @@ import {licenseKeyOption} from './license-key'; import {logLevelOption} from './log-level'; import {metadataOption} from './metadata'; import {mutedOption} from './mute'; +import {noOpenOption} from './no-open'; import {numberOfGifLoopsOption} from './number-of-gif-loops'; import {numberOfSharedAudioTagsOption} from './number-of-shared-audio-tags'; import {offthreadVideoCacheSizeInBytesOption} from './offthreadvideo-cache-size'; @@ -109,6 +110,7 @@ export const allOptions = { numberOfGifLoopsOption, reproOption, runsOption, + noOpenOption, pixelFormatOption, preferLosslessOption: preferLosslessAudioOption, proResProfileOption, diff --git a/packages/renderer/src/options/no-open.tsx b/packages/renderer/src/options/no-open.tsx new file mode 100644 index 00000000000..a3bdc4a4131 --- /dev/null +++ b/packages/renderer/src/options/no-open.tsx @@ -0,0 +1,37 @@ +import type {AnyRemotionOption} from './option'; + +let shouldOpenBrowser = true; + +const cliFlag = 'no-open' as const; + +export const noOpenOption = { + name: 'Disable browser auto-open', + cliFlag, + description: () => ( + <> + If specified, Remotion will not open a browser window when starting the + Studio. + + ), + ssrName: null, + docLink: 'https://www.remotion.dev/docs/cli/studio#--no-open', + type: false as boolean, + getValue: ({commandLine}) => { + // Minimist quirk: `--no-open` sets `open` to `false`. + // When no flag is passed, `open` is `undefined`. + const cliValue = (commandLine as Record).open; + if (cliValue === false) { + return {value: true, source: 'cli'}; + } + + if (!shouldOpenBrowser) { + return {value: true, source: 'config'}; + } + + return {value: false, source: 'default'}; + }, + setConfig: (shouldOpen: boolean) => { + shouldOpenBrowser = shouldOpen; + }, + id: cliFlag, +} satisfies AnyRemotionOption; diff --git a/packages/studio-server/src/maybe-open-browser.ts b/packages/studio-server/src/maybe-open-browser.ts index e01299c875a..77b92222575 100644 --- a/packages/studio-server/src/maybe-open-browser.ts +++ b/packages/studio-server/src/maybe-open-browser.ts @@ -2,56 +2,26 @@ import type {LogLevel} from '@remotion/renderer'; import {RenderInternals} from '@remotion/renderer'; import {openBrowser} from './better-opn'; -const getShouldOpenBrowser = ({ - configValueShouldOpenBrowser, - parsedCliOpen, -}: { - configValueShouldOpenBrowser: boolean; - parsedCliOpen: boolean; -}): { - shouldOpenBrowser: boolean; - reasonForBrowserDecision: string; -} => { - if (parsedCliOpen === false) { - return { - shouldOpenBrowser: false, - reasonForBrowserDecision: '--no-open specified', - }; - } - - if ((process.env.BROWSER ?? '').toLowerCase() === 'none') { - return { - shouldOpenBrowser: false, - reasonForBrowserDecision: 'env BROWSER=none was set', - }; - } - - if (configValueShouldOpenBrowser === false) { - return {shouldOpenBrowser: false, reasonForBrowserDecision: 'Config file'}; - } - - return {shouldOpenBrowser: true, reasonForBrowserDecision: 'default'}; -}; - export const maybeOpenBrowser = async ({ browserArgs, browserFlag, - configValueShouldOpenBrowser, - parsedCliOpen, + shouldOpenBrowser, url, logLevel, }: { browserArgs: string; browserFlag: string; - configValueShouldOpenBrowser: boolean; - parsedCliOpen: boolean; + shouldOpenBrowser: boolean; url: string; logLevel: LogLevel; }) => { - const {reasonForBrowserDecision, shouldOpenBrowser} = getShouldOpenBrowser({ - configValueShouldOpenBrowser, - parsedCliOpen, - }); + if ((process.env.BROWSER ?? '').toLowerCase() === 'none') { + RenderInternals.Log.verbose( + {indent: false, logLevel}, + 'Not opening browser, reason: env BROWSER=none was set', + ); + return {didOpenBrowser: false}; + } if (shouldOpenBrowser) { await openBrowser({ @@ -62,7 +32,7 @@ export const maybeOpenBrowser = async ({ } else { RenderInternals.Log.verbose( {indent: false, logLevel}, - `Not opening browser, reason: ${reasonForBrowserDecision}`, + 'Not opening browser, reason: --no-open specified or config file', ); } diff --git a/packages/studio-server/src/start-studio.ts b/packages/studio-server/src/start-studio.ts index db65d833bca..eddf9e95642 100644 --- a/packages/studio-server/src/start-studio.ts +++ b/packages/studio-server/src/start-studio.ts @@ -28,7 +28,7 @@ export type StartStudioResult = {type: 'restarted'} | {type: 'already-running'}; export const startStudio = async ({ browserArgs, browserFlag, - configValueShouldOpenBrowser, + shouldOpenBrowser, fullEntryPath, logLevel, getCurrentInputProps, @@ -45,7 +45,6 @@ export const startStudio = async ({ getRenderQueue, numberOfAudioTags, queueMethods, - parsedCliOpen, previewEntry, gitSource, bufferStateDelayInMilliseconds, @@ -59,7 +58,7 @@ export const startStudio = async ({ browserArgs: string; browserFlag: string; logLevel: LogLevel; - configValueShouldOpenBrowser: boolean; + shouldOpenBrowser: boolean; fullEntryPath: string; getCurrentInputProps: () => object; getEnvVariables: () => Record; @@ -78,7 +77,6 @@ export const startStudio = async ({ audioLatencyHint: AudioContextLatencyCategory | null; enableCrossSiteIsolation: boolean; queueMethods: QueueMethods; - parsedCliOpen: boolean; previewEntry: string; gitSource: GitSource | null; binariesDirectory: string | null; @@ -170,8 +168,7 @@ export const startStudio = async ({ const res = await maybeOpenBrowser({ browserArgs, browserFlag, - configValueShouldOpenBrowser, - parsedCliOpen, + shouldOpenBrowser, url: `http://localhost:${result.port}`, logLevel, }); @@ -207,8 +204,7 @@ export const startStudio = async ({ await maybeOpenBrowser({ browserArgs, browserFlag, - configValueShouldOpenBrowser, - parsedCliOpen, + shouldOpenBrowser, url: `http://localhost:${port}`, logLevel, }); From 00467247a4348a59e9a84cc4d9cbec12eb9bef5b Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Thu, 19 Feb 2026 22:12:23 +0100 Subject: [PATCH 18/21] prettier --- packages/cloudrun/src/functions/render-media-single-thread.ts | 3 ++- packages/cloudrun/src/functions/render-still-single-thread.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/cloudrun/src/functions/render-media-single-thread.ts b/packages/cloudrun/src/functions/render-media-single-thread.ts index 42538dee64f..c1a7ae62eed 100644 --- a/packages/cloudrun/src/functions/render-media-single-thread.ts +++ b/packages/cloudrun/src/functions/render-media-single-thread.ts @@ -140,7 +140,8 @@ export const renderMediaSingleThread = async ( height: body.forceHeight ?? composition.height, width: body.forceWidth ?? composition.width, fps: body.forceFps ?? composition.fps, - durationInFrames: body.forceDurationInFrames ?? composition.durationInFrames, + durationInFrames: + body.forceDurationInFrames ?? composition.durationInFrames, }, serveUrl: body.serveUrl, codec: body.codec, diff --git a/packages/cloudrun/src/functions/render-still-single-thread.ts b/packages/cloudrun/src/functions/render-still-single-thread.ts index c84864e7903..17e03bff306 100644 --- a/packages/cloudrun/src/functions/render-still-single-thread.ts +++ b/packages/cloudrun/src/functions/render-still-single-thread.ts @@ -72,7 +72,8 @@ export const renderStillSingleThread = async ( height: body.forceHeight ?? composition.height, width: body.forceWidth ?? composition.width, fps: body.forceFps ?? composition.fps, - durationInFrames: body.forceDurationInFrames ?? composition.durationInFrames, + durationInFrames: + body.forceDurationInFrames ?? composition.durationInFrames, }, serveUrl: body.serveUrl, output: tempFilePath, From 52f13db572a9145f40008c7dde09cd523ede7d21 Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Thu, 19 Feb 2026 22:20:31 +0100 Subject: [PATCH 19/21] fix it up --- packages/cli/src/config/open-browser.ts | 15 --------------- packages/cli/src/parsed-cli.ts | 1 - 2 files changed, 16 deletions(-) delete mode 100644 packages/cli/src/config/open-browser.ts diff --git a/packages/cli/src/config/open-browser.ts b/packages/cli/src/config/open-browser.ts deleted file mode 100644 index 502da2cf28e..00000000000 --- a/packages/cli/src/config/open-browser.ts +++ /dev/null @@ -1,15 +0,0 @@ -let should = true; - -export const setShouldOpenBrowser = (_should: boolean) => { - if (typeof _should !== 'boolean') { - throw new TypeError( - `Expected a boolean, got ${typeof _should} (${should})`, - ); - } - - should = _should; -}; - -export const getShouldOpenBrowser = () => { - return should; -}; diff --git a/packages/cli/src/parsed-cli.ts b/packages/cli/src/parsed-cli.ts index bb5722cf3b1..d8d3cf3137d 100644 --- a/packages/cli/src/parsed-cli.ts +++ b/packages/cli/src/parsed-cli.ts @@ -201,7 +201,6 @@ export const parsedCli = minimist(process.argv.slice(2), { [overwriteOption.cliFlag]: true, [bundleCacheOption.cliFlag]: null, [mutedOption.cliFlag]: null, - [noOpenOption.cliFlag]: null, }, }) as CommandLineOptions & { _: string[]; From 82213753d1d8ebd6e197d678058fde31c67bdb94 Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Thu, 19 Feb 2026 22:22:09 +0100 Subject: [PATCH 20/21] Update parsed-cli.ts --- packages/cli/src/parsed-cli.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/parsed-cli.ts b/packages/cli/src/parsed-cli.ts index d8d3cf3137d..c78f8a1f1ae 100644 --- a/packages/cli/src/parsed-cli.ts +++ b/packages/cli/src/parsed-cli.ts @@ -131,7 +131,7 @@ export type CommandLineOptions = { help: boolean; port: number; [stillFrameOption.cliFlag]: TypeOfOption; - ['disable-headless']: boolean; + [headlessOption.cliFlag]: TypeOfOption; [keyboardShortcutsOption.cliFlag]: TypeOfOption< typeof keyboardShortcutsOption >; From a583c0bd514eb1ff5e0424a68ff67022f075de7c Mon Sep 17 00:00:00 2001 From: JonnyBurger Date: Thu, 19 Feb 2026 22:23:52 +0100 Subject: [PATCH 21/21] Move `setImageSequencePattern` section before `See also` in config.mdx Co-Authored-By: Claude Opus 4.6 --- packages/docs/docs/config.mdx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/docs/docs/config.mdx b/packages/docs/docs/config.mdx index 00dc8cc5bcd..ce493f274cf 100644 --- a/packages/docs/docs/config.mdx +++ b/packages/docs/docs/config.mdx @@ -1094,6 +1094,18 @@ Config.overrideWebpackConfig(async (currentConfiguration) => { }); ``` +### `setImageSequencePattern()` + + + +```ts twoslash title="remotion.config.ts" +import {Config} from '@remotion/cli/config'; +// ---cut--- +Config.setImageSequencePattern('frame_[frame]_custom.[ext]'); +``` + +The [command line flag](/docs/cli/render#--image-sequence-pattern) `--image-sequence-pattern` will take precedence over this option. + ## Old config file format In v3.3.39, a new config file format was introduced which flattens the options so they can more easily be discovered using TypeScript autocompletion. @@ -1117,15 +1129,3 @@ The old way is deprecated, but will work for the foreseeable future. ## See also - [Encoding guide](/docs/encoding) - -### `setImageSequencePattern()` - - - -```ts twoslash title="remotion.config.ts" -import {Config} from '@remotion/cli/config'; -// ---cut--- -Config.setImageSequencePattern('frame_[frame]_custom.[ext]'); -``` - -The [command line flag](/docs/cli/render#--image-sequence-pattern) `--image-sequence-pattern` will take precedence over this option.