@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.
@@ -2851,7 +2851,8 @@ var SidebarRenderButton = ({ composition, visible }) => {
2851
2851
  initialChromeMode: defaults.chromeMode,
2852
2852
  initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes,
2853
2853
  renderDefaults: defaults,
2854
- initialDarkMode: defaults.darkMode
2854
+ initialDarkMode: defaults.darkMode,
2855
+ readOnlyStudio: false
2855
2856
  });
2856
2857
  if (isMobileLayout) {
2857
2858
  setSidebarCollapsedState({ left: "collapsed", right: "collapsed" });
@@ -10814,10 +10815,7 @@ var CurrentCompositionKeybindings = ({ readOnlyStudio }) => {
10814
10815
  if (!video) {
10815
10816
  return;
10816
10817
  }
10817
- if (readOnlyStudio) {
10818
- return showNotification("Studio is read-only", 2000);
10819
- }
10820
- if (type !== "connected") {
10818
+ if (type !== "connected" && !SHOW_BROWSER_RENDERING && !readOnlyStudio) {
10821
10819
  showNotification("Studio server is offline", 2000);
10822
10820
  return;
10823
10821
  }
@@ -17140,7 +17138,8 @@ var makeRetryPayload = (job) => {
17140
17138
  initialHardwareAcceleration: defaults.hardwareAcceleration,
17141
17139
  initialChromeMode: job.chromeMode,
17142
17140
  initialMediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
17143
- renderDefaults: defaults
17141
+ renderDefaults: defaults,
17142
+ readOnlyStudio: false
17144
17143
  };
17145
17144
  }
17146
17145
  if (job.type === "sequence") {
@@ -17192,7 +17191,8 @@ var makeRetryPayload = (job) => {
17192
17191
  initialHardwareAcceleration: defaults.hardwareAcceleration,
17193
17192
  initialChromeMode: job.chromeMode,
17194
17193
  initialMediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
17195
- renderDefaults: defaults
17194
+ renderDefaults: defaults,
17195
+ readOnlyStudio: false
17196
17196
  };
17197
17197
  }
17198
17198
  if (job.type === "video") {
@@ -17244,7 +17244,8 @@ var makeRetryPayload = (job) => {
17244
17244
  initialHardwareAcceleration: job.hardwareAcceleration,
17245
17245
  initialChromeMode: job.chromeMode,
17246
17246
  initialMediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
17247
- renderDefaults: defaults
17247
+ renderDefaults: defaults,
17248
+ readOnlyStudio: false
17248
17249
  };
17249
17250
  }
17250
17251
  throw new Error(`Job ${JSON.stringify(job)} Not implemented`);
@@ -18137,14 +18138,6 @@ var OptionsPanel = ({ readOnlyStudio }) => {
18137
18138
  import { useContext as useContext57, useEffect as useEffect60, useRef as useRef32, useState as useState62 } from "react";
18138
18139
  import { Internals as Internals45 } from "remotion";
18139
18140
 
18140
- // src/helpers/should-show-render-button.ts
18141
- var shouldShowRenderButton = (readOnlyStudio) => {
18142
- if (readOnlyStudio) {
18143
- return SHOW_BROWSER_RENDERING;
18144
- }
18145
- return true;
18146
- };
18147
-
18148
18141
  // src/state/loop.ts
18149
18142
  var key = "remotion.loop";
18150
18143
  var persistLoopOption = (option) => {
@@ -19005,7 +18998,7 @@ var label7 = {
19005
18998
  var RENDER_TYPE_STORAGE_KEY = "remotion.renderType";
19006
18999
  var getInitialRenderType = (readOnlyStudio) => {
19007
19000
  if (!SHOW_BROWSER_RENDERING) {
19008
- return "server-render";
19001
+ return readOnlyStudio ? "render-command" : "server-render";
19009
19002
  }
19010
19003
  if (readOnlyStudio) {
19011
19004
  return "client-render";
@@ -19062,8 +19055,15 @@ var RenderButton = ({
19062
19055
  });
19063
19056
  }, [refresh]);
19064
19057
  const connectionStatus = useContext55(StudioServerConnectionCtx).previewServerState.type;
19065
- const canRender = connectionStatus === "connected" || SHOW_BROWSER_RENDERING;
19058
+ const canServerRender = connectionStatus === "connected";
19059
+ const canRender = canServerRender || SHOW_BROWSER_RENDERING || readOnlyStudio;
19066
19060
  const renderType = useMemo95(() => {
19061
+ if (readOnlyStudio) {
19062
+ if (!SHOW_BROWSER_RENDERING) {
19063
+ return "render-command";
19064
+ }
19065
+ return preferredRenderType === "render-command" ? "render-command" : "client-render";
19066
+ }
19067
19067
  if (connectionStatus === "disconnected" && SHOW_BROWSER_RENDERING) {
19068
19068
  return "client-render";
19069
19069
  }
@@ -19071,9 +19071,9 @@ var RenderButton = ({
19071
19071
  return "server-render";
19072
19072
  }
19073
19073
  return preferredRenderType;
19074
- }, [connectionStatus, preferredRenderType]);
19074
+ }, [connectionStatus, preferredRenderType, readOnlyStudio]);
19075
19075
  const shortcut = areKeyboardShortcutsDisabled() ? "" : "(R)";
19076
- const tooltip = canRender ? "Export the current composition " + shortcut : "Connect to the Studio server to render";
19076
+ 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";
19077
19077
  const iconStyle4 = useMemo95(() => {
19078
19078
  return {
19079
19079
  style: {
@@ -19085,7 +19085,7 @@ var RenderButton = ({
19085
19085
  const video = Internals43.useVideo();
19086
19086
  const getCurrentFrame2 = PlayerInternals14.useFrameImperative();
19087
19087
  const { props } = useContext55(Internals43.EditorPropsContext);
19088
- const openServerRenderModal = useCallback90(() => {
19088
+ const openServerRenderModal = useCallback90((copyCommandOnly) => {
19089
19089
  if (!video) {
19090
19090
  return null;
19091
19091
  }
@@ -19095,6 +19095,7 @@ var RenderButton = ({
19095
19095
  }
19096
19096
  setSelectedModal({
19097
19097
  type: "server-render",
19098
+ readOnlyStudio: copyCommandOnly,
19098
19099
  compositionId: video.id,
19099
19100
  initialFrame: getCurrentFrame2(),
19100
19101
  initialStillImageFormat: defaults.stillImageFormat,
@@ -19178,8 +19179,12 @@ var RenderButton = ({
19178
19179
  });
19179
19180
  }, [video, setSelectedModal, getCurrentFrame2, props, inFrame, outFrame]);
19180
19181
  const onClick = useCallback90(() => {
19182
+ if (renderType === "render-command") {
19183
+ openServerRenderModal(true);
19184
+ return;
19185
+ }
19181
19186
  if (!SHOW_BROWSER_RENDERING || renderType === "server-render") {
19182
- openServerRenderModal();
19187
+ openServerRenderModal(false);
19183
19188
  } else {
19184
19189
  openClientRenderModal();
19185
19190
  }
@@ -19194,12 +19199,40 @@ var RenderButton = ({
19194
19199
  } catch {}
19195
19200
  setDropdownOpened(false);
19196
19201
  if (newType === "server-render") {
19197
- openServerRenderModal();
19202
+ openServerRenderModal(false);
19203
+ } else if (newType === "render-command") {
19204
+ openServerRenderModal(true);
19198
19205
  } else {
19199
19206
  openClientRenderModal();
19200
19207
  }
19201
- }, [openServerRenderModal, openClientRenderModal]);
19208
+ }, [openClientRenderModal, openServerRenderModal]);
19202
19209
  const dropdownValues = useMemo95(() => {
19210
+ if (readOnlyStudio) {
19211
+ return [
19212
+ {
19213
+ type: "item",
19214
+ id: "client-render",
19215
+ label: "Render on web",
19216
+ value: "client-render",
19217
+ onClick: () => handleRenderTypeChange("client-render"),
19218
+ keyHint: null,
19219
+ leftItem: null,
19220
+ subMenu: null,
19221
+ quickSwitcherLabel: null
19222
+ },
19223
+ {
19224
+ type: "item",
19225
+ id: "render-command",
19226
+ label: "Render via CLI",
19227
+ value: "render-command",
19228
+ onClick: () => handleRenderTypeChange("render-command"),
19229
+ keyHint: null,
19230
+ leftItem: null,
19231
+ subMenu: null,
19232
+ quickSwitcherLabel: null
19233
+ }
19234
+ ];
19235
+ }
19203
19236
  return [
19204
19237
  {
19205
19238
  type: "item",
@@ -19224,7 +19257,7 @@ var RenderButton = ({
19224
19257
  quickSwitcherLabel: null
19225
19258
  }
19226
19259
  ];
19227
- }, [handleRenderTypeChange]);
19260
+ }, [handleRenderTypeChange, readOnlyStudio]);
19228
19261
  const spaceToBottom = useMemo95(() => {
19229
19262
  const margin2 = 10;
19230
19263
  if (size4 && dropdownOpened) {
@@ -19266,10 +19299,10 @@ var RenderButton = ({
19266
19299
  cursor: canRender ? "pointer" : "inherit"
19267
19300
  };
19268
19301
  }, [canRender]);
19269
- const renderLabel = renderType === "server-render" ? "Render" : "Render on web";
19302
+ const renderLabel = renderType === "server-render" ? "Render" : renderType === "render-command" ? "Render via CLI" : "Render on web";
19270
19303
  const shouldShowDropdown = useMemo95(() => {
19271
19304
  if (readOnlyStudio) {
19272
- return false;
19305
+ return SHOW_BROWSER_RENDERING;
19273
19306
  }
19274
19307
  if (!SHOW_BROWSER_RENDERING) {
19275
19308
  return false;
@@ -19284,8 +19317,8 @@ var RenderButton = ({
19284
19317
  /* @__PURE__ */ jsx177("button", {
19285
19318
  style: { display: "none" },
19286
19319
  id: "render-modal-button-server",
19287
- disabled: !canRender,
19288
- onClick: openServerRenderModal,
19320
+ disabled: !canServerRender,
19321
+ onClick: () => openServerRenderModal(false),
19289
19322
  type: "button"
19290
19323
  }),
19291
19324
  " ",
@@ -19333,7 +19366,7 @@ var RenderButton = ({
19333
19366
  ref: dropdownRef,
19334
19367
  type: "button",
19335
19368
  style: dropdownTriggerStyle,
19336
- disabled: connectionStatus !== "connected",
19369
+ disabled: !readOnlyStudio && connectionStatus !== "connected",
19337
19370
  className: MENU_INITIATOR_CLASSNAME,
19338
19371
  onPointerDown,
19339
19372
  onClick: onClickDropdown,
@@ -19672,9 +19705,9 @@ var PreviewToolbar = ({ readOnlyStudio, bufferStateDelayInMilliseconds }) => {
19672
19705
  /* @__PURE__ */ jsx180(Spacing, {
19673
19706
  x: 2
19674
19707
  }),
19675
- shouldShowRenderButton(readOnlyStudio) ? /* @__PURE__ */ jsx180(RenderButton, {
19708
+ /* @__PURE__ */ jsx180(RenderButton, {
19676
19709
  readOnlyStudio
19677
- }) : null,
19710
+ }),
19678
19711
  /* @__PURE__ */ jsx180(Spacing, {
19679
19712
  x: 1.5
19680
19713
  })
@@ -27277,7 +27310,7 @@ var RenderStatusModal = ({
27277
27310
  };
27278
27311
 
27279
27312
  // src/components/RenderModal/ServerRenderModal.tsx
27280
- import { BrowserSafeApis as BrowserSafeApis10 } from "@remotion/renderer/client";
27313
+ import { BrowserSafeApis as BrowserSafeApis11 } from "@remotion/renderer/client";
27281
27314
  import { getDefaultOutLocation } from "@remotion/studio-shared";
27282
27315
  import {
27283
27316
  useCallback as useCallback136,
@@ -27303,6 +27336,252 @@ var envVariablesArrayToObject = (envVariables) => {
27303
27336
  }, {});
27304
27337
  };
27305
27338
 
27339
+ // src/helpers/make-render-command.ts
27340
+ import { BrowserSafeApis } from "@remotion/renderer/client";
27341
+ var shellQuote = (value) => {
27342
+ return `'${value.replace(/'/g, "'\\''")}'`;
27343
+ };
27344
+ var addFlagWithValue = (flags, flag, value) => {
27345
+ if (value === null || value === undefined) {
27346
+ return;
27347
+ }
27348
+ flags.push(`--${flag}=${shellQuote(String(value))}`);
27349
+ };
27350
+ var addBooleanFlag = (flags, flag, value) => {
27351
+ if (value) {
27352
+ flags.push(`--${flag}`);
27353
+ }
27354
+ };
27355
+ var valueFlag = (flag, value, defaultValue) => {
27356
+ return { flag, value, defaultValue };
27357
+ };
27358
+ var booleanFlag = (flag, value, defaultValue) => {
27359
+ return { flag, value, defaultValue };
27360
+ };
27361
+ var addValueFlagsIfChanged = (flags, mappings) => {
27362
+ for (const mapping of mappings) {
27363
+ if (mapping.value !== mapping.defaultValue) {
27364
+ addFlagWithValue(flags, mapping.flag, mapping.value);
27365
+ }
27366
+ }
27367
+ };
27368
+ var addTrueBooleanFlagsIfChanged = (flags, mappings) => {
27369
+ for (const mapping of mappings) {
27370
+ if (mapping.value && mapping.value !== mapping.defaultValue) {
27371
+ addBooleanFlag(flags, mapping.flag, true);
27372
+ }
27373
+ }
27374
+ };
27375
+ var getNpmRemotionCommandPrefix = (version) => {
27376
+ return version.trim() === "" ? "npx --yes --location=global -p @remotion/cli remotion" : `npx --yes --location=global -p @remotion/cli@${version} remotion`;
27377
+ };
27378
+ var normalizeServeUrlForRenderCommand = ({
27379
+ locationHref,
27380
+ compositionId
27381
+ }) => {
27382
+ const parsed = new URL(locationHref);
27383
+ parsed.hash = "";
27384
+ parsed.search = "";
27385
+ const suffix2 = `/${compositionId}`;
27386
+ if (parsed.pathname === suffix2) {
27387
+ parsed.pathname = "/";
27388
+ } else if (parsed.pathname.endsWith(suffix2)) {
27389
+ const basePath = parsed.pathname.slice(0, -suffix2.length);
27390
+ parsed.pathname = basePath === "" ? "/" : basePath;
27391
+ }
27392
+ if (parsed.pathname !== "/" && parsed.pathname.endsWith("/")) {
27393
+ parsed.pathname = parsed.pathname.slice(0, -1);
27394
+ }
27395
+ return `${parsed.origin}${parsed.pathname === "/" ? "" : parsed.pathname}`;
27396
+ };
27397
+ var trimDefaultOutPrefix = (outName) => {
27398
+ if (outName.startsWith("out/")) {
27399
+ const trimmed = outName.slice("out/".length);
27400
+ return trimmed.length === 0 ? outName : trimmed;
27401
+ }
27402
+ if (outName.startsWith("./out/")) {
27403
+ const trimmed = outName.slice("./out/".length);
27404
+ return trimmed.length === 0 ? outName : trimmed;
27405
+ }
27406
+ return outName;
27407
+ };
27408
+ var getRenderMediaFlag = (option) => {
27409
+ return BrowserSafeApis.optionsMap.renderMedia[option].cliFlag;
27410
+ };
27411
+ var renderMediaValueFlag = (option, value, defaultValue) => {
27412
+ return valueFlag(getRenderMediaFlag(option), value, defaultValue);
27413
+ };
27414
+ var renderMediaBooleanFlag = (option, value, defaultValue) => {
27415
+ return booleanFlag(getRenderMediaFlag(option), value, defaultValue);
27416
+ };
27417
+ var makeReadOnlyStudioRenderCommand = ({
27418
+ remotionVersion,
27419
+ locationHref,
27420
+ compositionId,
27421
+ outName,
27422
+ renderMode,
27423
+ renderDefaults,
27424
+ durationInFrames,
27425
+ concurrency,
27426
+ frame: frame2,
27427
+ startFrame,
27428
+ endFrame,
27429
+ stillImageFormat,
27430
+ sequenceImageFormat,
27431
+ videoImageFormat,
27432
+ jpegQuality,
27433
+ codec,
27434
+ muted,
27435
+ enforceAudioTrack,
27436
+ proResProfile,
27437
+ x264Preset,
27438
+ pixelFormat,
27439
+ crf,
27440
+ videoBitrate,
27441
+ audioBitrate,
27442
+ audioCodec,
27443
+ everyNthFrame,
27444
+ numberOfGifLoops,
27445
+ disallowParallelEncoding,
27446
+ encodingBufferSize,
27447
+ encodingMaxRate,
27448
+ forSeamlessAacConcatenation,
27449
+ separateAudioTo,
27450
+ colorSpace,
27451
+ scale,
27452
+ logLevel,
27453
+ delayRenderTimeout,
27454
+ hardwareAcceleration,
27455
+ chromeMode,
27456
+ headless,
27457
+ disableWebSecurity,
27458
+ ignoreCertificateErrors,
27459
+ gl,
27460
+ userAgent,
27461
+ multiProcessOnLinux,
27462
+ darkMode,
27463
+ offthreadVideoCacheSizeInBytes,
27464
+ offthreadVideoThreads,
27465
+ mediaCacheSizeInBytes,
27466
+ beepOnFinish,
27467
+ repro,
27468
+ metadata,
27469
+ envVariables,
27470
+ inputProps
27471
+ }) => {
27472
+ const serveUrl = normalizeServeUrlForRenderCommand({
27473
+ locationHref,
27474
+ compositionId
27475
+ });
27476
+ const isStillRender = renderMode === "still";
27477
+ const isSequenceRender = renderMode === "sequence";
27478
+ const hasCodecSpecificOptions = !isSequenceRender;
27479
+ const commandType = isStillRender ? "still" : "render";
27480
+ const command = getNpmRemotionCommandPrefix(remotionVersion);
27481
+ const { options } = BrowserSafeApis;
27482
+ const flags = [];
27483
+ addValueFlagsIfChanged(flags, [
27484
+ valueFlag(options.scaleOption.cliFlag, scale, renderDefaults.scale),
27485
+ renderMediaValueFlag("logLevel", logLevel, renderDefaults.logLevel),
27486
+ renderMediaValueFlag("timeoutInMilliseconds", delayRenderTimeout, renderDefaults.delayRenderTimeout),
27487
+ renderMediaValueFlag("chromeMode", chromeMode, renderDefaults.chromeMode),
27488
+ valueFlag(options.glOption.cliFlag, gl, renderDefaults.openGlRenderer),
27489
+ valueFlag(options.userAgentOption.cliFlag, userAgent, renderDefaults.userAgent),
27490
+ renderMediaValueFlag("offthreadVideoCacheSizeInBytes", offthreadVideoCacheSizeInBytes, renderDefaults.offthreadVideoCacheSizeInBytes),
27491
+ renderMediaValueFlag("offthreadVideoThreads", offthreadVideoThreads, renderDefaults.offthreadVideoThreads),
27492
+ renderMediaValueFlag("mediaCacheSizeInBytes", mediaCacheSizeInBytes, renderDefaults.mediaCacheSizeInBytes)
27493
+ ]);
27494
+ if (headless !== renderDefaults.headless) {
27495
+ addFlagWithValue(flags, options.headlessOption.cliFlag, !headless);
27496
+ }
27497
+ addTrueBooleanFlagsIfChanged(flags, [
27498
+ booleanFlag(options.disableWebSecurityOption.cliFlag, disableWebSecurity, renderDefaults.disableWebSecurity),
27499
+ booleanFlag(options.ignoreCertificateErrorsOption.cliFlag, ignoreCertificateErrors, renderDefaults.ignoreCertificateErrors),
27500
+ booleanFlag(options.enableMultiprocessOnLinuxOption.cliFlag, multiProcessOnLinux, renderDefaults.multiProcessOnLinux),
27501
+ booleanFlag(options.darkModeOption.cliFlag, darkMode, renderDefaults.darkMode),
27502
+ booleanFlag(options.beepOnFinishOption.cliFlag, beepOnFinish, renderDefaults.beepOnFinish)
27503
+ ]);
27504
+ if (isStillRender) {
27505
+ addValueFlagsIfChanged(flags, [
27506
+ valueFlag(options.stillFrameOption.cliFlag, frame2, 0),
27507
+ valueFlag(options.stillImageFormatOption.cliFlag, stillImageFormat, renderDefaults.stillImageFormat),
27508
+ valueFlag(options.jpegQualityOption.cliFlag, jpegQuality, renderDefaults.jpegQuality)
27509
+ ]);
27510
+ } else {
27511
+ addValueFlagsIfChanged(flags, [
27512
+ valueFlag(options.concurrencyOption.cliFlag, concurrency, renderDefaults.concurrency)
27513
+ ]);
27514
+ if (isSequenceRender) {
27515
+ addBooleanFlag(flags, options.imageSequenceOption.cliFlag, true);
27516
+ if (sequenceImageFormat !== "jpeg") {
27517
+ addFlagWithValue(flags, options.videoImageFormatOption.cliFlag, sequenceImageFormat);
27518
+ }
27519
+ } else {
27520
+ addValueFlagsIfChanged(flags, [
27521
+ valueFlag(options.videoImageFormatOption.cliFlag, videoImageFormat, renderDefaults.videoImageFormat),
27522
+ renderMediaValueFlag("hardwareAcceleration", hardwareAcceleration, renderDefaults.hardwareAcceleration)
27523
+ ]);
27524
+ }
27525
+ if (hasCodecSpecificOptions && codec !== renderDefaults.codec) {
27526
+ addFlagWithValue(flags, getRenderMediaFlag("codec"), codec);
27527
+ }
27528
+ if (startFrame !== 0 || endFrame !== durationInFrames - 1) {
27529
+ addFlagWithValue(flags, options.framesOption.cliFlag, `${startFrame}-${endFrame}`);
27530
+ }
27531
+ if (hasCodecSpecificOptions) {
27532
+ addTrueBooleanFlagsIfChanged(flags, [
27533
+ renderMediaBooleanFlag("muted", muted, renderDefaults.muted),
27534
+ booleanFlag(options.enforceAudioOption.cliFlag, enforceAudioTrack, renderDefaults.enforceAudioTrack),
27535
+ renderMediaBooleanFlag("forSeamlessAacConcatenation", forSeamlessAacConcatenation, renderDefaults.forSeamlessAacConcatenation)
27536
+ ]);
27537
+ addValueFlagsIfChanged(flags, [
27538
+ valueFlag(options.pixelFormatOption.cliFlag, pixelFormat, renderDefaults.pixelFormat),
27539
+ renderMediaValueFlag("colorSpace", colorSpace, renderDefaults.colorSpace),
27540
+ valueFlag(options.proResProfileOption.cliFlag, proResProfile, renderDefaults.proResProfile),
27541
+ renderMediaValueFlag("x264Preset", x264Preset, renderDefaults.x264Preset),
27542
+ valueFlag(options.crfOption.cliFlag, crf, null),
27543
+ valueFlag(options.jpegQualityOption.cliFlag, jpegQuality, renderDefaults.jpegQuality),
27544
+ renderMediaValueFlag("videoBitrate", videoBitrate, renderDefaults.videoBitrate),
27545
+ renderMediaValueFlag("audioBitrate", audioBitrate, renderDefaults.audioBitrate),
27546
+ valueFlag(options.everyNthFrameOption.cliFlag, everyNthFrame, renderDefaults.everyNthFrame),
27547
+ renderMediaValueFlag("numberOfGifLoops", numberOfGifLoops, renderDefaults.numberOfGifLoops),
27548
+ renderMediaValueFlag("encodingBufferSize", encodingBufferSize, renderDefaults.encodingBufferSize),
27549
+ renderMediaValueFlag("encodingMaxRate", encodingMaxRate, renderDefaults.encodingMaxRate),
27550
+ renderMediaValueFlag("separateAudioTo", separateAudioTo, null)
27551
+ ]);
27552
+ const defaultAudioCodec = BrowserSafeApis.defaultAudioCodecs[codec]?.compressed;
27553
+ if (audioCodec !== defaultAudioCodec) {
27554
+ addFlagWithValue(flags, getRenderMediaFlag("audioCodec"), audioCodec);
27555
+ }
27556
+ }
27557
+ addTrueBooleanFlagsIfChanged(flags, [
27558
+ renderMediaBooleanFlag("disallowParallelEncoding", disallowParallelEncoding, false),
27559
+ renderMediaBooleanFlag("repro", repro, renderDefaults.repro)
27560
+ ]);
27561
+ }
27562
+ if (metadata) {
27563
+ for (const [key5, value] of Object.entries(metadata)) {
27564
+ addFlagWithValue(flags, options.metadataOption.cliFlag, `${key5}=${value}`);
27565
+ }
27566
+ }
27567
+ if (Object.keys(inputProps).length > 0) {
27568
+ addFlagWithValue(flags, options.propsOption.cliFlag, JSON.stringify(inputProps));
27569
+ }
27570
+ const envArgs = Object.entries(envVariables).sort(([a], [b]) => a.localeCompare(b)).map(([key5, value]) => shellQuote(`${key5}=${value}`));
27571
+ const renderCommand = [
27572
+ command,
27573
+ commandType,
27574
+ shellQuote(serveUrl),
27575
+ shellQuote(compositionId),
27576
+ shellQuote(trimDefaultOutPrefix(outName)),
27577
+ ...flags
27578
+ ].join(" ");
27579
+ if (envArgs.length === 0) {
27580
+ return renderCommand;
27581
+ }
27582
+ return ["env", ...envArgs, renderCommand].join(" ");
27583
+ };
27584
+
27306
27585
  // src/helpers/render-modal-sections.ts
27307
27586
  import { useMemo as useMemo128, useState as useState83 } from "react";
27308
27587
  var useRenderModalSections = (renderMode, codec) => {
@@ -27444,14 +27723,14 @@ var VerticalTab = ({ children, onClick, style: style12, selected }) => {
27444
27723
  };
27445
27724
 
27446
27725
  // src/components/RenderModal/CrfSetting.tsx
27447
- import { BrowserSafeApis as BrowserSafeApis2 } from "@remotion/renderer/client";
27726
+ import { BrowserSafeApis as BrowserSafeApis3 } from "@remotion/renderer/client";
27448
27727
  import { useState as useState86 } from "react";
27449
27728
 
27450
27729
  // src/components/RenderModal/NumberSetting.tsx
27451
27730
  import { useCallback as useCallback121 } from "react";
27452
27731
 
27453
27732
  // src/components/RenderModal/OptionExplainerBubble.tsx
27454
- import { BrowserSafeApis } from "@remotion/renderer/client";
27733
+ import { BrowserSafeApis as BrowserSafeApis2 } from "@remotion/renderer/client";
27455
27734
 
27456
27735
  // src/components/RenderModal/CliCopyButton.tsx
27457
27736
  import { useCallback as useCallback120, useEffect as useEffect83, useMemo as useMemo130, useState as useState85 } from "react";
@@ -27678,7 +27957,7 @@ var OptionExplainer = ({ option }) => {
27678
27957
  // src/components/RenderModal/OptionExplainerBubble.tsx
27679
27958
  import { jsx as jsx248 } from "react/jsx-runtime";
27680
27959
  var OptionExplainerBubble = ({ id }) => {
27681
- const option = BrowserSafeApis.options[id];
27960
+ const option = BrowserSafeApis2.options[id];
27682
27961
  return /* @__PURE__ */ jsx248(InfoBubble, {
27683
27962
  title: "Learn more about this option",
27684
27963
  children: /* @__PURE__ */ jsx248(OptionExplainer, {
@@ -27746,8 +28025,8 @@ var NumberSetting = ({ name, value, step, hint, onValueChanged, max, min, format
27746
28025
  // src/components/RenderModal/CrfSetting.tsx
27747
28026
  import { jsx as jsx250 } from "react/jsx-runtime";
27748
28027
  var getDefaultCrfState = () => {
27749
- return BrowserSafeApis2.validCodecs.map((c) => {
27750
- return [c, BrowserSafeApis2.getDefaultCrfForCodec(c)];
28028
+ return BrowserSafeApis3.validCodecs.map((c) => {
28029
+ return [c, BrowserSafeApis3.getDefaultCrfForCodec(c)];
27751
28030
  }).reduce((acc, [codec, crf]) => {
27752
28031
  return {
27753
28032
  ...acc,
@@ -27757,7 +28036,7 @@ var getDefaultCrfState = () => {
27757
28036
  };
27758
28037
  var useCrfState = (codec) => {
27759
28038
  const [state, setState] = useState86(() => getDefaultCrfState());
27760
- const range = BrowserSafeApis2.getValidCrfRanges(codec);
28039
+ const range = BrowserSafeApis3.getValidCrfRanges(codec);
27761
28040
  const setCrf = (updater) => {
27762
28041
  setState((q) => {
27763
28042
  const val = q[codec];
@@ -27790,7 +28069,7 @@ var CrfSetting = ({ crf, setCrf, min, max, option }) => {
27790
28069
  };
27791
28070
 
27792
28071
  // src/components/RenderModal/get-default-codecs.ts
27793
- import { BrowserSafeApis as BrowserSafeApis3 } from "@remotion/renderer/client";
28072
+ import { BrowserSafeApis as BrowserSafeApis4 } from "@remotion/renderer/client";
27794
28073
  import { NoReactAPIs } from "@remotion/renderer/pure";
27795
28074
  var getDefaultCodecs = ({
27796
28075
  defaultConfigurationVideoCodec,
@@ -27810,7 +28089,7 @@ var getDefaultCodecs = ({
27810
28089
  initialVideoCodecForVideoTab: NoReactAPIs.isAudioCodec(defaultConfigurationVideoCodec) ? "h264" : defaultConfigurationVideoCodec
27811
28090
  };
27812
28091
  }
27813
- const suitableAudioCodecForVideoCodec = BrowserSafeApis3.defaultAudioCodecs[userPreferredVideoCodec].compressed;
28092
+ const suitableAudioCodecForVideoCodec = BrowserSafeApis4.defaultAudioCodecs[userPreferredVideoCodec].compressed;
27814
28093
  return {
27815
28094
  initialAudioCodec: defaultConfigurationAudioCodec ?? suitableAudioCodecForVideoCodec,
27816
28095
  initialVideoCodec: userPreferredVideoCodec,
@@ -27830,7 +28109,7 @@ var getStringBeforeSuffix = (fileName) => {
27830
28109
  };
27831
28110
 
27832
28111
  // src/components/RenderModal/out-name-checker.ts
27833
- import { BrowserSafeApis as BrowserSafeApis4 } from "@remotion/renderer/client";
28112
+ import { BrowserSafeApis as BrowserSafeApis5 } from "@remotion/renderer/client";
27834
28113
  var invalidCharacters = ["?", "*", "+", ":", "%"];
27835
28114
  var isValidStillExtension = (extension, stillImageFormat) => {
27836
28115
  if (stillImageFormat === "jpeg" && extension === "jpg") {
@@ -27870,8 +28149,8 @@ var isValidOutName = ({
27870
28149
  }) => {
27871
28150
  const extension = outName.substring(outName.lastIndexOf(".") + 1);
27872
28151
  const prefix = outName.substring(0, outName.lastIndexOf("."));
27873
- const map = BrowserSafeApis4.defaultFileExtensionMap[codec];
27874
- if (BrowserSafeApis4.supportedAudioCodecs[codec].length > 0 && !(audioCodec in map.forAudioCodec)) {
28152
+ const map = BrowserSafeApis5.defaultFileExtensionMap[codec];
28153
+ if (BrowserSafeApis5.supportedAudioCodecs[codec].length > 0 && !(audioCodec in map.forAudioCodec)) {
27875
28154
  throw new Error(`Audio codec ${audioCodec} is not supported for codec ${codec}`);
27876
28155
  }
27877
28156
  const hasDotAfterSlash = () => {
@@ -27887,7 +28166,7 @@ var isValidOutName = ({
27887
28166
  return prefix.split("").some((char) => invalidCharacters.includes(char));
27888
28167
  };
27889
28168
  if (renderMode === "video" || renderMode === "audio") {
27890
- BrowserSafeApis4.validateOutputFilename({
28169
+ BrowserSafeApis5.validateOutputFilename({
27891
28170
  codec,
27892
28171
  audioCodecSetting: audioCodec ?? null,
27893
28172
  extension,
@@ -27975,7 +28254,7 @@ var flexer = {
27975
28254
  };
27976
28255
 
27977
28256
  // src/components/RenderModal/RenderModalAdvanced.tsx
27978
- import { BrowserSafeApis as BrowserSafeApis5 } from "@remotion/renderer/client";
28257
+ import { BrowserSafeApis as BrowserSafeApis6 } from "@remotion/renderer/client";
27979
28258
  import { useCallback as useCallback125, useMemo as useMemo131 } from "react";
27980
28259
 
27981
28260
  // src/helpers/presets-labels.ts
@@ -28408,7 +28687,7 @@ var RenderModalAdvanced = ({
28408
28687
  });
28409
28688
  }, [extendedOpenGlOptions, openGlOption, setOpenGlOption]);
28410
28689
  const chromeModeOptions = useMemo131(() => {
28411
- return BrowserSafeApis5.validChromeModeOptions.map((option) => {
28690
+ return BrowserSafeApis6.validChromeModeOptions.map((option) => {
28412
28691
  return {
28413
28692
  label: option,
28414
28693
  onClick: () => setChromeModeOption(option),
@@ -28424,7 +28703,7 @@ var RenderModalAdvanced = ({
28424
28703
  });
28425
28704
  }, [chromeModeOption, setChromeModeOption]);
28426
28705
  const x264PresetOptions = useMemo131(() => {
28427
- return BrowserSafeApis5.x264PresetOptions.map((option) => {
28706
+ return BrowserSafeApis6.x264PresetOptions.map((option) => {
28428
28707
  return {
28429
28708
  label: labelx264Preset(option),
28430
28709
  onClick: () => setx264Preset(option),
@@ -28440,7 +28719,7 @@ var RenderModalAdvanced = ({
28440
28719
  });
28441
28720
  }, [setx264Preset, x264Preset]);
28442
28721
  const hardwareAccelerationValues = useMemo131(() => {
28443
- return BrowserSafeApis5.hardwareAccelerationOptions.map((option) => {
28722
+ return BrowserSafeApis6.hardwareAccelerationOptions.map((option) => {
28444
28723
  return {
28445
28724
  label: option,
28446
28725
  onClick: () => setHardwareAcceleration(option),
@@ -28931,7 +29210,7 @@ var RenderModalAdvanced = ({
28931
29210
  };
28932
29211
 
28933
29212
  // src/components/RenderModal/RenderModalAudio.tsx
28934
- import { BrowserSafeApis as BrowserSafeApis7 } from "@remotion/renderer/client";
29213
+ import { BrowserSafeApis as BrowserSafeApis8 } from "@remotion/renderer/client";
28935
29214
  import { useCallback as useCallback129 } from "react";
28936
29215
 
28937
29216
  // src/components/RenderModal/EnforceAudioTrackSetting.tsx
@@ -29024,7 +29303,7 @@ var MutedSetting = ({ muted, setMuted, enforceAudioTrack }) => {
29024
29303
  };
29025
29304
 
29026
29305
  // src/components/RenderModal/SeparateAudioOption.tsx
29027
- import { BrowserSafeApis as BrowserSafeApis6 } from "@remotion/renderer/client";
29306
+ import { BrowserSafeApis as BrowserSafeApis7 } from "@remotion/renderer/client";
29028
29307
  import { useCallback as useCallback128, useMemo as useMemo132 } from "react";
29029
29308
 
29030
29309
  // src/helpers/use-file-existence.ts
@@ -29159,7 +29438,7 @@ var SeparateAudioOptionInput = ({ separateAudioTo, setSeparateAudioTo, audioCode
29159
29438
  setSeparateAudioTo(e.target.value);
29160
29439
  }, [setSeparateAudioTo]);
29161
29440
  const validationMessage = useMemo132(() => {
29162
- const expectedExtension = BrowserSafeApis6.getExtensionFromAudioCodec(audioCodec);
29441
+ const expectedExtension = BrowserSafeApis7.getExtensionFromAudioCodec(audioCodec);
29163
29442
  const actualExtension = separateAudioTo.split(".").pop();
29164
29443
  if (actualExtension !== expectedExtension) {
29165
29444
  return `Expected extension: .${expectedExtension}`;
@@ -29178,7 +29457,7 @@ var SeparateAudioOptionInput = ({ separateAudioTo, setSeparateAudioTo, audioCode
29178
29457
  var SeparateAudioOption = ({ separateAudioTo, setSeparateAudioTo, audioCodec, outName }) => {
29179
29458
  const onSeparateAudioChange = useCallback128((e) => {
29180
29459
  if (e.target.checked) {
29181
- const extension = BrowserSafeApis6.getExtensionFromAudioCodec(audioCodec);
29460
+ const extension = BrowserSafeApis7.getExtensionFromAudioCodec(audioCodec);
29182
29461
  setSeparateAudioTo(`${getStringBeforeSuffix(outName)}.${extension}`);
29183
29462
  } else {
29184
29463
  setSeparateAudioTo(null);
@@ -29256,7 +29535,7 @@ var RenderModalAudio = ({
29256
29535
  setForSeamlessAacConcatenation(e.target.checked);
29257
29536
  }, [setForSeamlessAacConcatenation]);
29258
29537
  const audioCodecOptions = useCallback129((currentCodec) => {
29259
- return BrowserSafeApis7.supportedAudioCodecs[currentCodec].map((audioCodecOption) => {
29538
+ return BrowserSafeApis8.supportedAudioCodecs[currentCodec].map((audioCodecOption) => {
29260
29539
  return {
29261
29540
  label: humanReadableAudioCodec(audioCodecOption),
29262
29541
  onClick: () => setAudioCodec(audioCodecOption),
@@ -29401,7 +29680,7 @@ var RenderModalAudio = ({
29401
29680
  };
29402
29681
 
29403
29682
  // src/components/RenderModal/RenderModalBasic.tsx
29404
- import { BrowserSafeApis as BrowserSafeApis8 } from "@remotion/renderer/client";
29683
+ import { BrowserSafeApis as BrowserSafeApis9 } from "@remotion/renderer/client";
29405
29684
  import { NoReactAPIs as NoReactAPIs2 } from "@remotion/renderer/pure";
29406
29685
  import { useCallback as useCallback132, useMemo as useMemo134 } from "react";
29407
29686
 
@@ -29662,11 +29941,12 @@ var RenderModalBasic = ({
29662
29941
  startFrame,
29663
29942
  validationMessage,
29664
29943
  setVerboseLogging,
29665
- logLevel
29944
+ logLevel,
29945
+ showOutputName
29666
29946
  }) => {
29667
29947
  const existence = useFileExistence(outName);
29668
29948
  const videoCodecOptions = useMemo134(() => {
29669
- return BrowserSafeApis8.validCodecs.filter((c) => {
29949
+ return BrowserSafeApis9.validCodecs.filter((c) => {
29670
29950
  return NoReactAPIs2.isAudioCodec(c) === (renderMode === "audio");
29671
29951
  }).map((codecOption) => {
29672
29952
  return {
@@ -29684,7 +29964,7 @@ var RenderModalBasic = ({
29684
29964
  });
29685
29965
  }, [renderMode, setCodec, codec]);
29686
29966
  const proResProfileOptions = useMemo134(() => {
29687
- return BrowserSafeApis8.proResProfileOptions.map((option) => {
29967
+ return BrowserSafeApis9.proResProfileOptions.map((option) => {
29688
29968
  return {
29689
29969
  label: labelProResProfile(option),
29690
29970
  onClick: () => setProResProfile(option),
@@ -29823,14 +30103,14 @@ var RenderModalBasic = ({
29823
30103
  setStartFrame,
29824
30104
  startFrame
29825
30105
  }),
29826
- /* @__PURE__ */ jsx263(RenderModalOutputName, {
30106
+ showOutputName ? /* @__PURE__ */ jsx263(RenderModalOutputName, {
29827
30107
  existence,
29828
30108
  inputStyle: input,
29829
30109
  outName,
29830
30110
  onValueChange,
29831
30111
  validationMessage,
29832
30112
  label: renderMode === "sequence" ? "Folder name" : "Output name"
29833
- }),
30113
+ }) : null,
29834
30114
  /* @__PURE__ */ jsxs135("div", {
29835
30115
  style: optionRow,
29836
30116
  children: [
@@ -29968,7 +30248,7 @@ var RenderModalGif = ({
29968
30248
  };
29969
30249
 
29970
30250
  // src/components/RenderModal/RenderModalPicture.tsx
29971
- import { BrowserSafeApis as BrowserSafeApis9 } from "@remotion/renderer/client";
30251
+ import { BrowserSafeApis as BrowserSafeApis10 } from "@remotion/renderer/client";
29972
30252
  import { useCallback as useCallback135, useMemo as useMemo136 } from "react";
29973
30253
 
29974
30254
  // src/components/RenderModal/JpegQualitySetting.tsx
@@ -30072,7 +30352,7 @@ var RenderModalPicture = ({
30072
30352
  compositionHeight
30073
30353
  }) => {
30074
30354
  const colorSpaceOptions = useMemo136(() => {
30075
- return BrowserSafeApis9.validColorSpaces.map((option) => {
30355
+ return BrowserSafeApis10.validColorSpaces.map((option) => {
30076
30356
  return {
30077
30357
  label: option,
30078
30358
  onClick: () => setColorSpace(option),
@@ -30365,6 +30645,7 @@ var reducer2 = (state, action) => {
30365
30645
  return state;
30366
30646
  };
30367
30647
  var RenderModal = ({
30648
+ readOnlyStudio,
30368
30649
  initialFrame,
30369
30650
  initialVideoImageFormat,
30370
30651
  initialStillImageFormat,
@@ -30455,7 +30736,7 @@ var RenderModal = ({
30455
30736
  const [initialOutName] = useState88(() => {
30456
30737
  return getDefaultOutLocation({
30457
30738
  compositionName: resolvedComposition.id,
30458
- defaultExtension: initialRenderType === "still" ? initialStillImageFormat : isVideo ? BrowserSafeApis10.getFileExtensionFromCodec(initialVideoCodec, initialAudioCodec) : initialStillImageFormat,
30739
+ defaultExtension: initialRenderType === "still" ? initialStillImageFormat : isVideo ? BrowserSafeApis11.getFileExtensionFromCodec(initialVideoCodec, initialAudioCodec) : initialStillImageFormat,
30459
30740
  type: "asset",
30460
30741
  compositionDefaultOutName: resolvedComposition.defaultOutName,
30461
30742
  outputLocation: renderDefaults.outputLocation
@@ -30540,8 +30821,8 @@ var RenderModal = ({
30540
30821
  }
30541
30822
  return null;
30542
30823
  }, [customTargetAudioBitrate, shouldHaveCustomTargetAudioBitrate]);
30543
- const supportsCrf = BrowserSafeApis10.codecSupportsCrf(codec);
30544
- const supportsVideoBitrate = BrowserSafeApis10.codecSupportsVideoBitrate(codec);
30824
+ const supportsCrf = BrowserSafeApis11.codecSupportsCrf(codec);
30825
+ const supportsVideoBitrate = BrowserSafeApis11.codecSupportsVideoBitrate(codec);
30545
30826
  const supportsBothQualityControls = useMemo137(() => {
30546
30827
  return supportsCrf && supportsVideoBitrate && hardwareAcceleration !== "if-possible" && hardwareAcceleration !== "required";
30547
30828
  }, [hardwareAcceleration, supportsCrf, supportsVideoBitrate]);
@@ -30627,10 +30908,10 @@ var RenderModal = ({
30627
30908
  return Math.max(0, Math.min(resolvedComposition.durationInFrames - 1, parsed));
30628
30909
  }, [resolvedComposition.durationInFrames, unclampedFrame]);
30629
30910
  const deriveFinalAudioCodec = useCallback136((passedVideoCodec, passedAudioCodec) => {
30630
- if (passedAudioCodec !== null && BrowserSafeApis10.supportedAudioCodecs[passedVideoCodec].includes(passedAudioCodec)) {
30911
+ if (passedAudioCodec !== null && BrowserSafeApis11.supportedAudioCodecs[passedVideoCodec].includes(passedAudioCodec)) {
30631
30912
  return passedAudioCodec;
30632
30913
  }
30633
- return BrowserSafeApis10.defaultAudioCodecs[passedVideoCodec].compressed;
30914
+ return BrowserSafeApis11.defaultAudioCodecs[passedVideoCodec].compressed;
30634
30915
  }, []);
30635
30916
  const setDefaultOutName = useCallback136((options) => {
30636
30917
  if (options.type === "still") {
@@ -30645,7 +30926,7 @@ var RenderModal = ({
30645
30926
  });
30646
30927
  } else {
30647
30928
  setOutName((prev) => {
30648
- const codecSuffix = BrowserSafeApis10.getFileExtensionFromCodec(options.codec, deriveFinalAudioCodec(options.codec, options.audioCodec));
30929
+ const codecSuffix = BrowserSafeApis11.getFileExtensionFromCodec(options.codec, deriveFinalAudioCodec(options.codec, options.audioCodec));
30649
30930
  const newFileName = getStringBeforeSuffix(prev) + "." + codecSuffix;
30650
30931
  return newFileName;
30651
30932
  });
@@ -30662,7 +30943,7 @@ var RenderModal = ({
30662
30943
  if (prev === null) {
30663
30944
  return null;
30664
30945
  }
30665
- const newExtension = BrowserSafeApis10.getExtensionFromAudioCodec(newAudioCodec);
30946
+ const newExtension = BrowserSafeApis11.getExtensionFromAudioCodec(newAudioCodec);
30666
30947
  const newFileName = getStringBeforeSuffix(prev) + "." + newExtension;
30667
30948
  return newFileName;
30668
30949
  });
@@ -30751,7 +31032,7 @@ var RenderModal = ({
30751
31032
  }, [codec, everyNthFrameSetting]);
30752
31033
  const audioCodec = deriveFinalAudioCodec(codec, userSelectedAudioCodec);
30753
31034
  const availablePixelFormats = useMemo137(() => {
30754
- return BrowserSafeApis10.validPixelFormatsForCodec(codec);
31035
+ return BrowserSafeApis11.validPixelFormatsForCodec(codec);
30755
31036
  }, [codec]);
30756
31037
  const pixelFormat = useMemo137(() => {
30757
31038
  if (availablePixelFormats.includes(userPreferredPixelFormat)) {
@@ -31078,8 +31359,134 @@ var RenderModal = ({
31078
31359
  });
31079
31360
  const { tab, setTab, shownTabs } = useRenderModalSections(renderMode, codec);
31080
31361
  const { registerKeybinding } = useKeybinding();
31081
- const renderDisabled = state.type === "load" || !outnameValidation.valid;
31362
+ const readOnlyRenderCommand = useMemo137(() => {
31363
+ if (!readOnlyStudio) {
31364
+ return null;
31365
+ }
31366
+ return makeReadOnlyStudioRenderCommand({
31367
+ remotionVersion: window.remotion_version,
31368
+ locationHref: window.location.href,
31369
+ compositionId: resolvedComposition.id,
31370
+ outName,
31371
+ renderMode,
31372
+ renderDefaults,
31373
+ durationInFrames: resolvedComposition.durationInFrames,
31374
+ concurrency,
31375
+ frame: frame2,
31376
+ startFrame,
31377
+ endFrame,
31378
+ stillImageFormat,
31379
+ sequenceImageFormat,
31380
+ videoImageFormat,
31381
+ jpegQuality: renderMode === "video" ? stillImageFormat === "jpeg" ? jpegQuality : null : renderMode === "audio" ? null : jpegQuality,
31382
+ codec,
31383
+ muted,
31384
+ enforceAudioTrack,
31385
+ proResProfile,
31386
+ x264Preset,
31387
+ pixelFormat,
31388
+ crf: qualityControlType === "crf" && hardwareAcceleration !== "if-possible" && hardwareAcceleration !== "required" ? crf : null,
31389
+ videoBitrate,
31390
+ audioBitrate,
31391
+ audioCodec,
31392
+ everyNthFrame,
31393
+ numberOfGifLoops,
31394
+ disallowParallelEncoding,
31395
+ encodingBufferSize,
31396
+ encodingMaxRate,
31397
+ forSeamlessAacConcatenation,
31398
+ separateAudioTo,
31399
+ colorSpace,
31400
+ scale,
31401
+ logLevel,
31402
+ delayRenderTimeout,
31403
+ hardwareAcceleration,
31404
+ chromeMode,
31405
+ headless,
31406
+ disableWebSecurity,
31407
+ ignoreCertificateErrors,
31408
+ gl: openGlOption === "default" ? null : openGlOption,
31409
+ userAgent,
31410
+ multiProcessOnLinux,
31411
+ darkMode,
31412
+ offthreadVideoCacheSizeInBytes,
31413
+ offthreadVideoThreads,
31414
+ mediaCacheSizeInBytes,
31415
+ beepOnFinish,
31416
+ repro,
31417
+ metadata,
31418
+ envVariables: envVariablesArrayToObject(envVariables),
31419
+ inputProps
31420
+ });
31421
+ }, [
31422
+ audioBitrate,
31423
+ audioCodec,
31424
+ beepOnFinish,
31425
+ chromeMode,
31426
+ codec,
31427
+ colorSpace,
31428
+ concurrency,
31429
+ crf,
31430
+ darkMode,
31431
+ delayRenderTimeout,
31432
+ disableWebSecurity,
31433
+ disallowParallelEncoding,
31434
+ endFrame,
31435
+ encodingBufferSize,
31436
+ encodingMaxRate,
31437
+ enforceAudioTrack,
31438
+ envVariables,
31439
+ everyNthFrame,
31440
+ frame2,
31441
+ forSeamlessAacConcatenation,
31442
+ hardwareAcceleration,
31443
+ headless,
31444
+ ignoreCertificateErrors,
31445
+ inputProps,
31446
+ jpegQuality,
31447
+ logLevel,
31448
+ mediaCacheSizeInBytes,
31449
+ metadata,
31450
+ multiProcessOnLinux,
31451
+ muted,
31452
+ numberOfGifLoops,
31453
+ offthreadVideoCacheSizeInBytes,
31454
+ offthreadVideoThreads,
31455
+ openGlOption,
31456
+ outName,
31457
+ pixelFormat,
31458
+ proResProfile,
31459
+ qualityControlType,
31460
+ readOnlyStudio,
31461
+ renderDefaults,
31462
+ renderMode,
31463
+ repro,
31464
+ resolvedComposition.durationInFrames,
31465
+ resolvedComposition.id,
31466
+ scale,
31467
+ separateAudioTo,
31468
+ sequenceImageFormat,
31469
+ startFrame,
31470
+ stillImageFormat,
31471
+ userAgent,
31472
+ videoImageFormat,
31473
+ videoBitrate,
31474
+ x264Preset
31475
+ ]);
31476
+ const [commandCopiedAt, setCommandCopiedAt] = useState88(null);
31477
+ const renderDisabled = readOnlyStudio ? false : !outnameValidation.valid || state.type === "load";
31082
31478
  const trigger = useCallback136(() => {
31479
+ if (readOnlyStudio) {
31480
+ if (!readOnlyRenderCommand) {
31481
+ return;
31482
+ }
31483
+ copyText(readOnlyRenderCommand).then(() => {
31484
+ setCommandCopiedAt(Date.now());
31485
+ }).catch((err) => {
31486
+ showNotification(`Could not copy: ${err.message}`, 2000);
31487
+ });
31488
+ return;
31489
+ }
31083
31490
  if (renderMode === "still") {
31084
31491
  onClickStill();
31085
31492
  } else if (renderMode === "sequence") {
@@ -31087,7 +31494,23 @@ var RenderModal = ({
31087
31494
  } else {
31088
31495
  onClickVideo();
31089
31496
  }
31090
- }, [renderMode, onClickStill, onClickSequence, onClickVideo]);
31497
+ }, [
31498
+ onClickSequence,
31499
+ onClickStill,
31500
+ onClickVideo,
31501
+ readOnlyRenderCommand,
31502
+ readOnlyStudio,
31503
+ renderMode
31504
+ ]);
31505
+ useEffect85(() => {
31506
+ if (commandCopiedAt === null) {
31507
+ return;
31508
+ }
31509
+ const timeout = setTimeout(() => {
31510
+ setCommandCopiedAt(null);
31511
+ }, 2000);
31512
+ return () => clearTimeout(timeout);
31513
+ }, [commandCopiedAt]);
31091
31514
  useEffect85(() => {
31092
31515
  if (renderDisabled) {
31093
31516
  return;
@@ -31148,7 +31571,7 @@ var RenderModal = ({
31148
31571
  backgroundColor: outnameValidation.valid ? BLUE : BLUE_DISABLED
31149
31572
  },
31150
31573
  children: [
31151
- state.type === "idle" ? `Render ${renderMode}` : "Rendering...",
31574
+ readOnlyStudio ? commandCopiedAt ? "Copied command!" : "Copy command" : state.type === "idle" ? `Render ${renderMode}` : "Rendering...",
31152
31575
  /* @__PURE__ */ jsx269(ShortcutHint, {
31153
31576
  keyToPress: "↵",
31154
31577
  cmdOrCtrl: true
@@ -31269,6 +31692,7 @@ var RenderModal = ({
31269
31692
  setStartFrame,
31270
31693
  setVerboseLogging: setLogLevel,
31271
31694
  logLevel,
31695
+ showOutputName: !readOnlyStudio,
31272
31696
  startFrame,
31273
31697
  validationMessage: outnameValidation.valid ? null : outnameValidation.error.message
31274
31698
  }) : tab === "picture" ? /* @__PURE__ */ jsx269(RenderModalPicture, {
@@ -31332,7 +31756,7 @@ var RenderModal = ({
31332
31756
  propsEditType: "input-props",
31333
31757
  saving,
31334
31758
  setSaving,
31335
- readOnlyStudio: false
31759
+ readOnlyStudio
31336
31760
  }) : /* @__PURE__ */ jsx269(RenderModalAdvanced, {
31337
31761
  x264Preset,
31338
31762
  setx264Preset,
@@ -31798,6 +32222,7 @@ var WebRenderModalAudio = ({
31798
32222
  const audioBitrateOptions = useMemo139(() => getQualityOptions(audioBitrate, setAudioBitrate), [audioBitrate, setAudioBitrate]);
31799
32223
  const isAudioOnly = renderMode === "audio";
31800
32224
  const showAudioSettings = isAudioOnly || !muted;
32225
+ const showAudioCodecSetting = !isAudioOnly || containerSupported.length > 1;
31801
32226
  return /* @__PURE__ */ jsxs143("div", {
31802
32227
  style: container62,
31803
32228
  className: VERTICAL_SCROLLBAR_CLASSNAME,
@@ -31832,7 +32257,7 @@ var WebRenderModalAudio = ({
31832
32257
  })
31833
32258
  ]
31834
32259
  }),
31835
- /* @__PURE__ */ jsxs143("div", {
32260
+ showAudioCodecSetting ? /* @__PURE__ */ jsxs143("div", {
31836
32261
  style: optionRow,
31837
32262
  children: [
31838
32263
  /* @__PURE__ */ jsxs143("div", {
@@ -31853,8 +32278,8 @@ var WebRenderModalAudio = ({
31853
32278
  })
31854
32279
  })
31855
32280
  ]
31856
- }),
31857
- effectiveAudioCodec !== audioCodec ? /* @__PURE__ */ jsxs143("div", {
32281
+ }) : null,
32282
+ showAudioCodecSetting && effectiveAudioCodec !== audioCodec ? /* @__PURE__ */ jsxs143("div", {
31858
32283
  style: fallbackNoticeStyle,
31859
32284
  children: [
31860
32285
  humanReadableWebAudioCodec(audioCodec),
@@ -33524,7 +33949,8 @@ var Modals = ({ readOnlyStudio }) => {
33524
33949
  modalContextType && modalContextType.type === "web-render" && /* @__PURE__ */ jsx285(WebRenderModalWithLoader, {
33525
33950
  ...modalContextType
33526
33951
  }),
33527
- modalContextType && canRender && modalContextType.type === "server-render" && /* @__PURE__ */ jsx285(RenderModalWithLoader, {
33952
+ modalContextType && modalContextType.type === "server-render" && (canRender || modalContextType.readOnlyStudio) ? /* @__PURE__ */ jsx285(RenderModalWithLoader, {
33953
+ readOnlyStudio: modalContextType.readOnlyStudio ?? false,
33528
33954
  initialFrame: modalContextType.initialFrame,
33529
33955
  initialDarkMode: modalContextType.initialDarkMode,
33530
33956
  compositionId: modalContextType.compositionId,
@@ -33572,7 +33998,7 @@ var Modals = ({ readOnlyStudio }) => {
33572
33998
  initialHardwareAcceleration: modalContextType.initialHardwareAcceleration,
33573
33999
  initialChromeMode: modalContextType.initialChromeMode,
33574
34000
  renderDefaults: modalContextType.renderDefaults
33575
- }),
34001
+ }) : null,
33576
34002
  modalContextType && modalContextType.type === "render-progress" && /* @__PURE__ */ jsx285(RenderStatusModal, {
33577
34003
  jobId: modalContextType.jobId
33578
34004
  }),