@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.
@@ -2870,7 +2870,8 @@ var SidebarRenderButton = ({ composition, visible }) => {
2870
2870
  initialChromeMode: defaults.chromeMode,
2871
2871
  initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes,
2872
2872
  renderDefaults: defaults,
2873
- initialDarkMode: defaults.darkMode
2873
+ initialDarkMode: defaults.darkMode,
2874
+ readOnlyStudio: false
2874
2875
  });
2875
2876
  if (isMobileLayout) {
2876
2877
  setSidebarCollapsedState({ left: "collapsed", right: "collapsed" });
@@ -10833,10 +10834,7 @@ var CurrentCompositionKeybindings = ({ readOnlyStudio }) => {
10833
10834
  if (!video) {
10834
10835
  return;
10835
10836
  }
10836
- if (readOnlyStudio) {
10837
- return showNotification("Studio is read-only", 2000);
10838
- }
10839
- if (type !== "connected") {
10837
+ if (type !== "connected" && !SHOW_BROWSER_RENDERING && !readOnlyStudio) {
10840
10838
  showNotification("Studio server is offline", 2000);
10841
10839
  return;
10842
10840
  }
@@ -17159,7 +17157,8 @@ var makeRetryPayload = (job) => {
17159
17157
  initialHardwareAcceleration: defaults.hardwareAcceleration,
17160
17158
  initialChromeMode: job.chromeMode,
17161
17159
  initialMediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
17162
- renderDefaults: defaults
17160
+ renderDefaults: defaults,
17161
+ readOnlyStudio: false
17163
17162
  };
17164
17163
  }
17165
17164
  if (job.type === "sequence") {
@@ -17211,7 +17210,8 @@ var makeRetryPayload = (job) => {
17211
17210
  initialHardwareAcceleration: defaults.hardwareAcceleration,
17212
17211
  initialChromeMode: job.chromeMode,
17213
17212
  initialMediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
17214
- renderDefaults: defaults
17213
+ renderDefaults: defaults,
17214
+ readOnlyStudio: false
17215
17215
  };
17216
17216
  }
17217
17217
  if (job.type === "video") {
@@ -17263,7 +17263,8 @@ var makeRetryPayload = (job) => {
17263
17263
  initialHardwareAcceleration: job.hardwareAcceleration,
17264
17264
  initialChromeMode: job.chromeMode,
17265
17265
  initialMediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
17266
- renderDefaults: defaults
17266
+ renderDefaults: defaults,
17267
+ readOnlyStudio: false
17267
17268
  };
17268
17269
  }
17269
17270
  throw new Error(`Job ${JSON.stringify(job)} Not implemented`);
@@ -18156,14 +18157,6 @@ var OptionsPanel = ({ readOnlyStudio }) => {
18156
18157
  import { useContext as useContext57, useEffect as useEffect60, useRef as useRef32, useState as useState62 } from "react";
18157
18158
  import { Internals as Internals45 } from "remotion";
18158
18159
 
18159
- // src/helpers/should-show-render-button.ts
18160
- var shouldShowRenderButton = (readOnlyStudio) => {
18161
- if (readOnlyStudio) {
18162
- return SHOW_BROWSER_RENDERING;
18163
- }
18164
- return true;
18165
- };
18166
-
18167
18160
  // src/state/loop.ts
18168
18161
  var key = "remotion.loop";
18169
18162
  var persistLoopOption = (option) => {
@@ -19024,7 +19017,7 @@ var label7 = {
19024
19017
  var RENDER_TYPE_STORAGE_KEY = "remotion.renderType";
19025
19018
  var getInitialRenderType = (readOnlyStudio) => {
19026
19019
  if (!SHOW_BROWSER_RENDERING) {
19027
- return "server-render";
19020
+ return readOnlyStudio ? "render-command" : "server-render";
19028
19021
  }
19029
19022
  if (readOnlyStudio) {
19030
19023
  return "client-render";
@@ -19081,8 +19074,15 @@ var RenderButton = ({
19081
19074
  });
19082
19075
  }, [refresh]);
19083
19076
  const connectionStatus = useContext55(StudioServerConnectionCtx).previewServerState.type;
19084
- const canRender = connectionStatus === "connected" || SHOW_BROWSER_RENDERING;
19077
+ const canServerRender = connectionStatus === "connected";
19078
+ const canRender = canServerRender || SHOW_BROWSER_RENDERING || readOnlyStudio;
19085
19079
  const renderType = useMemo95(() => {
19080
+ if (readOnlyStudio) {
19081
+ if (!SHOW_BROWSER_RENDERING) {
19082
+ return "render-command";
19083
+ }
19084
+ return preferredRenderType === "render-command" ? "render-command" : "client-render";
19085
+ }
19086
19086
  if (connectionStatus === "disconnected" && SHOW_BROWSER_RENDERING) {
19087
19087
  return "client-render";
19088
19088
  }
@@ -19090,9 +19090,9 @@ var RenderButton = ({
19090
19090
  return "server-render";
19091
19091
  }
19092
19092
  return preferredRenderType;
19093
- }, [connectionStatus, preferredRenderType]);
19093
+ }, [connectionStatus, preferredRenderType, readOnlyStudio]);
19094
19094
  const shortcut = areKeyboardShortcutsDisabled() ? "" : "(R)";
19095
- const tooltip = canRender ? "Export the current composition " + shortcut : "Connect to the Studio server to render";
19095
+ 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";
19096
19096
  const iconStyle4 = useMemo95(() => {
19097
19097
  return {
19098
19098
  style: {
@@ -19104,7 +19104,7 @@ var RenderButton = ({
19104
19104
  const video = Internals43.useVideo();
19105
19105
  const getCurrentFrame2 = PlayerInternals14.useFrameImperative();
19106
19106
  const { props } = useContext55(Internals43.EditorPropsContext);
19107
- const openServerRenderModal = useCallback90(() => {
19107
+ const openServerRenderModal = useCallback90((copyCommandOnly) => {
19108
19108
  if (!video) {
19109
19109
  return null;
19110
19110
  }
@@ -19114,6 +19114,7 @@ var RenderButton = ({
19114
19114
  }
19115
19115
  setSelectedModal({
19116
19116
  type: "server-render",
19117
+ readOnlyStudio: copyCommandOnly,
19117
19118
  compositionId: video.id,
19118
19119
  initialFrame: getCurrentFrame2(),
19119
19120
  initialStillImageFormat: defaults.stillImageFormat,
@@ -19197,8 +19198,12 @@ var RenderButton = ({
19197
19198
  });
19198
19199
  }, [video, setSelectedModal, getCurrentFrame2, props, inFrame, outFrame]);
19199
19200
  const onClick = useCallback90(() => {
19201
+ if (renderType === "render-command") {
19202
+ openServerRenderModal(true);
19203
+ return;
19204
+ }
19200
19205
  if (!SHOW_BROWSER_RENDERING || renderType === "server-render") {
19201
- openServerRenderModal();
19206
+ openServerRenderModal(false);
19202
19207
  } else {
19203
19208
  openClientRenderModal();
19204
19209
  }
@@ -19213,12 +19218,40 @@ var RenderButton = ({
19213
19218
  } catch {}
19214
19219
  setDropdownOpened(false);
19215
19220
  if (newType === "server-render") {
19216
- openServerRenderModal();
19221
+ openServerRenderModal(false);
19222
+ } else if (newType === "render-command") {
19223
+ openServerRenderModal(true);
19217
19224
  } else {
19218
19225
  openClientRenderModal();
19219
19226
  }
19220
- }, [openServerRenderModal, openClientRenderModal]);
19227
+ }, [openClientRenderModal, openServerRenderModal]);
19221
19228
  const dropdownValues = useMemo95(() => {
19229
+ if (readOnlyStudio) {
19230
+ return [
19231
+ {
19232
+ type: "item",
19233
+ id: "client-render",
19234
+ label: "Render on web",
19235
+ value: "client-render",
19236
+ onClick: () => handleRenderTypeChange("client-render"),
19237
+ keyHint: null,
19238
+ leftItem: null,
19239
+ subMenu: null,
19240
+ quickSwitcherLabel: null
19241
+ },
19242
+ {
19243
+ type: "item",
19244
+ id: "render-command",
19245
+ label: "Render via CLI",
19246
+ value: "render-command",
19247
+ onClick: () => handleRenderTypeChange("render-command"),
19248
+ keyHint: null,
19249
+ leftItem: null,
19250
+ subMenu: null,
19251
+ quickSwitcherLabel: null
19252
+ }
19253
+ ];
19254
+ }
19222
19255
  return [
19223
19256
  {
19224
19257
  type: "item",
@@ -19243,7 +19276,7 @@ var RenderButton = ({
19243
19276
  quickSwitcherLabel: null
19244
19277
  }
19245
19278
  ];
19246
- }, [handleRenderTypeChange]);
19279
+ }, [handleRenderTypeChange, readOnlyStudio]);
19247
19280
  const spaceToBottom = useMemo95(() => {
19248
19281
  const margin2 = 10;
19249
19282
  if (size4 && dropdownOpened) {
@@ -19285,10 +19318,10 @@ var RenderButton = ({
19285
19318
  cursor: canRender ? "pointer" : "inherit"
19286
19319
  };
19287
19320
  }, [canRender]);
19288
- const renderLabel = renderType === "server-render" ? "Render" : "Render on web";
19321
+ const renderLabel = renderType === "server-render" ? "Render" : renderType === "render-command" ? "Render via CLI" : "Render on web";
19289
19322
  const shouldShowDropdown = useMemo95(() => {
19290
19323
  if (readOnlyStudio) {
19291
- return false;
19324
+ return SHOW_BROWSER_RENDERING;
19292
19325
  }
19293
19326
  if (!SHOW_BROWSER_RENDERING) {
19294
19327
  return false;
@@ -19303,8 +19336,8 @@ var RenderButton = ({
19303
19336
  /* @__PURE__ */ jsx177("button", {
19304
19337
  style: { display: "none" },
19305
19338
  id: "render-modal-button-server",
19306
- disabled: !canRender,
19307
- onClick: openServerRenderModal,
19339
+ disabled: !canServerRender,
19340
+ onClick: () => openServerRenderModal(false),
19308
19341
  type: "button"
19309
19342
  }),
19310
19343
  " ",
@@ -19352,7 +19385,7 @@ var RenderButton = ({
19352
19385
  ref: dropdownRef,
19353
19386
  type: "button",
19354
19387
  style: dropdownTriggerStyle,
19355
- disabled: connectionStatus !== "connected",
19388
+ disabled: !readOnlyStudio && connectionStatus !== "connected",
19356
19389
  className: MENU_INITIATOR_CLASSNAME,
19357
19390
  onPointerDown,
19358
19391
  onClick: onClickDropdown,
@@ -19691,9 +19724,9 @@ var PreviewToolbar = ({ readOnlyStudio, bufferStateDelayInMilliseconds }) => {
19691
19724
  /* @__PURE__ */ jsx180(Spacing, {
19692
19725
  x: 2
19693
19726
  }),
19694
- shouldShowRenderButton(readOnlyStudio) ? /* @__PURE__ */ jsx180(RenderButton, {
19727
+ /* @__PURE__ */ jsx180(RenderButton, {
19695
19728
  readOnlyStudio
19696
- }) : null,
19729
+ }),
19697
19730
  /* @__PURE__ */ jsx180(Spacing, {
19698
19731
  x: 1.5
19699
19732
  })
@@ -27296,7 +27329,7 @@ var RenderStatusModal = ({
27296
27329
  };
27297
27330
 
27298
27331
  // src/components/RenderModal/ServerRenderModal.tsx
27299
- import { BrowserSafeApis as BrowserSafeApis10 } from "@remotion/renderer/client";
27332
+ import { BrowserSafeApis as BrowserSafeApis11 } from "@remotion/renderer/client";
27300
27333
  import { getDefaultOutLocation } from "@remotion/studio-shared";
27301
27334
  import {
27302
27335
  useCallback as useCallback136,
@@ -27322,6 +27355,252 @@ var envVariablesArrayToObject = (envVariables) => {
27322
27355
  }, {});
27323
27356
  };
27324
27357
 
27358
+ // src/helpers/make-render-command.ts
27359
+ import { BrowserSafeApis } from "@remotion/renderer/client";
27360
+ var shellQuote = (value) => {
27361
+ return `'${value.replace(/'/g, "'\\''")}'`;
27362
+ };
27363
+ var addFlagWithValue = (flags, flag, value) => {
27364
+ if (value === null || value === undefined) {
27365
+ return;
27366
+ }
27367
+ flags.push(`--${flag}=${shellQuote(String(value))}`);
27368
+ };
27369
+ var addBooleanFlag = (flags, flag, value) => {
27370
+ if (value) {
27371
+ flags.push(`--${flag}`);
27372
+ }
27373
+ };
27374
+ var valueFlag = (flag, value, defaultValue) => {
27375
+ return { flag, value, defaultValue };
27376
+ };
27377
+ var booleanFlag = (flag, value, defaultValue) => {
27378
+ return { flag, value, defaultValue };
27379
+ };
27380
+ var addValueFlagsIfChanged = (flags, mappings) => {
27381
+ for (const mapping of mappings) {
27382
+ if (mapping.value !== mapping.defaultValue) {
27383
+ addFlagWithValue(flags, mapping.flag, mapping.value);
27384
+ }
27385
+ }
27386
+ };
27387
+ var addTrueBooleanFlagsIfChanged = (flags, mappings) => {
27388
+ for (const mapping of mappings) {
27389
+ if (mapping.value && mapping.value !== mapping.defaultValue) {
27390
+ addBooleanFlag(flags, mapping.flag, true);
27391
+ }
27392
+ }
27393
+ };
27394
+ var getNpmRemotionCommandPrefix = (version) => {
27395
+ return version.trim() === "" ? "npx --yes --location=global -p @remotion/cli remotion" : `npx --yes --location=global -p @remotion/cli@${version} remotion`;
27396
+ };
27397
+ var normalizeServeUrlForRenderCommand = ({
27398
+ locationHref,
27399
+ compositionId
27400
+ }) => {
27401
+ const parsed = new URL(locationHref);
27402
+ parsed.hash = "";
27403
+ parsed.search = "";
27404
+ const suffix2 = `/${compositionId}`;
27405
+ if (parsed.pathname === suffix2) {
27406
+ parsed.pathname = "/";
27407
+ } else if (parsed.pathname.endsWith(suffix2)) {
27408
+ const basePath = parsed.pathname.slice(0, -suffix2.length);
27409
+ parsed.pathname = basePath === "" ? "/" : basePath;
27410
+ }
27411
+ if (parsed.pathname !== "/" && parsed.pathname.endsWith("/")) {
27412
+ parsed.pathname = parsed.pathname.slice(0, -1);
27413
+ }
27414
+ return `${parsed.origin}${parsed.pathname === "/" ? "" : parsed.pathname}`;
27415
+ };
27416
+ var trimDefaultOutPrefix = (outName) => {
27417
+ if (outName.startsWith("out/")) {
27418
+ const trimmed = outName.slice("out/".length);
27419
+ return trimmed.length === 0 ? outName : trimmed;
27420
+ }
27421
+ if (outName.startsWith("./out/")) {
27422
+ const trimmed = outName.slice("./out/".length);
27423
+ return trimmed.length === 0 ? outName : trimmed;
27424
+ }
27425
+ return outName;
27426
+ };
27427
+ var getRenderMediaFlag = (option) => {
27428
+ return BrowserSafeApis.optionsMap.renderMedia[option].cliFlag;
27429
+ };
27430
+ var renderMediaValueFlag = (option, value, defaultValue) => {
27431
+ return valueFlag(getRenderMediaFlag(option), value, defaultValue);
27432
+ };
27433
+ var renderMediaBooleanFlag = (option, value, defaultValue) => {
27434
+ return booleanFlag(getRenderMediaFlag(option), value, defaultValue);
27435
+ };
27436
+ var makeReadOnlyStudioRenderCommand = ({
27437
+ remotionVersion,
27438
+ locationHref,
27439
+ compositionId,
27440
+ outName,
27441
+ renderMode,
27442
+ renderDefaults,
27443
+ durationInFrames,
27444
+ concurrency,
27445
+ frame: frame2,
27446
+ startFrame,
27447
+ endFrame,
27448
+ stillImageFormat,
27449
+ sequenceImageFormat,
27450
+ videoImageFormat,
27451
+ jpegQuality,
27452
+ codec,
27453
+ muted,
27454
+ enforceAudioTrack,
27455
+ proResProfile,
27456
+ x264Preset,
27457
+ pixelFormat,
27458
+ crf,
27459
+ videoBitrate,
27460
+ audioBitrate,
27461
+ audioCodec,
27462
+ everyNthFrame,
27463
+ numberOfGifLoops,
27464
+ disallowParallelEncoding,
27465
+ encodingBufferSize,
27466
+ encodingMaxRate,
27467
+ forSeamlessAacConcatenation,
27468
+ separateAudioTo,
27469
+ colorSpace,
27470
+ scale,
27471
+ logLevel,
27472
+ delayRenderTimeout,
27473
+ hardwareAcceleration,
27474
+ chromeMode,
27475
+ headless,
27476
+ disableWebSecurity,
27477
+ ignoreCertificateErrors,
27478
+ gl,
27479
+ userAgent,
27480
+ multiProcessOnLinux,
27481
+ darkMode,
27482
+ offthreadVideoCacheSizeInBytes,
27483
+ offthreadVideoThreads,
27484
+ mediaCacheSizeInBytes,
27485
+ beepOnFinish,
27486
+ repro,
27487
+ metadata,
27488
+ envVariables,
27489
+ inputProps
27490
+ }) => {
27491
+ const serveUrl = normalizeServeUrlForRenderCommand({
27492
+ locationHref,
27493
+ compositionId
27494
+ });
27495
+ const isStillRender = renderMode === "still";
27496
+ const isSequenceRender = renderMode === "sequence";
27497
+ const hasCodecSpecificOptions = !isSequenceRender;
27498
+ const commandType = isStillRender ? "still" : "render";
27499
+ const command = getNpmRemotionCommandPrefix(remotionVersion);
27500
+ const { options } = BrowserSafeApis;
27501
+ const flags = [];
27502
+ addValueFlagsIfChanged(flags, [
27503
+ valueFlag(options.scaleOption.cliFlag, scale, renderDefaults.scale),
27504
+ renderMediaValueFlag("logLevel", logLevel, renderDefaults.logLevel),
27505
+ renderMediaValueFlag("timeoutInMilliseconds", delayRenderTimeout, renderDefaults.delayRenderTimeout),
27506
+ renderMediaValueFlag("chromeMode", chromeMode, renderDefaults.chromeMode),
27507
+ valueFlag(options.glOption.cliFlag, gl, renderDefaults.openGlRenderer),
27508
+ valueFlag(options.userAgentOption.cliFlag, userAgent, renderDefaults.userAgent),
27509
+ renderMediaValueFlag("offthreadVideoCacheSizeInBytes", offthreadVideoCacheSizeInBytes, renderDefaults.offthreadVideoCacheSizeInBytes),
27510
+ renderMediaValueFlag("offthreadVideoThreads", offthreadVideoThreads, renderDefaults.offthreadVideoThreads),
27511
+ renderMediaValueFlag("mediaCacheSizeInBytes", mediaCacheSizeInBytes, renderDefaults.mediaCacheSizeInBytes)
27512
+ ]);
27513
+ if (headless !== renderDefaults.headless) {
27514
+ addFlagWithValue(flags, options.headlessOption.cliFlag, !headless);
27515
+ }
27516
+ addTrueBooleanFlagsIfChanged(flags, [
27517
+ booleanFlag(options.disableWebSecurityOption.cliFlag, disableWebSecurity, renderDefaults.disableWebSecurity),
27518
+ booleanFlag(options.ignoreCertificateErrorsOption.cliFlag, ignoreCertificateErrors, renderDefaults.ignoreCertificateErrors),
27519
+ booleanFlag(options.enableMultiprocessOnLinuxOption.cliFlag, multiProcessOnLinux, renderDefaults.multiProcessOnLinux),
27520
+ booleanFlag(options.darkModeOption.cliFlag, darkMode, renderDefaults.darkMode),
27521
+ booleanFlag(options.beepOnFinishOption.cliFlag, beepOnFinish, renderDefaults.beepOnFinish)
27522
+ ]);
27523
+ if (isStillRender) {
27524
+ addValueFlagsIfChanged(flags, [
27525
+ valueFlag(options.stillFrameOption.cliFlag, frame2, 0),
27526
+ valueFlag(options.stillImageFormatOption.cliFlag, stillImageFormat, renderDefaults.stillImageFormat),
27527
+ valueFlag(options.jpegQualityOption.cliFlag, jpegQuality, renderDefaults.jpegQuality)
27528
+ ]);
27529
+ } else {
27530
+ addValueFlagsIfChanged(flags, [
27531
+ valueFlag(options.concurrencyOption.cliFlag, concurrency, renderDefaults.concurrency)
27532
+ ]);
27533
+ if (isSequenceRender) {
27534
+ addBooleanFlag(flags, options.imageSequenceOption.cliFlag, true);
27535
+ if (sequenceImageFormat !== "jpeg") {
27536
+ addFlagWithValue(flags, options.videoImageFormatOption.cliFlag, sequenceImageFormat);
27537
+ }
27538
+ } else {
27539
+ addValueFlagsIfChanged(flags, [
27540
+ valueFlag(options.videoImageFormatOption.cliFlag, videoImageFormat, renderDefaults.videoImageFormat),
27541
+ renderMediaValueFlag("hardwareAcceleration", hardwareAcceleration, renderDefaults.hardwareAcceleration)
27542
+ ]);
27543
+ }
27544
+ if (hasCodecSpecificOptions && codec !== renderDefaults.codec) {
27545
+ addFlagWithValue(flags, getRenderMediaFlag("codec"), codec);
27546
+ }
27547
+ if (startFrame !== 0 || endFrame !== durationInFrames - 1) {
27548
+ addFlagWithValue(flags, options.framesOption.cliFlag, `${startFrame}-${endFrame}`);
27549
+ }
27550
+ if (hasCodecSpecificOptions) {
27551
+ addTrueBooleanFlagsIfChanged(flags, [
27552
+ renderMediaBooleanFlag("muted", muted, renderDefaults.muted),
27553
+ booleanFlag(options.enforceAudioOption.cliFlag, enforceAudioTrack, renderDefaults.enforceAudioTrack),
27554
+ renderMediaBooleanFlag("forSeamlessAacConcatenation", forSeamlessAacConcatenation, renderDefaults.forSeamlessAacConcatenation)
27555
+ ]);
27556
+ addValueFlagsIfChanged(flags, [
27557
+ valueFlag(options.pixelFormatOption.cliFlag, pixelFormat, renderDefaults.pixelFormat),
27558
+ renderMediaValueFlag("colorSpace", colorSpace, renderDefaults.colorSpace),
27559
+ valueFlag(options.proResProfileOption.cliFlag, proResProfile, renderDefaults.proResProfile),
27560
+ renderMediaValueFlag("x264Preset", x264Preset, renderDefaults.x264Preset),
27561
+ valueFlag(options.crfOption.cliFlag, crf, null),
27562
+ valueFlag(options.jpegQualityOption.cliFlag, jpegQuality, renderDefaults.jpegQuality),
27563
+ renderMediaValueFlag("videoBitrate", videoBitrate, renderDefaults.videoBitrate),
27564
+ renderMediaValueFlag("audioBitrate", audioBitrate, renderDefaults.audioBitrate),
27565
+ valueFlag(options.everyNthFrameOption.cliFlag, everyNthFrame, renderDefaults.everyNthFrame),
27566
+ renderMediaValueFlag("numberOfGifLoops", numberOfGifLoops, renderDefaults.numberOfGifLoops),
27567
+ renderMediaValueFlag("encodingBufferSize", encodingBufferSize, renderDefaults.encodingBufferSize),
27568
+ renderMediaValueFlag("encodingMaxRate", encodingMaxRate, renderDefaults.encodingMaxRate),
27569
+ renderMediaValueFlag("separateAudioTo", separateAudioTo, null)
27570
+ ]);
27571
+ const defaultAudioCodec = BrowserSafeApis.defaultAudioCodecs[codec]?.compressed;
27572
+ if (audioCodec !== defaultAudioCodec) {
27573
+ addFlagWithValue(flags, getRenderMediaFlag("audioCodec"), audioCodec);
27574
+ }
27575
+ }
27576
+ addTrueBooleanFlagsIfChanged(flags, [
27577
+ renderMediaBooleanFlag("disallowParallelEncoding", disallowParallelEncoding, false),
27578
+ renderMediaBooleanFlag("repro", repro, renderDefaults.repro)
27579
+ ]);
27580
+ }
27581
+ if (metadata) {
27582
+ for (const [key5, value] of Object.entries(metadata)) {
27583
+ addFlagWithValue(flags, options.metadataOption.cliFlag, `${key5}=${value}`);
27584
+ }
27585
+ }
27586
+ if (Object.keys(inputProps).length > 0) {
27587
+ addFlagWithValue(flags, options.propsOption.cliFlag, JSON.stringify(inputProps));
27588
+ }
27589
+ const envArgs = Object.entries(envVariables).sort(([a], [b]) => a.localeCompare(b)).map(([key5, value]) => shellQuote(`${key5}=${value}`));
27590
+ const renderCommand = [
27591
+ command,
27592
+ commandType,
27593
+ shellQuote(serveUrl),
27594
+ shellQuote(compositionId),
27595
+ shellQuote(trimDefaultOutPrefix(outName)),
27596
+ ...flags
27597
+ ].join(" ");
27598
+ if (envArgs.length === 0) {
27599
+ return renderCommand;
27600
+ }
27601
+ return ["env", ...envArgs, renderCommand].join(" ");
27602
+ };
27603
+
27325
27604
  // src/helpers/render-modal-sections.ts
27326
27605
  import { useMemo as useMemo128, useState as useState83 } from "react";
27327
27606
  var useRenderModalSections = (renderMode, codec) => {
@@ -27463,14 +27742,14 @@ var VerticalTab = ({ children, onClick, style: style12, selected }) => {
27463
27742
  };
27464
27743
 
27465
27744
  // src/components/RenderModal/CrfSetting.tsx
27466
- import { BrowserSafeApis as BrowserSafeApis2 } from "@remotion/renderer/client";
27745
+ import { BrowserSafeApis as BrowserSafeApis3 } from "@remotion/renderer/client";
27467
27746
  import { useState as useState86 } from "react";
27468
27747
 
27469
27748
  // src/components/RenderModal/NumberSetting.tsx
27470
27749
  import { useCallback as useCallback121 } from "react";
27471
27750
 
27472
27751
  // src/components/RenderModal/OptionExplainerBubble.tsx
27473
- import { BrowserSafeApis } from "@remotion/renderer/client";
27752
+ import { BrowserSafeApis as BrowserSafeApis2 } from "@remotion/renderer/client";
27474
27753
 
27475
27754
  // src/components/RenderModal/CliCopyButton.tsx
27476
27755
  import { useCallback as useCallback120, useEffect as useEffect83, useMemo as useMemo130, useState as useState85 } from "react";
@@ -27697,7 +27976,7 @@ var OptionExplainer = ({ option }) => {
27697
27976
  // src/components/RenderModal/OptionExplainerBubble.tsx
27698
27977
  import { jsx as jsx248 } from "react/jsx-runtime";
27699
27978
  var OptionExplainerBubble = ({ id }) => {
27700
- const option = BrowserSafeApis.options[id];
27979
+ const option = BrowserSafeApis2.options[id];
27701
27980
  return /* @__PURE__ */ jsx248(InfoBubble, {
27702
27981
  title: "Learn more about this option",
27703
27982
  children: /* @__PURE__ */ jsx248(OptionExplainer, {
@@ -27765,8 +28044,8 @@ var NumberSetting = ({ name, value, step, hint, onValueChanged, max, min, format
27765
28044
  // src/components/RenderModal/CrfSetting.tsx
27766
28045
  import { jsx as jsx250 } from "react/jsx-runtime";
27767
28046
  var getDefaultCrfState = () => {
27768
- return BrowserSafeApis2.validCodecs.map((c) => {
27769
- return [c, BrowserSafeApis2.getDefaultCrfForCodec(c)];
28047
+ return BrowserSafeApis3.validCodecs.map((c) => {
28048
+ return [c, BrowserSafeApis3.getDefaultCrfForCodec(c)];
27770
28049
  }).reduce((acc, [codec, crf]) => {
27771
28050
  return {
27772
28051
  ...acc,
@@ -27776,7 +28055,7 @@ var getDefaultCrfState = () => {
27776
28055
  };
27777
28056
  var useCrfState = (codec) => {
27778
28057
  const [state, setState] = useState86(() => getDefaultCrfState());
27779
- const range = BrowserSafeApis2.getValidCrfRanges(codec);
28058
+ const range = BrowserSafeApis3.getValidCrfRanges(codec);
27780
28059
  const setCrf = (updater) => {
27781
28060
  setState((q) => {
27782
28061
  const val = q[codec];
@@ -27809,7 +28088,7 @@ var CrfSetting = ({ crf, setCrf, min, max, option }) => {
27809
28088
  };
27810
28089
 
27811
28090
  // src/components/RenderModal/get-default-codecs.ts
27812
- import { BrowserSafeApis as BrowserSafeApis3 } from "@remotion/renderer/client";
28091
+ import { BrowserSafeApis as BrowserSafeApis4 } from "@remotion/renderer/client";
27813
28092
  import { NoReactAPIs } from "@remotion/renderer/pure";
27814
28093
  var getDefaultCodecs = ({
27815
28094
  defaultConfigurationVideoCodec,
@@ -27829,7 +28108,7 @@ var getDefaultCodecs = ({
27829
28108
  initialVideoCodecForVideoTab: NoReactAPIs.isAudioCodec(defaultConfigurationVideoCodec) ? "h264" : defaultConfigurationVideoCodec
27830
28109
  };
27831
28110
  }
27832
- const suitableAudioCodecForVideoCodec = BrowserSafeApis3.defaultAudioCodecs[userPreferredVideoCodec].compressed;
28111
+ const suitableAudioCodecForVideoCodec = BrowserSafeApis4.defaultAudioCodecs[userPreferredVideoCodec].compressed;
27833
28112
  return {
27834
28113
  initialAudioCodec: defaultConfigurationAudioCodec ?? suitableAudioCodecForVideoCodec,
27835
28114
  initialVideoCodec: userPreferredVideoCodec,
@@ -27849,7 +28128,7 @@ var getStringBeforeSuffix = (fileName) => {
27849
28128
  };
27850
28129
 
27851
28130
  // src/components/RenderModal/out-name-checker.ts
27852
- import { BrowserSafeApis as BrowserSafeApis4 } from "@remotion/renderer/client";
28131
+ import { BrowserSafeApis as BrowserSafeApis5 } from "@remotion/renderer/client";
27853
28132
  var invalidCharacters = ["?", "*", "+", ":", "%"];
27854
28133
  var isValidStillExtension = (extension, stillImageFormat) => {
27855
28134
  if (stillImageFormat === "jpeg" && extension === "jpg") {
@@ -27889,8 +28168,8 @@ var isValidOutName = ({
27889
28168
  }) => {
27890
28169
  const extension = outName.substring(outName.lastIndexOf(".") + 1);
27891
28170
  const prefix = outName.substring(0, outName.lastIndexOf("."));
27892
- const map = BrowserSafeApis4.defaultFileExtensionMap[codec];
27893
- if (BrowserSafeApis4.supportedAudioCodecs[codec].length > 0 && !(audioCodec in map.forAudioCodec)) {
28171
+ const map = BrowserSafeApis5.defaultFileExtensionMap[codec];
28172
+ if (BrowserSafeApis5.supportedAudioCodecs[codec].length > 0 && !(audioCodec in map.forAudioCodec)) {
27894
28173
  throw new Error(`Audio codec ${audioCodec} is not supported for codec ${codec}`);
27895
28174
  }
27896
28175
  const hasDotAfterSlash = () => {
@@ -27906,7 +28185,7 @@ var isValidOutName = ({
27906
28185
  return prefix.split("").some((char) => invalidCharacters.includes(char));
27907
28186
  };
27908
28187
  if (renderMode === "video" || renderMode === "audio") {
27909
- BrowserSafeApis4.validateOutputFilename({
28188
+ BrowserSafeApis5.validateOutputFilename({
27910
28189
  codec,
27911
28190
  audioCodecSetting: audioCodec ?? null,
27912
28191
  extension,
@@ -27994,7 +28273,7 @@ var flexer = {
27994
28273
  };
27995
28274
 
27996
28275
  // src/components/RenderModal/RenderModalAdvanced.tsx
27997
- import { BrowserSafeApis as BrowserSafeApis5 } from "@remotion/renderer/client";
28276
+ import { BrowserSafeApis as BrowserSafeApis6 } from "@remotion/renderer/client";
27998
28277
  import { useCallback as useCallback125, useMemo as useMemo131 } from "react";
27999
28278
 
28000
28279
  // src/helpers/presets-labels.ts
@@ -28427,7 +28706,7 @@ var RenderModalAdvanced = ({
28427
28706
  });
28428
28707
  }, [extendedOpenGlOptions, openGlOption, setOpenGlOption]);
28429
28708
  const chromeModeOptions = useMemo131(() => {
28430
- return BrowserSafeApis5.validChromeModeOptions.map((option) => {
28709
+ return BrowserSafeApis6.validChromeModeOptions.map((option) => {
28431
28710
  return {
28432
28711
  label: option,
28433
28712
  onClick: () => setChromeModeOption(option),
@@ -28443,7 +28722,7 @@ var RenderModalAdvanced = ({
28443
28722
  });
28444
28723
  }, [chromeModeOption, setChromeModeOption]);
28445
28724
  const x264PresetOptions = useMemo131(() => {
28446
- return BrowserSafeApis5.x264PresetOptions.map((option) => {
28725
+ return BrowserSafeApis6.x264PresetOptions.map((option) => {
28447
28726
  return {
28448
28727
  label: labelx264Preset(option),
28449
28728
  onClick: () => setx264Preset(option),
@@ -28459,7 +28738,7 @@ var RenderModalAdvanced = ({
28459
28738
  });
28460
28739
  }, [setx264Preset, x264Preset]);
28461
28740
  const hardwareAccelerationValues = useMemo131(() => {
28462
- return BrowserSafeApis5.hardwareAccelerationOptions.map((option) => {
28741
+ return BrowserSafeApis6.hardwareAccelerationOptions.map((option) => {
28463
28742
  return {
28464
28743
  label: option,
28465
28744
  onClick: () => setHardwareAcceleration(option),
@@ -28950,7 +29229,7 @@ var RenderModalAdvanced = ({
28950
29229
  };
28951
29230
 
28952
29231
  // src/components/RenderModal/RenderModalAudio.tsx
28953
- import { BrowserSafeApis as BrowserSafeApis7 } from "@remotion/renderer/client";
29232
+ import { BrowserSafeApis as BrowserSafeApis8 } from "@remotion/renderer/client";
28954
29233
  import { useCallback as useCallback129 } from "react";
28955
29234
 
28956
29235
  // src/components/RenderModal/EnforceAudioTrackSetting.tsx
@@ -29043,7 +29322,7 @@ var MutedSetting = ({ muted, setMuted, enforceAudioTrack }) => {
29043
29322
  };
29044
29323
 
29045
29324
  // src/components/RenderModal/SeparateAudioOption.tsx
29046
- import { BrowserSafeApis as BrowserSafeApis6 } from "@remotion/renderer/client";
29325
+ import { BrowserSafeApis as BrowserSafeApis7 } from "@remotion/renderer/client";
29047
29326
  import { useCallback as useCallback128, useMemo as useMemo132 } from "react";
29048
29327
 
29049
29328
  // src/helpers/use-file-existence.ts
@@ -29178,7 +29457,7 @@ var SeparateAudioOptionInput = ({ separateAudioTo, setSeparateAudioTo, audioCode
29178
29457
  setSeparateAudioTo(e.target.value);
29179
29458
  }, [setSeparateAudioTo]);
29180
29459
  const validationMessage = useMemo132(() => {
29181
- const expectedExtension = BrowserSafeApis6.getExtensionFromAudioCodec(audioCodec);
29460
+ const expectedExtension = BrowserSafeApis7.getExtensionFromAudioCodec(audioCodec);
29182
29461
  const actualExtension = separateAudioTo.split(".").pop();
29183
29462
  if (actualExtension !== expectedExtension) {
29184
29463
  return `Expected extension: .${expectedExtension}`;
@@ -29197,7 +29476,7 @@ var SeparateAudioOptionInput = ({ separateAudioTo, setSeparateAudioTo, audioCode
29197
29476
  var SeparateAudioOption = ({ separateAudioTo, setSeparateAudioTo, audioCodec, outName }) => {
29198
29477
  const onSeparateAudioChange = useCallback128((e) => {
29199
29478
  if (e.target.checked) {
29200
- const extension = BrowserSafeApis6.getExtensionFromAudioCodec(audioCodec);
29479
+ const extension = BrowserSafeApis7.getExtensionFromAudioCodec(audioCodec);
29201
29480
  setSeparateAudioTo(`${getStringBeforeSuffix(outName)}.${extension}`);
29202
29481
  } else {
29203
29482
  setSeparateAudioTo(null);
@@ -29275,7 +29554,7 @@ var RenderModalAudio = ({
29275
29554
  setForSeamlessAacConcatenation(e.target.checked);
29276
29555
  }, [setForSeamlessAacConcatenation]);
29277
29556
  const audioCodecOptions = useCallback129((currentCodec) => {
29278
- return BrowserSafeApis7.supportedAudioCodecs[currentCodec].map((audioCodecOption) => {
29557
+ return BrowserSafeApis8.supportedAudioCodecs[currentCodec].map((audioCodecOption) => {
29279
29558
  return {
29280
29559
  label: humanReadableAudioCodec(audioCodecOption),
29281
29560
  onClick: () => setAudioCodec(audioCodecOption),
@@ -29420,7 +29699,7 @@ var RenderModalAudio = ({
29420
29699
  };
29421
29700
 
29422
29701
  // src/components/RenderModal/RenderModalBasic.tsx
29423
- import { BrowserSafeApis as BrowserSafeApis8 } from "@remotion/renderer/client";
29702
+ import { BrowserSafeApis as BrowserSafeApis9 } from "@remotion/renderer/client";
29424
29703
  import { NoReactAPIs as NoReactAPIs2 } from "@remotion/renderer/pure";
29425
29704
  import { useCallback as useCallback132, useMemo as useMemo134 } from "react";
29426
29705
 
@@ -29681,11 +29960,12 @@ var RenderModalBasic = ({
29681
29960
  startFrame,
29682
29961
  validationMessage,
29683
29962
  setVerboseLogging,
29684
- logLevel
29963
+ logLevel,
29964
+ showOutputName
29685
29965
  }) => {
29686
29966
  const existence = useFileExistence(outName);
29687
29967
  const videoCodecOptions = useMemo134(() => {
29688
- return BrowserSafeApis8.validCodecs.filter((c) => {
29968
+ return BrowserSafeApis9.validCodecs.filter((c) => {
29689
29969
  return NoReactAPIs2.isAudioCodec(c) === (renderMode === "audio");
29690
29970
  }).map((codecOption) => {
29691
29971
  return {
@@ -29703,7 +29983,7 @@ var RenderModalBasic = ({
29703
29983
  });
29704
29984
  }, [renderMode, setCodec, codec]);
29705
29985
  const proResProfileOptions = useMemo134(() => {
29706
- return BrowserSafeApis8.proResProfileOptions.map((option) => {
29986
+ return BrowserSafeApis9.proResProfileOptions.map((option) => {
29707
29987
  return {
29708
29988
  label: labelProResProfile(option),
29709
29989
  onClick: () => setProResProfile(option),
@@ -29842,14 +30122,14 @@ var RenderModalBasic = ({
29842
30122
  setStartFrame,
29843
30123
  startFrame
29844
30124
  }),
29845
- /* @__PURE__ */ jsx263(RenderModalOutputName, {
30125
+ showOutputName ? /* @__PURE__ */ jsx263(RenderModalOutputName, {
29846
30126
  existence,
29847
30127
  inputStyle: input,
29848
30128
  outName,
29849
30129
  onValueChange,
29850
30130
  validationMessage,
29851
30131
  label: renderMode === "sequence" ? "Folder name" : "Output name"
29852
- }),
30132
+ }) : null,
29853
30133
  /* @__PURE__ */ jsxs135("div", {
29854
30134
  style: optionRow,
29855
30135
  children: [
@@ -29987,7 +30267,7 @@ var RenderModalGif = ({
29987
30267
  };
29988
30268
 
29989
30269
  // src/components/RenderModal/RenderModalPicture.tsx
29990
- import { BrowserSafeApis as BrowserSafeApis9 } from "@remotion/renderer/client";
30270
+ import { BrowserSafeApis as BrowserSafeApis10 } from "@remotion/renderer/client";
29991
30271
  import { useCallback as useCallback135, useMemo as useMemo136 } from "react";
29992
30272
 
29993
30273
  // src/components/RenderModal/JpegQualitySetting.tsx
@@ -30091,7 +30371,7 @@ var RenderModalPicture = ({
30091
30371
  compositionHeight
30092
30372
  }) => {
30093
30373
  const colorSpaceOptions = useMemo136(() => {
30094
- return BrowserSafeApis9.validColorSpaces.map((option) => {
30374
+ return BrowserSafeApis10.validColorSpaces.map((option) => {
30095
30375
  return {
30096
30376
  label: option,
30097
30377
  onClick: () => setColorSpace(option),
@@ -30384,6 +30664,7 @@ var reducer2 = (state, action) => {
30384
30664
  return state;
30385
30665
  };
30386
30666
  var RenderModal = ({
30667
+ readOnlyStudio,
30387
30668
  initialFrame,
30388
30669
  initialVideoImageFormat,
30389
30670
  initialStillImageFormat,
@@ -30474,7 +30755,7 @@ var RenderModal = ({
30474
30755
  const [initialOutName] = useState88(() => {
30475
30756
  return getDefaultOutLocation({
30476
30757
  compositionName: resolvedComposition.id,
30477
- defaultExtension: initialRenderType === "still" ? initialStillImageFormat : isVideo ? BrowserSafeApis10.getFileExtensionFromCodec(initialVideoCodec, initialAudioCodec) : initialStillImageFormat,
30758
+ defaultExtension: initialRenderType === "still" ? initialStillImageFormat : isVideo ? BrowserSafeApis11.getFileExtensionFromCodec(initialVideoCodec, initialAudioCodec) : initialStillImageFormat,
30478
30759
  type: "asset",
30479
30760
  compositionDefaultOutName: resolvedComposition.defaultOutName,
30480
30761
  outputLocation: renderDefaults.outputLocation
@@ -30559,8 +30840,8 @@ var RenderModal = ({
30559
30840
  }
30560
30841
  return null;
30561
30842
  }, [customTargetAudioBitrate, shouldHaveCustomTargetAudioBitrate]);
30562
- const supportsCrf = BrowserSafeApis10.codecSupportsCrf(codec);
30563
- const supportsVideoBitrate = BrowserSafeApis10.codecSupportsVideoBitrate(codec);
30843
+ const supportsCrf = BrowserSafeApis11.codecSupportsCrf(codec);
30844
+ const supportsVideoBitrate = BrowserSafeApis11.codecSupportsVideoBitrate(codec);
30564
30845
  const supportsBothQualityControls = useMemo137(() => {
30565
30846
  return supportsCrf && supportsVideoBitrate && hardwareAcceleration !== "if-possible" && hardwareAcceleration !== "required";
30566
30847
  }, [hardwareAcceleration, supportsCrf, supportsVideoBitrate]);
@@ -30646,10 +30927,10 @@ var RenderModal = ({
30646
30927
  return Math.max(0, Math.min(resolvedComposition.durationInFrames - 1, parsed));
30647
30928
  }, [resolvedComposition.durationInFrames, unclampedFrame]);
30648
30929
  const deriveFinalAudioCodec = useCallback136((passedVideoCodec, passedAudioCodec) => {
30649
- if (passedAudioCodec !== null && BrowserSafeApis10.supportedAudioCodecs[passedVideoCodec].includes(passedAudioCodec)) {
30930
+ if (passedAudioCodec !== null && BrowserSafeApis11.supportedAudioCodecs[passedVideoCodec].includes(passedAudioCodec)) {
30650
30931
  return passedAudioCodec;
30651
30932
  }
30652
- return BrowserSafeApis10.defaultAudioCodecs[passedVideoCodec].compressed;
30933
+ return BrowserSafeApis11.defaultAudioCodecs[passedVideoCodec].compressed;
30653
30934
  }, []);
30654
30935
  const setDefaultOutName = useCallback136((options) => {
30655
30936
  if (options.type === "still") {
@@ -30664,7 +30945,7 @@ var RenderModal = ({
30664
30945
  });
30665
30946
  } else {
30666
30947
  setOutName((prev) => {
30667
- const codecSuffix = BrowserSafeApis10.getFileExtensionFromCodec(options.codec, deriveFinalAudioCodec(options.codec, options.audioCodec));
30948
+ const codecSuffix = BrowserSafeApis11.getFileExtensionFromCodec(options.codec, deriveFinalAudioCodec(options.codec, options.audioCodec));
30668
30949
  const newFileName = getStringBeforeSuffix(prev) + "." + codecSuffix;
30669
30950
  return newFileName;
30670
30951
  });
@@ -30681,7 +30962,7 @@ var RenderModal = ({
30681
30962
  if (prev === null) {
30682
30963
  return null;
30683
30964
  }
30684
- const newExtension = BrowserSafeApis10.getExtensionFromAudioCodec(newAudioCodec);
30965
+ const newExtension = BrowserSafeApis11.getExtensionFromAudioCodec(newAudioCodec);
30685
30966
  const newFileName = getStringBeforeSuffix(prev) + "." + newExtension;
30686
30967
  return newFileName;
30687
30968
  });
@@ -30770,7 +31051,7 @@ var RenderModal = ({
30770
31051
  }, [codec, everyNthFrameSetting]);
30771
31052
  const audioCodec = deriveFinalAudioCodec(codec, userSelectedAudioCodec);
30772
31053
  const availablePixelFormats = useMemo137(() => {
30773
- return BrowserSafeApis10.validPixelFormatsForCodec(codec);
31054
+ return BrowserSafeApis11.validPixelFormatsForCodec(codec);
30774
31055
  }, [codec]);
30775
31056
  const pixelFormat = useMemo137(() => {
30776
31057
  if (availablePixelFormats.includes(userPreferredPixelFormat)) {
@@ -31097,8 +31378,134 @@ var RenderModal = ({
31097
31378
  });
31098
31379
  const { tab, setTab, shownTabs } = useRenderModalSections(renderMode, codec);
31099
31380
  const { registerKeybinding } = useKeybinding();
31100
- const renderDisabled = state.type === "load" || !outnameValidation.valid;
31381
+ const readOnlyRenderCommand = useMemo137(() => {
31382
+ if (!readOnlyStudio) {
31383
+ return null;
31384
+ }
31385
+ return makeReadOnlyStudioRenderCommand({
31386
+ remotionVersion: window.remotion_version,
31387
+ locationHref: window.location.href,
31388
+ compositionId: resolvedComposition.id,
31389
+ outName,
31390
+ renderMode,
31391
+ renderDefaults,
31392
+ durationInFrames: resolvedComposition.durationInFrames,
31393
+ concurrency,
31394
+ frame: frame2,
31395
+ startFrame,
31396
+ endFrame,
31397
+ stillImageFormat,
31398
+ sequenceImageFormat,
31399
+ videoImageFormat,
31400
+ jpegQuality: renderMode === "video" ? stillImageFormat === "jpeg" ? jpegQuality : null : renderMode === "audio" ? null : jpegQuality,
31401
+ codec,
31402
+ muted,
31403
+ enforceAudioTrack,
31404
+ proResProfile,
31405
+ x264Preset,
31406
+ pixelFormat,
31407
+ crf: qualityControlType === "crf" && hardwareAcceleration !== "if-possible" && hardwareAcceleration !== "required" ? crf : null,
31408
+ videoBitrate,
31409
+ audioBitrate,
31410
+ audioCodec,
31411
+ everyNthFrame,
31412
+ numberOfGifLoops,
31413
+ disallowParallelEncoding,
31414
+ encodingBufferSize,
31415
+ encodingMaxRate,
31416
+ forSeamlessAacConcatenation,
31417
+ separateAudioTo,
31418
+ colorSpace,
31419
+ scale,
31420
+ logLevel,
31421
+ delayRenderTimeout,
31422
+ hardwareAcceleration,
31423
+ chromeMode,
31424
+ headless,
31425
+ disableWebSecurity,
31426
+ ignoreCertificateErrors,
31427
+ gl: openGlOption === "default" ? null : openGlOption,
31428
+ userAgent,
31429
+ multiProcessOnLinux,
31430
+ darkMode,
31431
+ offthreadVideoCacheSizeInBytes,
31432
+ offthreadVideoThreads,
31433
+ mediaCacheSizeInBytes,
31434
+ beepOnFinish,
31435
+ repro,
31436
+ metadata,
31437
+ envVariables: envVariablesArrayToObject(envVariables),
31438
+ inputProps
31439
+ });
31440
+ }, [
31441
+ audioBitrate,
31442
+ audioCodec,
31443
+ beepOnFinish,
31444
+ chromeMode,
31445
+ codec,
31446
+ colorSpace,
31447
+ concurrency,
31448
+ crf,
31449
+ darkMode,
31450
+ delayRenderTimeout,
31451
+ disableWebSecurity,
31452
+ disallowParallelEncoding,
31453
+ endFrame,
31454
+ encodingBufferSize,
31455
+ encodingMaxRate,
31456
+ enforceAudioTrack,
31457
+ envVariables,
31458
+ everyNthFrame,
31459
+ frame2,
31460
+ forSeamlessAacConcatenation,
31461
+ hardwareAcceleration,
31462
+ headless,
31463
+ ignoreCertificateErrors,
31464
+ inputProps,
31465
+ jpegQuality,
31466
+ logLevel,
31467
+ mediaCacheSizeInBytes,
31468
+ metadata,
31469
+ multiProcessOnLinux,
31470
+ muted,
31471
+ numberOfGifLoops,
31472
+ offthreadVideoCacheSizeInBytes,
31473
+ offthreadVideoThreads,
31474
+ openGlOption,
31475
+ outName,
31476
+ pixelFormat,
31477
+ proResProfile,
31478
+ qualityControlType,
31479
+ readOnlyStudio,
31480
+ renderDefaults,
31481
+ renderMode,
31482
+ repro,
31483
+ resolvedComposition.durationInFrames,
31484
+ resolvedComposition.id,
31485
+ scale,
31486
+ separateAudioTo,
31487
+ sequenceImageFormat,
31488
+ startFrame,
31489
+ stillImageFormat,
31490
+ userAgent,
31491
+ videoImageFormat,
31492
+ videoBitrate,
31493
+ x264Preset
31494
+ ]);
31495
+ const [commandCopiedAt, setCommandCopiedAt] = useState88(null);
31496
+ const renderDisabled = readOnlyStudio ? false : !outnameValidation.valid || state.type === "load";
31101
31497
  const trigger = useCallback136(() => {
31498
+ if (readOnlyStudio) {
31499
+ if (!readOnlyRenderCommand) {
31500
+ return;
31501
+ }
31502
+ copyText(readOnlyRenderCommand).then(() => {
31503
+ setCommandCopiedAt(Date.now());
31504
+ }).catch((err) => {
31505
+ showNotification(`Could not copy: ${err.message}`, 2000);
31506
+ });
31507
+ return;
31508
+ }
31102
31509
  if (renderMode === "still") {
31103
31510
  onClickStill();
31104
31511
  } else if (renderMode === "sequence") {
@@ -31106,7 +31513,23 @@ var RenderModal = ({
31106
31513
  } else {
31107
31514
  onClickVideo();
31108
31515
  }
31109
- }, [renderMode, onClickStill, onClickSequence, onClickVideo]);
31516
+ }, [
31517
+ onClickSequence,
31518
+ onClickStill,
31519
+ onClickVideo,
31520
+ readOnlyRenderCommand,
31521
+ readOnlyStudio,
31522
+ renderMode
31523
+ ]);
31524
+ useEffect85(() => {
31525
+ if (commandCopiedAt === null) {
31526
+ return;
31527
+ }
31528
+ const timeout = setTimeout(() => {
31529
+ setCommandCopiedAt(null);
31530
+ }, 2000);
31531
+ return () => clearTimeout(timeout);
31532
+ }, [commandCopiedAt]);
31110
31533
  useEffect85(() => {
31111
31534
  if (renderDisabled) {
31112
31535
  return;
@@ -31167,7 +31590,7 @@ var RenderModal = ({
31167
31590
  backgroundColor: outnameValidation.valid ? BLUE : BLUE_DISABLED
31168
31591
  },
31169
31592
  children: [
31170
- state.type === "idle" ? `Render ${renderMode}` : "Rendering...",
31593
+ readOnlyStudio ? commandCopiedAt ? "Copied command!" : "Copy command" : state.type === "idle" ? `Render ${renderMode}` : "Rendering...",
31171
31594
  /* @__PURE__ */ jsx269(ShortcutHint, {
31172
31595
  keyToPress: "↵",
31173
31596
  cmdOrCtrl: true
@@ -31288,6 +31711,7 @@ var RenderModal = ({
31288
31711
  setStartFrame,
31289
31712
  setVerboseLogging: setLogLevel,
31290
31713
  logLevel,
31714
+ showOutputName: !readOnlyStudio,
31291
31715
  startFrame,
31292
31716
  validationMessage: outnameValidation.valid ? null : outnameValidation.error.message
31293
31717
  }) : tab === "picture" ? /* @__PURE__ */ jsx269(RenderModalPicture, {
@@ -31351,7 +31775,7 @@ var RenderModal = ({
31351
31775
  propsEditType: "input-props",
31352
31776
  saving,
31353
31777
  setSaving,
31354
- readOnlyStudio: false
31778
+ readOnlyStudio
31355
31779
  }) : /* @__PURE__ */ jsx269(RenderModalAdvanced, {
31356
31780
  x264Preset,
31357
31781
  setx264Preset,
@@ -31817,6 +32241,7 @@ var WebRenderModalAudio = ({
31817
32241
  const audioBitrateOptions = useMemo139(() => getQualityOptions(audioBitrate, setAudioBitrate), [audioBitrate, setAudioBitrate]);
31818
32242
  const isAudioOnly = renderMode === "audio";
31819
32243
  const showAudioSettings = isAudioOnly || !muted;
32244
+ const showAudioCodecSetting = !isAudioOnly || containerSupported.length > 1;
31820
32245
  return /* @__PURE__ */ jsxs143("div", {
31821
32246
  style: container62,
31822
32247
  className: VERTICAL_SCROLLBAR_CLASSNAME,
@@ -31851,7 +32276,7 @@ var WebRenderModalAudio = ({
31851
32276
  })
31852
32277
  ]
31853
32278
  }),
31854
- /* @__PURE__ */ jsxs143("div", {
32279
+ showAudioCodecSetting ? /* @__PURE__ */ jsxs143("div", {
31855
32280
  style: optionRow,
31856
32281
  children: [
31857
32282
  /* @__PURE__ */ jsxs143("div", {
@@ -31872,8 +32297,8 @@ var WebRenderModalAudio = ({
31872
32297
  })
31873
32298
  })
31874
32299
  ]
31875
- }),
31876
- effectiveAudioCodec !== audioCodec ? /* @__PURE__ */ jsxs143("div", {
32300
+ }) : null,
32301
+ showAudioCodecSetting && effectiveAudioCodec !== audioCodec ? /* @__PURE__ */ jsxs143("div", {
31877
32302
  style: fallbackNoticeStyle,
31878
32303
  children: [
31879
32304
  humanReadableWebAudioCodec(audioCodec),
@@ -33543,7 +33968,8 @@ var Modals = ({ readOnlyStudio }) => {
33543
33968
  modalContextType && modalContextType.type === "web-render" && /* @__PURE__ */ jsx285(WebRenderModalWithLoader, {
33544
33969
  ...modalContextType
33545
33970
  }),
33546
- modalContextType && canRender && modalContextType.type === "server-render" && /* @__PURE__ */ jsx285(RenderModalWithLoader, {
33971
+ modalContextType && modalContextType.type === "server-render" && (canRender || modalContextType.readOnlyStudio) ? /* @__PURE__ */ jsx285(RenderModalWithLoader, {
33972
+ readOnlyStudio: modalContextType.readOnlyStudio ?? false,
33547
33973
  initialFrame: modalContextType.initialFrame,
33548
33974
  initialDarkMode: modalContextType.initialDarkMode,
33549
33975
  compositionId: modalContextType.compositionId,
@@ -33591,7 +34017,7 @@ var Modals = ({ readOnlyStudio }) => {
33591
34017
  initialHardwareAcceleration: modalContextType.initialHardwareAcceleration,
33592
34018
  initialChromeMode: modalContextType.initialChromeMode,
33593
34019
  renderDefaults: modalContextType.renderDefaults
33594
- }),
34020
+ }) : null,
33595
34021
  modalContextType && modalContextType.type === "render-progress" && /* @__PURE__ */ jsx285(RenderStatusModal, {
33596
34022
  jobId: modalContextType.jobId
33597
34023
  }),