@remotion/studio 4.0.433 → 4.0.434

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5150,7 +5150,8 @@ var SidebarRenderButton = ({ composition, visible }) => {
5150
5150
  initialChromeMode: defaults.chromeMode,
5151
5151
  initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes,
5152
5152
  renderDefaults: defaults,
5153
- initialDarkMode: defaults.darkMode
5153
+ initialDarkMode: defaults.darkMode,
5154
+ readOnlyStudio: false
5154
5155
  });
5155
5156
  if (isMobileLayout) {
5156
5157
  setSidebarCollapsedState({ left: "collapsed", right: "collapsed" });
@@ -11490,10 +11491,7 @@ var CurrentCompositionKeybindings = ({ readOnlyStudio }) => {
11490
11491
  if (!video) {
11491
11492
  return;
11492
11493
  }
11493
- if (readOnlyStudio) {
11494
- return showNotification("Studio is read-only", 2000);
11495
- }
11496
- if (type !== "connected") {
11494
+ if (type !== "connected" && !SHOW_BROWSER_RENDERING && !readOnlyStudio) {
11497
11495
  showNotification("Studio server is offline", 2000);
11498
11496
  return;
11499
11497
  }
@@ -17816,7 +17814,8 @@ var makeRetryPayload = (job) => {
17816
17814
  initialHardwareAcceleration: defaults.hardwareAcceleration,
17817
17815
  initialChromeMode: job.chromeMode,
17818
17816
  initialMediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
17819
- renderDefaults: defaults
17817
+ renderDefaults: defaults,
17818
+ readOnlyStudio: false
17820
17819
  };
17821
17820
  }
17822
17821
  if (job.type === "sequence") {
@@ -17868,7 +17867,8 @@ var makeRetryPayload = (job) => {
17868
17867
  initialHardwareAcceleration: defaults.hardwareAcceleration,
17869
17868
  initialChromeMode: job.chromeMode,
17870
17869
  initialMediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
17871
- renderDefaults: defaults
17870
+ renderDefaults: defaults,
17871
+ readOnlyStudio: false
17872
17872
  };
17873
17873
  }
17874
17874
  if (job.type === "video") {
@@ -17920,7 +17920,8 @@ var makeRetryPayload = (job) => {
17920
17920
  initialHardwareAcceleration: job.hardwareAcceleration,
17921
17921
  initialChromeMode: job.chromeMode,
17922
17922
  initialMediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
17923
- renderDefaults: defaults
17923
+ renderDefaults: defaults,
17924
+ readOnlyStudio: false
17924
17925
  };
17925
17926
  }
17926
17927
  throw new Error(`Job ${JSON.stringify(job)} Not implemented`);
@@ -18813,14 +18814,6 @@ var OptionsPanel = ({ readOnlyStudio }) => {
18813
18814
  import { useContext as useContext57, useEffect as useEffect61, useRef as useRef32, useState as useState64 } from "react";
18814
18815
  import { Internals as Internals45 } from "remotion";
18815
18816
 
18816
- // src/helpers/should-show-render-button.ts
18817
- var shouldShowRenderButton = (readOnlyStudio) => {
18818
- if (readOnlyStudio) {
18819
- return SHOW_BROWSER_RENDERING;
18820
- }
18821
- return true;
18822
- };
18823
-
18824
18817
  // src/state/loop.ts
18825
18818
  var key = "remotion.loop";
18826
18819
  var persistLoopOption = (option) => {
@@ -19681,7 +19674,7 @@ var label8 = {
19681
19674
  var RENDER_TYPE_STORAGE_KEY = "remotion.renderType";
19682
19675
  var getInitialRenderType = (readOnlyStudio) => {
19683
19676
  if (!SHOW_BROWSER_RENDERING) {
19684
- return "server-render";
19677
+ return readOnlyStudio ? "render-command" : "server-render";
19685
19678
  }
19686
19679
  if (readOnlyStudio) {
19687
19680
  return "client-render";
@@ -19738,8 +19731,15 @@ var RenderButton = ({
19738
19731
  });
19739
19732
  }, [refresh]);
19740
19733
  const connectionStatus = useContext55(StudioServerConnectionCtx).previewServerState.type;
19741
- const canRender = connectionStatus === "connected" || SHOW_BROWSER_RENDERING;
19734
+ const canServerRender = connectionStatus === "connected";
19735
+ const canRender = canServerRender || SHOW_BROWSER_RENDERING || readOnlyStudio;
19742
19736
  const renderType = useMemo95(() => {
19737
+ if (readOnlyStudio) {
19738
+ if (!SHOW_BROWSER_RENDERING) {
19739
+ return "render-command";
19740
+ }
19741
+ return preferredRenderType === "render-command" ? "render-command" : "client-render";
19742
+ }
19743
19743
  if (connectionStatus === "disconnected" && SHOW_BROWSER_RENDERING) {
19744
19744
  return "client-render";
19745
19745
  }
@@ -19747,9 +19747,9 @@ var RenderButton = ({
19747
19747
  return "server-render";
19748
19748
  }
19749
19749
  return preferredRenderType;
19750
- }, [connectionStatus, preferredRenderType]);
19750
+ }, [connectionStatus, preferredRenderType, readOnlyStudio]);
19751
19751
  const shortcut = areKeyboardShortcutsDisabled() ? "" : "(R)";
19752
- const tooltip = canRender ? "Export the current composition " + shortcut : "Connect to the Studio server to render";
19752
+ const tooltip = renderType === "render-command" ? "Copy a CLI command to render this composition " + shortcut : canRender ? "Export the current composition " + shortcut : "Connect to the Studio server to render";
19753
19753
  const iconStyle4 = useMemo95(() => {
19754
19754
  return {
19755
19755
  style: {
@@ -19761,7 +19761,7 @@ var RenderButton = ({
19761
19761
  const video = Internals43.useVideo();
19762
19762
  const getCurrentFrame2 = PlayerInternals14.useFrameImperative();
19763
19763
  const { props } = useContext55(Internals43.EditorPropsContext);
19764
- const openServerRenderModal = useCallback91(() => {
19764
+ const openServerRenderModal = useCallback91((copyCommandOnly) => {
19765
19765
  if (!video) {
19766
19766
  return null;
19767
19767
  }
@@ -19771,6 +19771,7 @@ var RenderButton = ({
19771
19771
  }
19772
19772
  setSelectedModal({
19773
19773
  type: "server-render",
19774
+ readOnlyStudio: copyCommandOnly,
19774
19775
  compositionId: video.id,
19775
19776
  initialFrame: getCurrentFrame2(),
19776
19777
  initialStillImageFormat: defaults.stillImageFormat,
@@ -19854,8 +19855,12 @@ var RenderButton = ({
19854
19855
  });
19855
19856
  }, [video, setSelectedModal, getCurrentFrame2, props, inFrame, outFrame]);
19856
19857
  const onClick = useCallback91(() => {
19858
+ if (renderType === "render-command") {
19859
+ openServerRenderModal(true);
19860
+ return;
19861
+ }
19857
19862
  if (!SHOW_BROWSER_RENDERING || renderType === "server-render") {
19858
- openServerRenderModal();
19863
+ openServerRenderModal(false);
19859
19864
  } else {
19860
19865
  openClientRenderModal();
19861
19866
  }
@@ -19870,12 +19875,40 @@ var RenderButton = ({
19870
19875
  } catch {}
19871
19876
  setDropdownOpened(false);
19872
19877
  if (newType === "server-render") {
19873
- openServerRenderModal();
19878
+ openServerRenderModal(false);
19879
+ } else if (newType === "render-command") {
19880
+ openServerRenderModal(true);
19874
19881
  } else {
19875
19882
  openClientRenderModal();
19876
19883
  }
19877
- }, [openServerRenderModal, openClientRenderModal]);
19884
+ }, [openClientRenderModal, openServerRenderModal]);
19878
19885
  const dropdownValues = useMemo95(() => {
19886
+ if (readOnlyStudio) {
19887
+ return [
19888
+ {
19889
+ type: "item",
19890
+ id: "client-render",
19891
+ label: "Render on web",
19892
+ value: "client-render",
19893
+ onClick: () => handleRenderTypeChange("client-render"),
19894
+ keyHint: null,
19895
+ leftItem: null,
19896
+ subMenu: null,
19897
+ quickSwitcherLabel: null
19898
+ },
19899
+ {
19900
+ type: "item",
19901
+ id: "render-command",
19902
+ label: "Render via CLI",
19903
+ value: "render-command",
19904
+ onClick: () => handleRenderTypeChange("render-command"),
19905
+ keyHint: null,
19906
+ leftItem: null,
19907
+ subMenu: null,
19908
+ quickSwitcherLabel: null
19909
+ }
19910
+ ];
19911
+ }
19879
19912
  return [
19880
19913
  {
19881
19914
  type: "item",
@@ -19900,7 +19933,7 @@ var RenderButton = ({
19900
19933
  quickSwitcherLabel: null
19901
19934
  }
19902
19935
  ];
19903
- }, [handleRenderTypeChange]);
19936
+ }, [handleRenderTypeChange, readOnlyStudio]);
19904
19937
  const spaceToBottom = useMemo95(() => {
19905
19938
  const margin2 = 10;
19906
19939
  if (size4 && dropdownOpened) {
@@ -19942,10 +19975,10 @@ var RenderButton = ({
19942
19975
  cursor: canRender ? "pointer" : "inherit"
19943
19976
  };
19944
19977
  }, [canRender]);
19945
- const renderLabel = renderType === "server-render" ? "Render" : "Render on web";
19978
+ const renderLabel = renderType === "server-render" ? "Render" : renderType === "render-command" ? "Render via CLI" : "Render on web";
19946
19979
  const shouldShowDropdown = useMemo95(() => {
19947
19980
  if (readOnlyStudio) {
19948
- return false;
19981
+ return SHOW_BROWSER_RENDERING;
19949
19982
  }
19950
19983
  if (!SHOW_BROWSER_RENDERING) {
19951
19984
  return false;
@@ -19960,8 +19993,8 @@ var RenderButton = ({
19960
19993
  /* @__PURE__ */ jsx180("button", {
19961
19994
  style: { display: "none" },
19962
19995
  id: "render-modal-button-server",
19963
- disabled: !canRender,
19964
- onClick: openServerRenderModal,
19996
+ disabled: !canServerRender,
19997
+ onClick: () => openServerRenderModal(false),
19965
19998
  type: "button"
19966
19999
  }),
19967
20000
  " ",
@@ -20009,7 +20042,7 @@ var RenderButton = ({
20009
20042
  ref: dropdownRef,
20010
20043
  type: "button",
20011
20044
  style: dropdownTriggerStyle,
20012
- disabled: connectionStatus !== "connected",
20045
+ disabled: !readOnlyStudio && connectionStatus !== "connected",
20013
20046
  className: MENU_INITIATOR_CLASSNAME,
20014
20047
  onPointerDown,
20015
20048
  onClick: onClickDropdown,
@@ -20348,9 +20381,9 @@ var PreviewToolbar = ({ readOnlyStudio, bufferStateDelayInMilliseconds }) => {
20348
20381
  /* @__PURE__ */ jsx183(Spacing, {
20349
20382
  x: 2
20350
20383
  }),
20351
- shouldShowRenderButton(readOnlyStudio) ? /* @__PURE__ */ jsx183(RenderButton, {
20384
+ /* @__PURE__ */ jsx183(RenderButton, {
20352
20385
  readOnlyStudio
20353
- }) : null,
20386
+ }),
20354
20387
  /* @__PURE__ */ jsx183(Spacing, {
20355
20388
  x: 1.5
20356
20389
  })
@@ -27953,7 +27986,7 @@ var RenderStatusModal = ({
27953
27986
  };
27954
27987
 
27955
27988
  // src/components/RenderModal/ServerRenderModal.tsx
27956
- import { BrowserSafeApis as BrowserSafeApis10 } from "@remotion/renderer/client";
27989
+ import { BrowserSafeApis as BrowserSafeApis11 } from "@remotion/renderer/client";
27957
27990
  import { getDefaultOutLocation } from "@remotion/studio-shared";
27958
27991
  import {
27959
27992
  useCallback as useCallback137,
@@ -27979,6 +28012,252 @@ var envVariablesArrayToObject = (envVariables) => {
27979
28012
  }, {});
27980
28013
  };
27981
28014
 
28015
+ // src/helpers/make-render-command.ts
28016
+ import { BrowserSafeApis } from "@remotion/renderer/client";
28017
+ var shellQuote = (value) => {
28018
+ return `'${value.replace(/'/g, "'\\''")}'`;
28019
+ };
28020
+ var addFlagWithValue = (flags, flag, value) => {
28021
+ if (value === null || value === undefined) {
28022
+ return;
28023
+ }
28024
+ flags.push(`--${flag}=${shellQuote(String(value))}`);
28025
+ };
28026
+ var addBooleanFlag = (flags, flag, value) => {
28027
+ if (value) {
28028
+ flags.push(`--${flag}`);
28029
+ }
28030
+ };
28031
+ var valueFlag = (flag, value, defaultValue) => {
28032
+ return { flag, value, defaultValue };
28033
+ };
28034
+ var booleanFlag = (flag, value, defaultValue) => {
28035
+ return { flag, value, defaultValue };
28036
+ };
28037
+ var addValueFlagsIfChanged = (flags, mappings) => {
28038
+ for (const mapping of mappings) {
28039
+ if (mapping.value !== mapping.defaultValue) {
28040
+ addFlagWithValue(flags, mapping.flag, mapping.value);
28041
+ }
28042
+ }
28043
+ };
28044
+ var addTrueBooleanFlagsIfChanged = (flags, mappings) => {
28045
+ for (const mapping of mappings) {
28046
+ if (mapping.value && mapping.value !== mapping.defaultValue) {
28047
+ addBooleanFlag(flags, mapping.flag, true);
28048
+ }
28049
+ }
28050
+ };
28051
+ var getNpmRemotionCommandPrefix = (version) => {
28052
+ return version.trim() === "" ? "npx --yes --location=global -p @remotion/cli remotion" : `npx --yes --location=global -p @remotion/cli@${version} remotion`;
28053
+ };
28054
+ var normalizeServeUrlForRenderCommand = ({
28055
+ locationHref,
28056
+ compositionId
28057
+ }) => {
28058
+ const parsed = new URL(locationHref);
28059
+ parsed.hash = "";
28060
+ parsed.search = "";
28061
+ const suffix2 = `/${compositionId}`;
28062
+ if (parsed.pathname === suffix2) {
28063
+ parsed.pathname = "/";
28064
+ } else if (parsed.pathname.endsWith(suffix2)) {
28065
+ const basePath = parsed.pathname.slice(0, -suffix2.length);
28066
+ parsed.pathname = basePath === "" ? "/" : basePath;
28067
+ }
28068
+ if (parsed.pathname !== "/" && parsed.pathname.endsWith("/")) {
28069
+ parsed.pathname = parsed.pathname.slice(0, -1);
28070
+ }
28071
+ return `${parsed.origin}${parsed.pathname === "/" ? "" : parsed.pathname}`;
28072
+ };
28073
+ var trimDefaultOutPrefix = (outName) => {
28074
+ if (outName.startsWith("out/")) {
28075
+ const trimmed = outName.slice("out/".length);
28076
+ return trimmed.length === 0 ? outName : trimmed;
28077
+ }
28078
+ if (outName.startsWith("./out/")) {
28079
+ const trimmed = outName.slice("./out/".length);
28080
+ return trimmed.length === 0 ? outName : trimmed;
28081
+ }
28082
+ return outName;
28083
+ };
28084
+ var getRenderMediaFlag = (option) => {
28085
+ return BrowserSafeApis.optionsMap.renderMedia[option].cliFlag;
28086
+ };
28087
+ var renderMediaValueFlag = (option, value, defaultValue) => {
28088
+ return valueFlag(getRenderMediaFlag(option), value, defaultValue);
28089
+ };
28090
+ var renderMediaBooleanFlag = (option, value, defaultValue) => {
28091
+ return booleanFlag(getRenderMediaFlag(option), value, defaultValue);
28092
+ };
28093
+ var makeReadOnlyStudioRenderCommand = ({
28094
+ remotionVersion,
28095
+ locationHref,
28096
+ compositionId,
28097
+ outName,
28098
+ renderMode,
28099
+ renderDefaults,
28100
+ durationInFrames,
28101
+ concurrency,
28102
+ frame: frame2,
28103
+ startFrame,
28104
+ endFrame,
28105
+ stillImageFormat,
28106
+ sequenceImageFormat,
28107
+ videoImageFormat,
28108
+ jpegQuality,
28109
+ codec,
28110
+ muted,
28111
+ enforceAudioTrack,
28112
+ proResProfile,
28113
+ x264Preset,
28114
+ pixelFormat,
28115
+ crf,
28116
+ videoBitrate,
28117
+ audioBitrate,
28118
+ audioCodec,
28119
+ everyNthFrame,
28120
+ numberOfGifLoops,
28121
+ disallowParallelEncoding,
28122
+ encodingBufferSize,
28123
+ encodingMaxRate,
28124
+ forSeamlessAacConcatenation,
28125
+ separateAudioTo,
28126
+ colorSpace,
28127
+ scale,
28128
+ logLevel,
28129
+ delayRenderTimeout,
28130
+ hardwareAcceleration,
28131
+ chromeMode,
28132
+ headless,
28133
+ disableWebSecurity,
28134
+ ignoreCertificateErrors,
28135
+ gl,
28136
+ userAgent,
28137
+ multiProcessOnLinux,
28138
+ darkMode,
28139
+ offthreadVideoCacheSizeInBytes,
28140
+ offthreadVideoThreads,
28141
+ mediaCacheSizeInBytes,
28142
+ beepOnFinish,
28143
+ repro,
28144
+ metadata,
28145
+ envVariables,
28146
+ inputProps
28147
+ }) => {
28148
+ const serveUrl = normalizeServeUrlForRenderCommand({
28149
+ locationHref,
28150
+ compositionId
28151
+ });
28152
+ const isStillRender = renderMode === "still";
28153
+ const isSequenceRender = renderMode === "sequence";
28154
+ const hasCodecSpecificOptions = !isSequenceRender;
28155
+ const commandType = isStillRender ? "still" : "render";
28156
+ const command = getNpmRemotionCommandPrefix(remotionVersion);
28157
+ const { options } = BrowserSafeApis;
28158
+ const flags = [];
28159
+ addValueFlagsIfChanged(flags, [
28160
+ valueFlag(options.scaleOption.cliFlag, scale, renderDefaults.scale),
28161
+ renderMediaValueFlag("logLevel", logLevel, renderDefaults.logLevel),
28162
+ renderMediaValueFlag("timeoutInMilliseconds", delayRenderTimeout, renderDefaults.delayRenderTimeout),
28163
+ renderMediaValueFlag("chromeMode", chromeMode, renderDefaults.chromeMode),
28164
+ valueFlag(options.glOption.cliFlag, gl, renderDefaults.openGlRenderer),
28165
+ valueFlag(options.userAgentOption.cliFlag, userAgent, renderDefaults.userAgent),
28166
+ renderMediaValueFlag("offthreadVideoCacheSizeInBytes", offthreadVideoCacheSizeInBytes, renderDefaults.offthreadVideoCacheSizeInBytes),
28167
+ renderMediaValueFlag("offthreadVideoThreads", offthreadVideoThreads, renderDefaults.offthreadVideoThreads),
28168
+ renderMediaValueFlag("mediaCacheSizeInBytes", mediaCacheSizeInBytes, renderDefaults.mediaCacheSizeInBytes)
28169
+ ]);
28170
+ if (headless !== renderDefaults.headless) {
28171
+ addFlagWithValue(flags, options.headlessOption.cliFlag, !headless);
28172
+ }
28173
+ addTrueBooleanFlagsIfChanged(flags, [
28174
+ booleanFlag(options.disableWebSecurityOption.cliFlag, disableWebSecurity, renderDefaults.disableWebSecurity),
28175
+ booleanFlag(options.ignoreCertificateErrorsOption.cliFlag, ignoreCertificateErrors, renderDefaults.ignoreCertificateErrors),
28176
+ booleanFlag(options.enableMultiprocessOnLinuxOption.cliFlag, multiProcessOnLinux, renderDefaults.multiProcessOnLinux),
28177
+ booleanFlag(options.darkModeOption.cliFlag, darkMode, renderDefaults.darkMode),
28178
+ booleanFlag(options.beepOnFinishOption.cliFlag, beepOnFinish, renderDefaults.beepOnFinish)
28179
+ ]);
28180
+ if (isStillRender) {
28181
+ addValueFlagsIfChanged(flags, [
28182
+ valueFlag(options.stillFrameOption.cliFlag, frame2, 0),
28183
+ valueFlag(options.stillImageFormatOption.cliFlag, stillImageFormat, renderDefaults.stillImageFormat),
28184
+ valueFlag(options.jpegQualityOption.cliFlag, jpegQuality, renderDefaults.jpegQuality)
28185
+ ]);
28186
+ } else {
28187
+ addValueFlagsIfChanged(flags, [
28188
+ valueFlag(options.concurrencyOption.cliFlag, concurrency, renderDefaults.concurrency)
28189
+ ]);
28190
+ if (isSequenceRender) {
28191
+ addBooleanFlag(flags, options.imageSequenceOption.cliFlag, true);
28192
+ if (sequenceImageFormat !== "jpeg") {
28193
+ addFlagWithValue(flags, options.videoImageFormatOption.cliFlag, sequenceImageFormat);
28194
+ }
28195
+ } else {
28196
+ addValueFlagsIfChanged(flags, [
28197
+ valueFlag(options.videoImageFormatOption.cliFlag, videoImageFormat, renderDefaults.videoImageFormat),
28198
+ renderMediaValueFlag("hardwareAcceleration", hardwareAcceleration, renderDefaults.hardwareAcceleration)
28199
+ ]);
28200
+ }
28201
+ if (hasCodecSpecificOptions && codec !== renderDefaults.codec) {
28202
+ addFlagWithValue(flags, getRenderMediaFlag("codec"), codec);
28203
+ }
28204
+ if (startFrame !== 0 || endFrame !== durationInFrames - 1) {
28205
+ addFlagWithValue(flags, options.framesOption.cliFlag, `${startFrame}-${endFrame}`);
28206
+ }
28207
+ if (hasCodecSpecificOptions) {
28208
+ addTrueBooleanFlagsIfChanged(flags, [
28209
+ renderMediaBooleanFlag("muted", muted, renderDefaults.muted),
28210
+ booleanFlag(options.enforceAudioOption.cliFlag, enforceAudioTrack, renderDefaults.enforceAudioTrack),
28211
+ renderMediaBooleanFlag("forSeamlessAacConcatenation", forSeamlessAacConcatenation, renderDefaults.forSeamlessAacConcatenation)
28212
+ ]);
28213
+ addValueFlagsIfChanged(flags, [
28214
+ valueFlag(options.pixelFormatOption.cliFlag, pixelFormat, renderDefaults.pixelFormat),
28215
+ renderMediaValueFlag("colorSpace", colorSpace, renderDefaults.colorSpace),
28216
+ valueFlag(options.proResProfileOption.cliFlag, proResProfile, renderDefaults.proResProfile),
28217
+ renderMediaValueFlag("x264Preset", x264Preset, renderDefaults.x264Preset),
28218
+ valueFlag(options.crfOption.cliFlag, crf, null),
28219
+ valueFlag(options.jpegQualityOption.cliFlag, jpegQuality, renderDefaults.jpegQuality),
28220
+ renderMediaValueFlag("videoBitrate", videoBitrate, renderDefaults.videoBitrate),
28221
+ renderMediaValueFlag("audioBitrate", audioBitrate, renderDefaults.audioBitrate),
28222
+ valueFlag(options.everyNthFrameOption.cliFlag, everyNthFrame, renderDefaults.everyNthFrame),
28223
+ renderMediaValueFlag("numberOfGifLoops", numberOfGifLoops, renderDefaults.numberOfGifLoops),
28224
+ renderMediaValueFlag("encodingBufferSize", encodingBufferSize, renderDefaults.encodingBufferSize),
28225
+ renderMediaValueFlag("encodingMaxRate", encodingMaxRate, renderDefaults.encodingMaxRate),
28226
+ renderMediaValueFlag("separateAudioTo", separateAudioTo, null)
28227
+ ]);
28228
+ const defaultAudioCodec = BrowserSafeApis.defaultAudioCodecs[codec]?.compressed;
28229
+ if (audioCodec !== defaultAudioCodec) {
28230
+ addFlagWithValue(flags, getRenderMediaFlag("audioCodec"), audioCodec);
28231
+ }
28232
+ }
28233
+ addTrueBooleanFlagsIfChanged(flags, [
28234
+ renderMediaBooleanFlag("disallowParallelEncoding", disallowParallelEncoding, false),
28235
+ renderMediaBooleanFlag("repro", repro, renderDefaults.repro)
28236
+ ]);
28237
+ }
28238
+ if (metadata) {
28239
+ for (const [key5, value] of Object.entries(metadata)) {
28240
+ addFlagWithValue(flags, options.metadataOption.cliFlag, `${key5}=${value}`);
28241
+ }
28242
+ }
28243
+ if (Object.keys(inputProps).length > 0) {
28244
+ addFlagWithValue(flags, options.propsOption.cliFlag, JSON.stringify(inputProps));
28245
+ }
28246
+ const envArgs = Object.entries(envVariables).sort(([a], [b]) => a.localeCompare(b)).map(([key5, value]) => shellQuote(`${key5}=${value}`));
28247
+ const renderCommand = [
28248
+ command,
28249
+ commandType,
28250
+ shellQuote(serveUrl),
28251
+ shellQuote(compositionId),
28252
+ shellQuote(trimDefaultOutPrefix(outName)),
28253
+ ...flags
28254
+ ].join(" ");
28255
+ if (envArgs.length === 0) {
28256
+ return renderCommand;
28257
+ }
28258
+ return ["env", ...envArgs, renderCommand].join(" ");
28259
+ };
28260
+
27982
28261
  // src/helpers/render-modal-sections.ts
27983
28262
  import { useMemo as useMemo128, useState as useState85 } from "react";
27984
28263
  var useRenderModalSections = (renderMode, codec) => {
@@ -28120,14 +28399,14 @@ var VerticalTab = ({ children, onClick, style: style12, selected }) => {
28120
28399
  };
28121
28400
 
28122
28401
  // src/components/RenderModal/CrfSetting.tsx
28123
- import { BrowserSafeApis as BrowserSafeApis2 } from "@remotion/renderer/client";
28402
+ import { BrowserSafeApis as BrowserSafeApis3 } from "@remotion/renderer/client";
28124
28403
  import { useState as useState88 } from "react";
28125
28404
 
28126
28405
  // src/components/RenderModal/NumberSetting.tsx
28127
28406
  import { useCallback as useCallback122 } from "react";
28128
28407
 
28129
28408
  // src/components/RenderModal/OptionExplainerBubble.tsx
28130
- import { BrowserSafeApis } from "@remotion/renderer/client";
28409
+ import { BrowserSafeApis as BrowserSafeApis2 } from "@remotion/renderer/client";
28131
28410
 
28132
28411
  // src/components/RenderModal/CliCopyButton.tsx
28133
28412
  import { useCallback as useCallback121, useEffect as useEffect84, useMemo as useMemo130, useState as useState87 } from "react";
@@ -28354,7 +28633,7 @@ var OptionExplainer = ({ option }) => {
28354
28633
  // src/components/RenderModal/OptionExplainerBubble.tsx
28355
28634
  import { jsx as jsx251 } from "react/jsx-runtime";
28356
28635
  var OptionExplainerBubble = ({ id }) => {
28357
- const option = BrowserSafeApis.options[id];
28636
+ const option = BrowserSafeApis2.options[id];
28358
28637
  return /* @__PURE__ */ jsx251(InfoBubble, {
28359
28638
  title: "Learn more about this option",
28360
28639
  children: /* @__PURE__ */ jsx251(OptionExplainer, {
@@ -28422,8 +28701,8 @@ var NumberSetting = ({ name, value, step, hint, onValueChanged, max, min, format
28422
28701
  // src/components/RenderModal/CrfSetting.tsx
28423
28702
  import { jsx as jsx253 } from "react/jsx-runtime";
28424
28703
  var getDefaultCrfState = () => {
28425
- return BrowserSafeApis2.validCodecs.map((c) => {
28426
- return [c, BrowserSafeApis2.getDefaultCrfForCodec(c)];
28704
+ return BrowserSafeApis3.validCodecs.map((c) => {
28705
+ return [c, BrowserSafeApis3.getDefaultCrfForCodec(c)];
28427
28706
  }).reduce((acc, [codec, crf]) => {
28428
28707
  return {
28429
28708
  ...acc,
@@ -28433,7 +28712,7 @@ var getDefaultCrfState = () => {
28433
28712
  };
28434
28713
  var useCrfState = (codec) => {
28435
28714
  const [state, setState] = useState88(() => getDefaultCrfState());
28436
- const range = BrowserSafeApis2.getValidCrfRanges(codec);
28715
+ const range = BrowserSafeApis3.getValidCrfRanges(codec);
28437
28716
  const setCrf = (updater) => {
28438
28717
  setState((q) => {
28439
28718
  const val = q[codec];
@@ -28466,7 +28745,7 @@ var CrfSetting = ({ crf, setCrf, min, max, option }) => {
28466
28745
  };
28467
28746
 
28468
28747
  // src/components/RenderModal/get-default-codecs.ts
28469
- import { BrowserSafeApis as BrowserSafeApis3 } from "@remotion/renderer/client";
28748
+ import { BrowserSafeApis as BrowserSafeApis4 } from "@remotion/renderer/client";
28470
28749
  import { NoReactAPIs } from "@remotion/renderer/pure";
28471
28750
  var getDefaultCodecs = ({
28472
28751
  defaultConfigurationVideoCodec,
@@ -28486,7 +28765,7 @@ var getDefaultCodecs = ({
28486
28765
  initialVideoCodecForVideoTab: NoReactAPIs.isAudioCodec(defaultConfigurationVideoCodec) ? "h264" : defaultConfigurationVideoCodec
28487
28766
  };
28488
28767
  }
28489
- const suitableAudioCodecForVideoCodec = BrowserSafeApis3.defaultAudioCodecs[userPreferredVideoCodec].compressed;
28768
+ const suitableAudioCodecForVideoCodec = BrowserSafeApis4.defaultAudioCodecs[userPreferredVideoCodec].compressed;
28490
28769
  return {
28491
28770
  initialAudioCodec: defaultConfigurationAudioCodec ?? suitableAudioCodecForVideoCodec,
28492
28771
  initialVideoCodec: userPreferredVideoCodec,
@@ -28506,7 +28785,7 @@ var getStringBeforeSuffix = (fileName) => {
28506
28785
  };
28507
28786
 
28508
28787
  // src/components/RenderModal/out-name-checker.ts
28509
- import { BrowserSafeApis as BrowserSafeApis4 } from "@remotion/renderer/client";
28788
+ import { BrowserSafeApis as BrowserSafeApis5 } from "@remotion/renderer/client";
28510
28789
  var invalidCharacters = ["?", "*", "+", ":", "%"];
28511
28790
  var isValidStillExtension = (extension, stillImageFormat) => {
28512
28791
  if (stillImageFormat === "jpeg" && extension === "jpg") {
@@ -28546,8 +28825,8 @@ var isValidOutName = ({
28546
28825
  }) => {
28547
28826
  const extension = outName.substring(outName.lastIndexOf(".") + 1);
28548
28827
  const prefix = outName.substring(0, outName.lastIndexOf("."));
28549
- const map = BrowserSafeApis4.defaultFileExtensionMap[codec];
28550
- if (BrowserSafeApis4.supportedAudioCodecs[codec].length > 0 && !(audioCodec in map.forAudioCodec)) {
28828
+ const map = BrowserSafeApis5.defaultFileExtensionMap[codec];
28829
+ if (BrowserSafeApis5.supportedAudioCodecs[codec].length > 0 && !(audioCodec in map.forAudioCodec)) {
28551
28830
  throw new Error(`Audio codec ${audioCodec} is not supported for codec ${codec}`);
28552
28831
  }
28553
28832
  const hasDotAfterSlash = () => {
@@ -28563,7 +28842,7 @@ var isValidOutName = ({
28563
28842
  return prefix.split("").some((char) => invalidCharacters.includes(char));
28564
28843
  };
28565
28844
  if (renderMode === "video" || renderMode === "audio") {
28566
- BrowserSafeApis4.validateOutputFilename({
28845
+ BrowserSafeApis5.validateOutputFilename({
28567
28846
  codec,
28568
28847
  audioCodecSetting: audioCodec ?? null,
28569
28848
  extension,
@@ -28651,7 +28930,7 @@ var flexer = {
28651
28930
  };
28652
28931
 
28653
28932
  // src/components/RenderModal/RenderModalAdvanced.tsx
28654
- import { BrowserSafeApis as BrowserSafeApis5 } from "@remotion/renderer/client";
28933
+ import { BrowserSafeApis as BrowserSafeApis6 } from "@remotion/renderer/client";
28655
28934
  import { useCallback as useCallback126, useMemo as useMemo131 } from "react";
28656
28935
 
28657
28936
  // src/helpers/presets-labels.ts
@@ -29084,7 +29363,7 @@ var RenderModalAdvanced = ({
29084
29363
  });
29085
29364
  }, [extendedOpenGlOptions, openGlOption, setOpenGlOption]);
29086
29365
  const chromeModeOptions = useMemo131(() => {
29087
- return BrowserSafeApis5.validChromeModeOptions.map((option) => {
29366
+ return BrowserSafeApis6.validChromeModeOptions.map((option) => {
29088
29367
  return {
29089
29368
  label: option,
29090
29369
  onClick: () => setChromeModeOption(option),
@@ -29100,7 +29379,7 @@ var RenderModalAdvanced = ({
29100
29379
  });
29101
29380
  }, [chromeModeOption, setChromeModeOption]);
29102
29381
  const x264PresetOptions = useMemo131(() => {
29103
- return BrowserSafeApis5.x264PresetOptions.map((option) => {
29382
+ return BrowserSafeApis6.x264PresetOptions.map((option) => {
29104
29383
  return {
29105
29384
  label: labelx264Preset(option),
29106
29385
  onClick: () => setx264Preset(option),
@@ -29116,7 +29395,7 @@ var RenderModalAdvanced = ({
29116
29395
  });
29117
29396
  }, [setx264Preset, x264Preset]);
29118
29397
  const hardwareAccelerationValues = useMemo131(() => {
29119
- return BrowserSafeApis5.hardwareAccelerationOptions.map((option) => {
29398
+ return BrowserSafeApis6.hardwareAccelerationOptions.map((option) => {
29120
29399
  return {
29121
29400
  label: option,
29122
29401
  onClick: () => setHardwareAcceleration(option),
@@ -29607,7 +29886,7 @@ var RenderModalAdvanced = ({
29607
29886
  };
29608
29887
 
29609
29888
  // src/components/RenderModal/RenderModalAudio.tsx
29610
- import { BrowserSafeApis as BrowserSafeApis7 } from "@remotion/renderer/client";
29889
+ import { BrowserSafeApis as BrowserSafeApis8 } from "@remotion/renderer/client";
29611
29890
  import { useCallback as useCallback130 } from "react";
29612
29891
 
29613
29892
  // src/components/RenderModal/EnforceAudioTrackSetting.tsx
@@ -29700,7 +29979,7 @@ var MutedSetting = ({ muted, setMuted, enforceAudioTrack }) => {
29700
29979
  };
29701
29980
 
29702
29981
  // src/components/RenderModal/SeparateAudioOption.tsx
29703
- import { BrowserSafeApis as BrowserSafeApis6 } from "@remotion/renderer/client";
29982
+ import { BrowserSafeApis as BrowserSafeApis7 } from "@remotion/renderer/client";
29704
29983
  import { useCallback as useCallback129, useMemo as useMemo132 } from "react";
29705
29984
 
29706
29985
  // src/helpers/use-file-existence.ts
@@ -29835,7 +30114,7 @@ var SeparateAudioOptionInput = ({ separateAudioTo, setSeparateAudioTo, audioCode
29835
30114
  setSeparateAudioTo(e.target.value);
29836
30115
  }, [setSeparateAudioTo]);
29837
30116
  const validationMessage = useMemo132(() => {
29838
- const expectedExtension = BrowserSafeApis6.getExtensionFromAudioCodec(audioCodec);
30117
+ const expectedExtension = BrowserSafeApis7.getExtensionFromAudioCodec(audioCodec);
29839
30118
  const actualExtension = separateAudioTo.split(".").pop();
29840
30119
  if (actualExtension !== expectedExtension) {
29841
30120
  return `Expected extension: .${expectedExtension}`;
@@ -29854,7 +30133,7 @@ var SeparateAudioOptionInput = ({ separateAudioTo, setSeparateAudioTo, audioCode
29854
30133
  var SeparateAudioOption = ({ separateAudioTo, setSeparateAudioTo, audioCodec, outName }) => {
29855
30134
  const onSeparateAudioChange = useCallback129((e) => {
29856
30135
  if (e.target.checked) {
29857
- const extension = BrowserSafeApis6.getExtensionFromAudioCodec(audioCodec);
30136
+ const extension = BrowserSafeApis7.getExtensionFromAudioCodec(audioCodec);
29858
30137
  setSeparateAudioTo(`${getStringBeforeSuffix(outName)}.${extension}`);
29859
30138
  } else {
29860
30139
  setSeparateAudioTo(null);
@@ -29932,7 +30211,7 @@ var RenderModalAudio = ({
29932
30211
  setForSeamlessAacConcatenation(e.target.checked);
29933
30212
  }, [setForSeamlessAacConcatenation]);
29934
30213
  const audioCodecOptions = useCallback130((currentCodec) => {
29935
- return BrowserSafeApis7.supportedAudioCodecs[currentCodec].map((audioCodecOption) => {
30214
+ return BrowserSafeApis8.supportedAudioCodecs[currentCodec].map((audioCodecOption) => {
29936
30215
  return {
29937
30216
  label: humanReadableAudioCodec(audioCodecOption),
29938
30217
  onClick: () => setAudioCodec(audioCodecOption),
@@ -30077,7 +30356,7 @@ var RenderModalAudio = ({
30077
30356
  };
30078
30357
 
30079
30358
  // src/components/RenderModal/RenderModalBasic.tsx
30080
- import { BrowserSafeApis as BrowserSafeApis8 } from "@remotion/renderer/client";
30359
+ import { BrowserSafeApis as BrowserSafeApis9 } from "@remotion/renderer/client";
30081
30360
  import { NoReactAPIs as NoReactAPIs2 } from "@remotion/renderer/pure";
30082
30361
  import { useCallback as useCallback133, useMemo as useMemo134 } from "react";
30083
30362
 
@@ -30338,11 +30617,12 @@ var RenderModalBasic = ({
30338
30617
  startFrame,
30339
30618
  validationMessage,
30340
30619
  setVerboseLogging,
30341
- logLevel
30620
+ logLevel,
30621
+ showOutputName
30342
30622
  }) => {
30343
30623
  const existence = useFileExistence(outName);
30344
30624
  const videoCodecOptions = useMemo134(() => {
30345
- return BrowserSafeApis8.validCodecs.filter((c) => {
30625
+ return BrowserSafeApis9.validCodecs.filter((c) => {
30346
30626
  return NoReactAPIs2.isAudioCodec(c) === (renderMode === "audio");
30347
30627
  }).map((codecOption) => {
30348
30628
  return {
@@ -30360,7 +30640,7 @@ var RenderModalBasic = ({
30360
30640
  });
30361
30641
  }, [renderMode, setCodec, codec]);
30362
30642
  const proResProfileOptions = useMemo134(() => {
30363
- return BrowserSafeApis8.proResProfileOptions.map((option) => {
30643
+ return BrowserSafeApis9.proResProfileOptions.map((option) => {
30364
30644
  return {
30365
30645
  label: labelProResProfile(option),
30366
30646
  onClick: () => setProResProfile(option),
@@ -30499,14 +30779,14 @@ var RenderModalBasic = ({
30499
30779
  setStartFrame,
30500
30780
  startFrame
30501
30781
  }),
30502
- /* @__PURE__ */ jsx266(RenderModalOutputName, {
30782
+ showOutputName ? /* @__PURE__ */ jsx266(RenderModalOutputName, {
30503
30783
  existence,
30504
30784
  inputStyle: input,
30505
30785
  outName,
30506
30786
  onValueChange,
30507
30787
  validationMessage,
30508
30788
  label: renderMode === "sequence" ? "Folder name" : "Output name"
30509
- }),
30789
+ }) : null,
30510
30790
  /* @__PURE__ */ jsxs136("div", {
30511
30791
  style: optionRow,
30512
30792
  children: [
@@ -30644,7 +30924,7 @@ var RenderModalGif = ({
30644
30924
  };
30645
30925
 
30646
30926
  // src/components/RenderModal/RenderModalPicture.tsx
30647
- import { BrowserSafeApis as BrowserSafeApis9 } from "@remotion/renderer/client";
30927
+ import { BrowserSafeApis as BrowserSafeApis10 } from "@remotion/renderer/client";
30648
30928
  import { useCallback as useCallback136, useMemo as useMemo136 } from "react";
30649
30929
 
30650
30930
  // src/components/RenderModal/JpegQualitySetting.tsx
@@ -30748,7 +31028,7 @@ var RenderModalPicture = ({
30748
31028
  compositionHeight
30749
31029
  }) => {
30750
31030
  const colorSpaceOptions = useMemo136(() => {
30751
- return BrowserSafeApis9.validColorSpaces.map((option) => {
31031
+ return BrowserSafeApis10.validColorSpaces.map((option) => {
30752
31032
  return {
30753
31033
  label: option,
30754
31034
  onClick: () => setColorSpace(option),
@@ -31041,6 +31321,7 @@ var reducer2 = (state, action) => {
31041
31321
  return state;
31042
31322
  };
31043
31323
  var RenderModal = ({
31324
+ readOnlyStudio,
31044
31325
  initialFrame,
31045
31326
  initialVideoImageFormat,
31046
31327
  initialStillImageFormat,
@@ -31131,7 +31412,7 @@ var RenderModal = ({
31131
31412
  const [initialOutName] = useState90(() => {
31132
31413
  return getDefaultOutLocation({
31133
31414
  compositionName: resolvedComposition.id,
31134
- defaultExtension: initialRenderType === "still" ? initialStillImageFormat : isVideo ? BrowserSafeApis10.getFileExtensionFromCodec(initialVideoCodec, initialAudioCodec) : initialStillImageFormat,
31415
+ defaultExtension: initialRenderType === "still" ? initialStillImageFormat : isVideo ? BrowserSafeApis11.getFileExtensionFromCodec(initialVideoCodec, initialAudioCodec) : initialStillImageFormat,
31135
31416
  type: "asset",
31136
31417
  compositionDefaultOutName: resolvedComposition.defaultOutName,
31137
31418
  outputLocation: renderDefaults.outputLocation
@@ -31216,8 +31497,8 @@ var RenderModal = ({
31216
31497
  }
31217
31498
  return null;
31218
31499
  }, [customTargetAudioBitrate, shouldHaveCustomTargetAudioBitrate]);
31219
- const supportsCrf = BrowserSafeApis10.codecSupportsCrf(codec);
31220
- const supportsVideoBitrate = BrowserSafeApis10.codecSupportsVideoBitrate(codec);
31500
+ const supportsCrf = BrowserSafeApis11.codecSupportsCrf(codec);
31501
+ const supportsVideoBitrate = BrowserSafeApis11.codecSupportsVideoBitrate(codec);
31221
31502
  const supportsBothQualityControls = useMemo137(() => {
31222
31503
  return supportsCrf && supportsVideoBitrate && hardwareAcceleration !== "if-possible" && hardwareAcceleration !== "required";
31223
31504
  }, [hardwareAcceleration, supportsCrf, supportsVideoBitrate]);
@@ -31303,10 +31584,10 @@ var RenderModal = ({
31303
31584
  return Math.max(0, Math.min(resolvedComposition.durationInFrames - 1, parsed));
31304
31585
  }, [resolvedComposition.durationInFrames, unclampedFrame]);
31305
31586
  const deriveFinalAudioCodec = useCallback137((passedVideoCodec, passedAudioCodec) => {
31306
- if (passedAudioCodec !== null && BrowserSafeApis10.supportedAudioCodecs[passedVideoCodec].includes(passedAudioCodec)) {
31587
+ if (passedAudioCodec !== null && BrowserSafeApis11.supportedAudioCodecs[passedVideoCodec].includes(passedAudioCodec)) {
31307
31588
  return passedAudioCodec;
31308
31589
  }
31309
- return BrowserSafeApis10.defaultAudioCodecs[passedVideoCodec].compressed;
31590
+ return BrowserSafeApis11.defaultAudioCodecs[passedVideoCodec].compressed;
31310
31591
  }, []);
31311
31592
  const setDefaultOutName = useCallback137((options) => {
31312
31593
  if (options.type === "still") {
@@ -31321,7 +31602,7 @@ var RenderModal = ({
31321
31602
  });
31322
31603
  } else {
31323
31604
  setOutName((prev) => {
31324
- const codecSuffix = BrowserSafeApis10.getFileExtensionFromCodec(options.codec, deriveFinalAudioCodec(options.codec, options.audioCodec));
31605
+ const codecSuffix = BrowserSafeApis11.getFileExtensionFromCodec(options.codec, deriveFinalAudioCodec(options.codec, options.audioCodec));
31325
31606
  const newFileName = getStringBeforeSuffix(prev) + "." + codecSuffix;
31326
31607
  return newFileName;
31327
31608
  });
@@ -31338,7 +31619,7 @@ var RenderModal = ({
31338
31619
  if (prev === null) {
31339
31620
  return null;
31340
31621
  }
31341
- const newExtension = BrowserSafeApis10.getExtensionFromAudioCodec(newAudioCodec);
31622
+ const newExtension = BrowserSafeApis11.getExtensionFromAudioCodec(newAudioCodec);
31342
31623
  const newFileName = getStringBeforeSuffix(prev) + "." + newExtension;
31343
31624
  return newFileName;
31344
31625
  });
@@ -31427,7 +31708,7 @@ var RenderModal = ({
31427
31708
  }, [codec, everyNthFrameSetting]);
31428
31709
  const audioCodec = deriveFinalAudioCodec(codec, userSelectedAudioCodec);
31429
31710
  const availablePixelFormats = useMemo137(() => {
31430
- return BrowserSafeApis10.validPixelFormatsForCodec(codec);
31711
+ return BrowserSafeApis11.validPixelFormatsForCodec(codec);
31431
31712
  }, [codec]);
31432
31713
  const pixelFormat = useMemo137(() => {
31433
31714
  if (availablePixelFormats.includes(userPreferredPixelFormat)) {
@@ -31754,8 +32035,134 @@ var RenderModal = ({
31754
32035
  });
31755
32036
  const { tab, setTab, shownTabs } = useRenderModalSections(renderMode, codec);
31756
32037
  const { registerKeybinding } = useKeybinding();
31757
- const renderDisabled = state.type === "load" || !outnameValidation.valid;
32038
+ const readOnlyRenderCommand = useMemo137(() => {
32039
+ if (!readOnlyStudio) {
32040
+ return null;
32041
+ }
32042
+ return makeReadOnlyStudioRenderCommand({
32043
+ remotionVersion: window.remotion_version,
32044
+ locationHref: window.location.href,
32045
+ compositionId: resolvedComposition.id,
32046
+ outName,
32047
+ renderMode,
32048
+ renderDefaults,
32049
+ durationInFrames: resolvedComposition.durationInFrames,
32050
+ concurrency,
32051
+ frame: frame2,
32052
+ startFrame,
32053
+ endFrame,
32054
+ stillImageFormat,
32055
+ sequenceImageFormat,
32056
+ videoImageFormat,
32057
+ jpegQuality: renderMode === "video" ? stillImageFormat === "jpeg" ? jpegQuality : null : renderMode === "audio" ? null : jpegQuality,
32058
+ codec,
32059
+ muted,
32060
+ enforceAudioTrack,
32061
+ proResProfile,
32062
+ x264Preset,
32063
+ pixelFormat,
32064
+ crf: qualityControlType === "crf" && hardwareAcceleration !== "if-possible" && hardwareAcceleration !== "required" ? crf : null,
32065
+ videoBitrate,
32066
+ audioBitrate,
32067
+ audioCodec,
32068
+ everyNthFrame,
32069
+ numberOfGifLoops,
32070
+ disallowParallelEncoding,
32071
+ encodingBufferSize,
32072
+ encodingMaxRate,
32073
+ forSeamlessAacConcatenation,
32074
+ separateAudioTo,
32075
+ colorSpace,
32076
+ scale,
32077
+ logLevel,
32078
+ delayRenderTimeout,
32079
+ hardwareAcceleration,
32080
+ chromeMode,
32081
+ headless,
32082
+ disableWebSecurity,
32083
+ ignoreCertificateErrors,
32084
+ gl: openGlOption === "default" ? null : openGlOption,
32085
+ userAgent,
32086
+ multiProcessOnLinux,
32087
+ darkMode,
32088
+ offthreadVideoCacheSizeInBytes,
32089
+ offthreadVideoThreads,
32090
+ mediaCacheSizeInBytes,
32091
+ beepOnFinish,
32092
+ repro,
32093
+ metadata,
32094
+ envVariables: envVariablesArrayToObject(envVariables),
32095
+ inputProps
32096
+ });
32097
+ }, [
32098
+ audioBitrate,
32099
+ audioCodec,
32100
+ beepOnFinish,
32101
+ chromeMode,
32102
+ codec,
32103
+ colorSpace,
32104
+ concurrency,
32105
+ crf,
32106
+ darkMode,
32107
+ delayRenderTimeout,
32108
+ disableWebSecurity,
32109
+ disallowParallelEncoding,
32110
+ endFrame,
32111
+ encodingBufferSize,
32112
+ encodingMaxRate,
32113
+ enforceAudioTrack,
32114
+ envVariables,
32115
+ everyNthFrame,
32116
+ frame2,
32117
+ forSeamlessAacConcatenation,
32118
+ hardwareAcceleration,
32119
+ headless,
32120
+ ignoreCertificateErrors,
32121
+ inputProps,
32122
+ jpegQuality,
32123
+ logLevel,
32124
+ mediaCacheSizeInBytes,
32125
+ metadata,
32126
+ multiProcessOnLinux,
32127
+ muted,
32128
+ numberOfGifLoops,
32129
+ offthreadVideoCacheSizeInBytes,
32130
+ offthreadVideoThreads,
32131
+ openGlOption,
32132
+ outName,
32133
+ pixelFormat,
32134
+ proResProfile,
32135
+ qualityControlType,
32136
+ readOnlyStudio,
32137
+ renderDefaults,
32138
+ renderMode,
32139
+ repro,
32140
+ resolvedComposition.durationInFrames,
32141
+ resolvedComposition.id,
32142
+ scale,
32143
+ separateAudioTo,
32144
+ sequenceImageFormat,
32145
+ startFrame,
32146
+ stillImageFormat,
32147
+ userAgent,
32148
+ videoImageFormat,
32149
+ videoBitrate,
32150
+ x264Preset
32151
+ ]);
32152
+ const [commandCopiedAt, setCommandCopiedAt] = useState90(null);
32153
+ const renderDisabled = readOnlyStudio ? false : !outnameValidation.valid || state.type === "load";
31758
32154
  const trigger = useCallback137(() => {
32155
+ if (readOnlyStudio) {
32156
+ if (!readOnlyRenderCommand) {
32157
+ return;
32158
+ }
32159
+ copyText(readOnlyRenderCommand).then(() => {
32160
+ setCommandCopiedAt(Date.now());
32161
+ }).catch((err) => {
32162
+ showNotification(`Could not copy: ${err.message}`, 2000);
32163
+ });
32164
+ return;
32165
+ }
31759
32166
  if (renderMode === "still") {
31760
32167
  onClickStill();
31761
32168
  } else if (renderMode === "sequence") {
@@ -31763,7 +32170,23 @@ var RenderModal = ({
31763
32170
  } else {
31764
32171
  onClickVideo();
31765
32172
  }
31766
- }, [renderMode, onClickStill, onClickSequence, onClickVideo]);
32173
+ }, [
32174
+ onClickSequence,
32175
+ onClickStill,
32176
+ onClickVideo,
32177
+ readOnlyRenderCommand,
32178
+ readOnlyStudio,
32179
+ renderMode
32180
+ ]);
32181
+ useEffect86(() => {
32182
+ if (commandCopiedAt === null) {
32183
+ return;
32184
+ }
32185
+ const timeout = setTimeout(() => {
32186
+ setCommandCopiedAt(null);
32187
+ }, 2000);
32188
+ return () => clearTimeout(timeout);
32189
+ }, [commandCopiedAt]);
31767
32190
  useEffect86(() => {
31768
32191
  if (renderDisabled) {
31769
32192
  return;
@@ -31824,7 +32247,7 @@ var RenderModal = ({
31824
32247
  backgroundColor: outnameValidation.valid ? BLUE : BLUE_DISABLED
31825
32248
  },
31826
32249
  children: [
31827
- state.type === "idle" ? `Render ${renderMode}` : "Rendering...",
32250
+ readOnlyStudio ? commandCopiedAt ? "Copied command!" : "Copy command" : state.type === "idle" ? `Render ${renderMode}` : "Rendering...",
31828
32251
  /* @__PURE__ */ jsx272(ShortcutHint, {
31829
32252
  keyToPress: "↵",
31830
32253
  cmdOrCtrl: true
@@ -31945,6 +32368,7 @@ var RenderModal = ({
31945
32368
  setStartFrame,
31946
32369
  setVerboseLogging: setLogLevel,
31947
32370
  logLevel,
32371
+ showOutputName: !readOnlyStudio,
31948
32372
  startFrame,
31949
32373
  validationMessage: outnameValidation.valid ? null : outnameValidation.error.message
31950
32374
  }) : tab === "picture" ? /* @__PURE__ */ jsx272(RenderModalPicture, {
@@ -32008,7 +32432,7 @@ var RenderModal = ({
32008
32432
  propsEditType: "input-props",
32009
32433
  saving,
32010
32434
  setSaving,
32011
- readOnlyStudio: false
32435
+ readOnlyStudio
32012
32436
  }) : /* @__PURE__ */ jsx272(RenderModalAdvanced, {
32013
32437
  x264Preset,
32014
32438
  setx264Preset,
@@ -32474,6 +32898,7 @@ var WebRenderModalAudio = ({
32474
32898
  const audioBitrateOptions = useMemo139(() => getQualityOptions(audioBitrate, setAudioBitrate), [audioBitrate, setAudioBitrate]);
32475
32899
  const isAudioOnly = renderMode === "audio";
32476
32900
  const showAudioSettings = isAudioOnly || !muted;
32901
+ const showAudioCodecSetting = !isAudioOnly || containerSupported.length > 1;
32477
32902
  return /* @__PURE__ */ jsxs144("div", {
32478
32903
  style: container63,
32479
32904
  className: VERTICAL_SCROLLBAR_CLASSNAME,
@@ -32508,7 +32933,7 @@ var WebRenderModalAudio = ({
32508
32933
  })
32509
32934
  ]
32510
32935
  }),
32511
- /* @__PURE__ */ jsxs144("div", {
32936
+ showAudioCodecSetting ? /* @__PURE__ */ jsxs144("div", {
32512
32937
  style: optionRow,
32513
32938
  children: [
32514
32939
  /* @__PURE__ */ jsxs144("div", {
@@ -32529,8 +32954,8 @@ var WebRenderModalAudio = ({
32529
32954
  })
32530
32955
  })
32531
32956
  ]
32532
- }),
32533
- effectiveAudioCodec !== audioCodec ? /* @__PURE__ */ jsxs144("div", {
32957
+ }) : null,
32958
+ showAudioCodecSetting && effectiveAudioCodec !== audioCodec ? /* @__PURE__ */ jsxs144("div", {
32534
32959
  style: fallbackNoticeStyle,
32535
32960
  children: [
32536
32961
  humanReadableWebAudioCodec(audioCodec),
@@ -34200,7 +34625,8 @@ var Modals = ({ readOnlyStudio }) => {
34200
34625
  modalContextType && modalContextType.type === "web-render" && /* @__PURE__ */ jsx288(WebRenderModalWithLoader, {
34201
34626
  ...modalContextType
34202
34627
  }),
34203
- modalContextType && canRender && modalContextType.type === "server-render" && /* @__PURE__ */ jsx288(RenderModalWithLoader, {
34628
+ modalContextType && modalContextType.type === "server-render" && (canRender || modalContextType.readOnlyStudio) ? /* @__PURE__ */ jsx288(RenderModalWithLoader, {
34629
+ readOnlyStudio: modalContextType.readOnlyStudio ?? false,
34204
34630
  initialFrame: modalContextType.initialFrame,
34205
34631
  initialDarkMode: modalContextType.initialDarkMode,
34206
34632
  compositionId: modalContextType.compositionId,
@@ -34248,7 +34674,7 @@ var Modals = ({ readOnlyStudio }) => {
34248
34674
  initialHardwareAcceleration: modalContextType.initialHardwareAcceleration,
34249
34675
  initialChromeMode: modalContextType.initialChromeMode,
34250
34676
  renderDefaults: modalContextType.renderDefaults
34251
- }),
34677
+ }) : null,
34252
34678
  modalContextType && modalContextType.type === "render-progress" && /* @__PURE__ */ jsx288(RenderStatusModal, {
34253
34679
  jobId: modalContextType.jobId
34254
34680
  }),