diff --git a/packages/cli/src/benchmark.ts b/packages/cli/src/benchmark.ts index 1cb4c77a617..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, @@ -67,6 +65,13 @@ const { userAgentOption, disableWebSecurityOption, ignoreCertificateErrorsOption, + concurrencyOption, + overrideHeightOption, + overrideWidthOption, + overrideFpsOption, + overrideDurationOption, + bundleCacheOption, + runsOption, } = BrowserSafeApis.options; const getValidConcurrency = (cliConcurrency: number | string | null) => { @@ -182,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, @@ -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; @@ -266,6 +280,9 @@ export const benchmarkCommand = async ( const keyboardShortcutsEnabled = keyboardShortcutsOption.getValue({ commandLine: parsedCli, }).value; + const shouldCache = bundleCacheOption.getValue({ + commandLine: parsedCli, + }).value; if (experimentalClientSideRenderingEnabled) { Log.warn( @@ -338,6 +355,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 71b99ea3a48..ff0e56b4a6b 100644 --- a/packages/cli/src/bundle.ts +++ b/packages/cli/src/bundle.ts @@ -21,6 +21,8 @@ const { askAIOption, experimentalClientSideRenderingOption, keyboardShortcutsOption, + outDirOption, + bundleCacheOption, } = BrowserSafeApis.options; export const bundleCommand = async ( @@ -73,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( @@ -90,8 +95,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( @@ -160,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/config/index.ts b/packages/cli/src/config/index.ts index 2256a33a51b..9075b66ed2b 100644 --- a/packages/cli/src/config/index.ts +++ b/packages/cli/src/config/index.ts @@ -38,24 +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, - setWebpackPollingInMilliseconds, -} from './webpack-poll'; +import {getWebpackPolling} from './webpack-poll'; export type {Concurrency, WebpackConfiguration, WebpackOverrideFn}; @@ -119,6 +112,13 @@ const { overrideWidthOption, overrideFpsOption, overrideDurationOption, + outDirOption, + webpackPollOption, + imageSequenceOption, + bundleCacheOption, + envFileOption, + runsOption, + noOpenOption, } = BrowserSafeApis.options; declare global { @@ -587,10 +587,20 @@ type FlatConfig = RemotionConfigObject & * Default: false */ setIPv4: (ipv4: boolean) => void; + /** + * Define the output directory for `npx remotion bundle`. + * Default: `build` in the Remotion root. + */ + setBundleOutDir: (outDir: string | null) => void; /** * 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.' */ @@ -653,11 +663,11 @@ export const Config: FlatConfig = { setExperimentalClientSideRenderingEnabled: experimentalClientSideRenderingOption.setConfig, setNumberOfSharedAudioTags: numberOfSharedAudioTagsOption.setConfig, - setWebpackPollingInMilliseconds, - setShouldOpenBrowser, + setWebpackPollingInMilliseconds: webpackPollOption.setConfig, + setShouldOpenBrowser: noOpenOption.setConfig, setBufferStateDelayInMilliseconds, overrideWebpackConfig, - setCachingEnabled: setWebpackCaching, + setCachingEnabled: bundleCacheOption.setConfig, setPort, setStudioPort, setRendererPort, @@ -673,7 +683,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, @@ -705,7 +715,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, @@ -718,9 +728,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, @@ -738,6 +747,8 @@ export const Config: FlatConfig = { setPublicLicenseKey: publicLicenseKeyOption.setConfig, setForceNewStudioEnabled: forceNewStudioOption.setConfig, setIPv4: ipv4Option.setConfig, + setBundleOutDir: outDirOption.setConfig, + setBenchmarkRuns: runsOption.setConfig, }; export const ConfigInternals = { @@ -761,7 +772,6 @@ export const ConfigInternals = { getMetadata, getEntryPoint, getWebpackPolling, - getShouldOpenBrowser, getBufferStateDelayInMilliseconds, getOutputCodecOrUndefined: BrowserSafeApis.getOutputCodecOrUndefined, }; 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/get-cli-options.ts b/packages/cli/src/get-cli-options.ts index 4109df43438..b1f606009d4 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'; @@ -56,36 +55,14 @@ export const getCliOptions = (options: { }) => { const frameRange = getAndValidateFrameRange(options.logLevel, false); - const shouldOutputImageSequence = options.isStill - ? true - : ConfigInternals.getShouldOutputImageSequence(frameRange); - - const concurrency = BrowserSafeApis.options.concurrencyOption.getValue({ + const imageSequence = BrowserSafeApis.options.imageSequenceOption.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, - }); + const shouldOutputImageSequence = options.isStill + ? true + : imageSequence || typeof frameRange === 'number'; return { - concurrency, frameRange, shouldOutputImageSequence, inputProps: getInputProps(null, options.logLevel), @@ -94,11 +71,10 @@ 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, - fps, - durationInFrames, }; }; diff --git a/packages/cli/src/get-env.ts b/packages/cli/src/get-env.ts index 23474036bb8..42464d3ad89 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,37 +143,21 @@ export const getEnvironmentVariables = ( ): Record => { const processEnv = getProcessEnv(); - if (parsedCli['env-file']) { - const envFile = path.resolve(process.cwd(), parsedCli['env-file']); - 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 {value: envFileValue, source: envFileSource} = envFileOption.getValue({ + commandLine: parsedCli, + }); const remotionRoot = RenderInternals.findRemotionRoot(); - const configFileSetting = ConfigInternals.getDotEnvLocation(); - 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}, diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 3ed5e42cd84..01464651d09 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, versionFlagOption} = 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,8 +118,10 @@ export const cli = async () => { } else if (command === 'upgrade') { await upgradeCommand({ remotionRoot, - packageManager: parsedCli['package-manager'], - version: parsedCli.version, + packageManager: packageManager ?? undefined, + version: + versionFlagOption.getValue({commandLine: parsedCli}).value ?? + undefined, logLevel, args, }); @@ -130,7 +139,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..4230b6693c3 100644 --- a/packages/cli/src/parse-command-line.ts +++ b/packages/cli/src/parse-command-line.ts @@ -1,178 +1,21 @@ -import type { - AudioCodec, - Codec, - OpenGlRenderer, - 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, -} = BrowserSafeApis.options; - -export type CommandLineOptions = { - [browserExecutableOption.cliFlag]: TypeOfOption< - typeof browserExecutableOption - >; - [pixelFormatOption.cliFlag]: TypeOfOption; - ['image-format']: VideoImageFormat | StillImageFormat; - [proResProfileOption.cliFlag]: TypeOfOption; - [x264Option.cliFlag]: TypeOfOption; - ['bundle-cache']: string; - ['env-file']: string; - [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; - version: string; - codec: Codec; - [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; - crf: number; - force: boolean; - output: string | undefined; - overwrite: boolean; - png: boolean; - props: string; - quality: number; - [jpegQualityOption.cliFlag]: TypeOfOption; - frames: string | number; - scale: number; - sequence: boolean; - quiet: boolean; - q: boolean; - log: string; - help: boolean; - port: number; - frame: string | number; - ['disable-headless']: boolean; - ['disable-keyboard-shortcuts']: boolean; - ['enable-experimental-client-side-rendering']: boolean; - muted: boolean; - [overrideHeightOption.cliFlag]: TypeOfOption; - [overrideWidthOption.cliFlag]: TypeOfOption; - [overrideFpsOption.cliFlag]: TypeOfOption; - [overrideDurationOption.cliFlag]: TypeOfOption; - runs: number; - concurrencies: string; - [enforceAudioOption.cliFlag]: TypeOfOption; - gl: OpenGlRenderer; - ['package-manager']: string; - ['webpack-poll']: number; - ['no-open']: boolean; - ['browser']: string; - ['browser-args']: string; - [userAgentOption.cliFlag]: TypeOfOption; - ['out-dir']: string; - [audioLatencyHintOption.cliFlag]: AudioContextLatencyCategory; - [ipv4Option.cliFlag]: TypeOfOption; - [deleteAfterOption.cliFlag]: TypeOfOption; - [folderExpiryOption.cliFlag]: TypeOfOption; - [enableMultiprocessOnLinuxOption.cliFlag]: TypeOfOption< - typeof enableMultiprocessOnLinuxOption - >; - repro: boolean; - 'image-sequence-pattern': string; - 'license-key': string; - [publicLicenseKeyOption.cliFlag]: string; - [forceNewStudioOption.cliFlag]: TypeOfOption; -}; - 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_') ) { 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..c78f8a1f1ae 100644 --- a/packages/cli/src/parsed-cli.ts +++ b/packages/cli/src/parsed-cli.ts @@ -1,49 +1,206 @@ +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; + [headlessOption.cliFlag]: TypeOfOption; + [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 = [ - 'overwrite', - 'force', - 'sequence', + overwriteOption.cliFlag, + imageSequenceOption.cliFlag, 'help', 'quiet', 'q', - 'muted', - BrowserSafeApis.options.enforceAudioOption.cliFlag, - // Lambda flags - 'force', - 'disable-chunk-optimization', - 'save-browser-logs', - 'disable-cloudwatch', - 'enable-lambda-insights', - 'yes', - 'y', - BrowserSafeApis.options.disableWebSecurityOption.cliFlag, - BrowserSafeApis.options.darkModeOption.cliFlag, - BrowserSafeApis.options.ignoreCertificateErrorsOption.cliFlag, - 'disable-headless', - 'disable-keyboard-shortcuts', - 'default-only', - 'no-open', - BrowserSafeApis.options.ipv4Option.cliFlag, - BrowserSafeApis.options.beepOnFinishOption.cliFlag, - BrowserSafeApis.options.disableGitSourceOption.cliFlag, - BrowserSafeApis.options.disallowParallelEncodingOption.cliFlag, - BrowserSafeApis.options.forSeamlessAacConcatenationOption.cliFlag, - 'repro', - 'compatible-only', - 'force-path-style', - 'onlyAllocateCpuDuringRequestProcessing', - BrowserSafeApis.options.isProductionOption.cliFlag, - BrowserSafeApis.options.forceNewStudioOption.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, - muted: null, + [overwriteOption.cliFlag]: true, + [bundleCacheOption.cliFlag]: null, + [mutedOption.cliFlag]: null, }, }) as CommandLineOptions & { _: string[]; diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts index 2aca245daf2..995f2a80440 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,7 +195,14 @@ export const renderVideoFlow = async ({ askAIEnabled: boolean; experimentalClientSideRenderingEnabled: boolean; 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; @@ -337,6 +345,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 940cccd5881..bcdee69c71e 100644 --- a/packages/cli/src/render.tsx +++ b/packages/cli/src/render.tsx @@ -55,6 +55,12 @@ const { userAgentOption, disableWebSecurityOption, ignoreCertificateErrorsOption, + concurrencyOption, + overrideHeightOption, + overrideWidthOption, + overrideFpsOption, + overrideDurationOption, + bundleCacheOption, } = BrowserSafeApis.options; export const render = async ( @@ -95,15 +101,10 @@ export const render = async ( } const { - concurrency, frameRange, shouldOutputImageSequence, inputProps, envVariables, - height, - width, - fps, - durationInFrames, ffmpegOverride, } = getCliOptions({ isStill: false, @@ -111,6 +112,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; @@ -232,6 +247,9 @@ export const render = async ( const mediaCacheSizeInBytes = mediaCacheSizeInBytesOption.getValue({ commandLine: parsedCli, }).value; + const shouldCache = bundleCacheOption.getValue({ + commandLine: parsedCli, + }).value; await renderVideoFlow({ fullEntryPoint, @@ -307,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 781319f92d3..517758fe312 100644 --- a/packages/cli/src/setup-cache.ts +++ b/packages/cli/src/setup-cache.ts @@ -35,6 +35,7 @@ export const bundleOnCliOrTakeServeUrl = async ({ experimentalClientSideRenderingEnabled, askAIEnabled, keyboardShortcutsEnabled, + shouldCache, }: { fullPath: string; remotionRoot: string; @@ -57,6 +58,7 @@ export const bundleOnCliOrTakeServeUrl = async ({ experimentalClientSideRenderingEnabled: boolean; askAIEnabled: boolean; keyboardShortcutsEnabled: boolean; + shouldCache: boolean; }): Promise<{ urlOrBundle: string; cleanup: () => void; @@ -100,6 +102,7 @@ export const bundleOnCliOrTakeServeUrl = async ({ experimentalClientSideRenderingEnabled, askAIEnabled, keyboardShortcutsEnabled, + shouldCache, }); return { @@ -127,6 +130,7 @@ export const bundleOnCli = async ({ experimentalClientSideRenderingEnabled, askAIEnabled, keyboardShortcutsEnabled, + shouldCache, }: { fullPath: string; remotionRoot: string; @@ -149,9 +153,8 @@ export const bundleOnCli = async ({ experimentalClientSideRenderingEnabled: boolean; keyboardShortcutsEnabled: boolean; askAIEnabled: boolean; + shouldCache: boolean; }) => { - const shouldCache = ConfigInternals.getWebpackCaching(); - const symlinkState: SymbolicLinksState = { symlinks: [], }; diff --git a/packages/cli/src/still.ts b/packages/cli/src/still.ts index a6669ff9334..3caf73681c8 100644 --- a/packages/cli/src/still.ts +++ b/packages/cli/src/still.ts @@ -34,6 +34,11 @@ const { userAgentOption, disableWebSecurityOption, ignoreCertificateErrorsOption, + overrideHeightOption, + overrideWidthOption, + overrideFpsOption, + overrideDurationOption, + bundleCacheOption, } = BrowserSafeApis.options; export const still = async ( @@ -73,20 +78,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; @@ -147,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, @@ -206,5 +217,6 @@ export const still = async ( experimentalClientSideRenderingOption.getValue({commandLine: parsedCli}) .value, keyboardShortcutsEnabled, + shouldCache, }); }; diff --git a/packages/cli/src/studio.ts b/packages/cli/src/studio.ts index 1bf1f5aa377..69d2b337695 100644 --- a/packages/cli/src/studio.ts +++ b/packages/cli/src/studio.ts @@ -42,6 +42,8 @@ const { numberOfSharedAudioTagsOption, audioLatencyHintOption, ipv4Option, + webpackPollOption, + noOpenOption, } = BrowserSafeApis.options; export const studioCommand = async ( @@ -147,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, @@ -158,7 +160,7 @@ export const studioCommand = async ( remotionRoot, relativePublicDir, webpackOverride: ConfigInternals.getWebpackOverrideFn(), - poll: ConfigInternals.getWebpackPolling(), + poll: webpackPollOption.getValue({commandLine: parsedCli}).value, getRenderDefaults, getRenderQueue, numberOfAudioTags: numberOfSharedAudioTagsOption.getValue({ @@ -169,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/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/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/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/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, 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/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/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/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 c54eb2d826c..03ce7785b00 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` @@ -76,7 +69,7 @@ Specify a location for the Remotion config file. ### `--env-file` -Specify a location for a dotenv file. Default `.env`. + ### `--jpeg-quality` @@ -96,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` @@ -186,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 81907c98eba..82a0fc0dd81 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` @@ -52,20 +52,19 @@ 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` -[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/cli/upgrade.mdx b/packages/docs/docs/cli/upgrade.mdx index 4e821bdc503..71f30551d6a 100644 --- a/packages/docs/docs/cli/upgrade.mdx +++ b/packages/docs/docs/cli/upgrade.mdx @@ -17,13 +17,11 @@ 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` -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 5782e446832..ce493f274cf 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'; @@ -87,6 +87,30 @@ Config.setPublicDir('./custom-public-dir'); The [command line flag](/docs/cli/render#--public-dir) `--public-dir` will take precedence over this option. +## `setBundleOutDir()` + + + +```ts twoslash title="remotion.config.ts" +import {Config} from '@remotion/cli/config'; +// ---cut--- +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. @@ -129,7 +153,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'; @@ -141,7 +165,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'; @@ -153,8 +177,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'; @@ -179,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'; @@ -188,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()` @@ -454,7 +479,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'; @@ -598,7 +623,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'; @@ -1069,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. @@ -1092,21 +1129,3 @@ The old way is deprecated, but will work for the foreseeable future. ## See also - [Encoding guide](/docs/encoding) - -### setImageSequencePattern(pattern) - -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'; - -Config.setImageSequencePattern('frame_[frame]_custom.[ext]'); -``` - -This will produce files like `frame_0001_custom.jpeg`, `frame_0002_custom.jpeg`, ... 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/args.ts b/packages/lambda/src/cli/args.ts index 0fb709d9711..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; @@ -17,10 +29,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; @@ -49,7 +58,7 @@ type LambdaCommandLineOptions = { export const parsedLambdaCli = CliInternals.minimist( process.argv.slice(2), { - boolean: CliInternals.BooleanFlags, + boolean: LambdaBooleanFlags, string: ['_'], }, ); diff --git a/packages/lambda/src/cli/commands/render/render.ts b/packages/lambda/src/cli/commands/render/render.ts index 2ee5686bd68..d435064cf4a 100644 --- a/packages/lambda/src/cli/commands/render/render.ts +++ b/packages/lambda/src/cli/commands/render/render.ts @@ -63,6 +63,12 @@ const { userAgentOption, disableWebSecurityOption, ignoreCertificateErrorsOption, + audioCodecOption, + videoCodecOption, + overrideHeightOption, + overrideWidthOption, + overrideFpsOption, + overrideDurationOption, } = BrowserSafeApis.options; export const renderCommand = async ({ @@ -93,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; @@ -206,6 +217,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 +303,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 +379,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/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/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/bundle-cache.tsx b/packages/renderer/src/options/bundle-cache.tsx new file mode 100644 index 00000000000..dc274724f6d --- /dev/null +++ b/packages/renderer/src/options/bundle-cache.tsx @@ -0,0 +1,42 @@ +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 && commandLine[cliFlag] !== null) { + return { + source: 'cli', + value: Boolean(commandLine[cliFlag]), + }; + } + + return { + source: cachingEnabled ? 'default' : 'config', + value: cachingEnabled, + }; + }, + 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, + id: cliFlag, +} satisfies AnyRemotionOption; diff --git a/packages/renderer/src/options/concurrency.tsx b/packages/renderer/src/options/concurrency.tsx index 81314bd273d..1a3907df03d 100644 --- a/packages/renderer/src/options/concurrency.tsx +++ b/packages/renderer/src/options/concurrency.tsx @@ -6,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, @@ -21,9 +45,12 @@ export const concurrencyOption = { type: null as Concurrency, getValue: ({commandLine}) => { if (commandLine[cliFlag] !== undefined) { + const value = commandLine[cliFlag] as Concurrency; + validateConcurrencyValue(value, 'concurrency'); + return { source: 'cli', - value: commandLine[cliFlag] as Concurrency, + value, }; } @@ -40,6 +67,8 @@ export const concurrencyOption = { }; }, setConfig: (value) => { + validateConcurrencyValue(value, 'Config.setConcurrency'); + currentConcurrency = value; }, id: cliFlag, 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 a669ff7611d..4697db766ca 100644 --- a/packages/renderer/src/options/index.tsx +++ b/packages/renderer/src/options/index.tsx @@ -5,6 +5,7 @@ import {audioCodecOption} from './audio-codec'; import {beepOnFinishOption} from './beep-on-finish'; import {binariesDirectoryOption} from './binaries-directory'; import {browserExecutableOption} from './browser-executable'; +import {bundleCacheOption} from './bundle-cache'; import {chromeModeOption} from './chrome-mode'; import {colorSpaceOption} from './color-space'; import {concurrencyOption} from './concurrency'; @@ -20,6 +21,7 @@ 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,6 +31,7 @@ import {glOption} from './gl'; import {hardwareAccelerationOption} from './hardware-acceleration'; 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'; @@ -39,17 +42,20 @@ 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'; 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'; @@ -57,17 +63,21 @@ 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'; 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'; 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 = { @@ -99,6 +109,8 @@ export const allOptions = { beepOnFinishOption, numberOfGifLoopsOption, reproOption, + runsOption, + noOpenOption, pixelFormatOption, preferLosslessOption: preferLosslessAudioOption, proResProfileOption, @@ -141,6 +153,14 @@ export const allOptions = { overrideWidthOption, overrideFpsOption, overrideDurationOption, + outDirOption, + packageManagerOption, + webpackPollOption, + stillFrameOption, + imageSequenceOption, + versionFlagOption, + bundleCacheOption, + envFileOption, }; export type AvailableOptions = keyof typeof allOptions; 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/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; 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; diff --git a/packages/renderer/src/options/still-frame.tsx b/packages/renderer/src/options/still-frame.tsx new file mode 100644 index 00000000000..172c315820e --- /dev/null +++ b/packages/renderer/src/options/still-frame.tsx @@ -0,0 +1,59 @@ +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, + 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) { + const frame = Number(commandLine[cliFlag]); + validate(frame); + return { + source: 'cli', + value: frame, + }; + } + + if (currentFrame !== null) { + return { + source: 'config', + value: currentFrame, + }; + } + + return { + source: 'default', + value: null, + }; + }, + setConfig: (value: number | null) => { + if (value !== null) { + validate(value); + } + + 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..558023f52b8 --- /dev/null +++ b/packages/renderer/src/options/version-flag.tsx @@ -0,0 +1,33 @@ +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; diff --git a/packages/renderer/src/options/webpack-poll.tsx b/packages/renderer/src/options/webpack-poll.tsx new file mode 100644 index 00000000000..dbf2a9fe3a5 --- /dev/null +++ b/packages/renderer/src/options/webpack-poll.tsx @@ -0,0 +1,58 @@ +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; 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, });