@remotion/studio 4.0.433 → 4.0.435

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.
Files changed (34) hide show
  1. package/dist/components/CompositionSelector.js +36 -2
  2. package/dist/components/CompositionSelectorItem.js +1 -0
  3. package/dist/components/CurrentComposition.d.ts +1 -1
  4. package/dist/components/CurrentComposition.js +2 -3
  5. package/dist/components/CurrentCompositionSideEffects.js +2 -4
  6. package/dist/components/Modals.js +3 -2
  7. package/dist/components/PreviewToolbar.js +1 -2
  8. package/dist/components/RenderButton.d.ts +1 -1
  9. package/dist/components/RenderButton.js +64 -17
  10. package/dist/components/RenderModal/ClientRenderProgress.js +13 -9
  11. package/dist/components/RenderModal/RenderModalBasic.d.ts +1 -0
  12. package/dist/components/RenderModal/RenderModalBasic.js +2 -2
  13. package/dist/components/RenderModal/ServerRenderModal.d.ts +1 -0
  14. package/dist/components/RenderModal/ServerRenderModal.js +170 -5
  15. package/dist/components/RenderModal/WebRenderModalAudio.js +3 -2
  16. package/dist/components/RenderQueue/ClientRenderQueueProcessor.js +3 -0
  17. package/dist/components/RenderQueue/RenderQueueItemStatus.js +1 -2
  18. package/dist/components/RenderQueue/RenderQueueProgressMessage.js +2 -3
  19. package/dist/components/RenderQueue/client-render-progress.d.ts +3 -0
  20. package/dist/components/RenderQueue/client-render-progress.js +31 -0
  21. package/dist/components/RenderQueue/client-side-render-types.d.ts +3 -0
  22. package/dist/components/RenderQueue/context.js +7 -1
  23. package/dist/components/SidebarRenderButton.js +1 -0
  24. package/dist/esm/{chunk-bd1bkakk.js → chunk-x88z6n54.js} +1063 -535
  25. package/dist/esm/internals.mjs +1063 -535
  26. package/dist/esm/previewEntry.mjs +634 -106
  27. package/dist/esm/renderEntry.mjs +1 -1
  28. package/dist/helpers/make-render-command.d.ts +51 -0
  29. package/dist/helpers/make-render-command.js +201 -0
  30. package/dist/helpers/retry-payload.js +3 -0
  31. package/dist/state/modals.d.ts +1 -0
  32. package/package.json +9 -9
  33. package/dist/helpers/should-show-render-button.d.ts +0 -1
  34. package/dist/helpers/should-show-render-button.js +0 -11
@@ -8,6 +8,8 @@ const react_1 = require("react");
8
8
  const ShortcutHint_1 = require("../../error-overlay/remotion-overlay/ShortcutHint");
9
9
  const colors_1 = require("../../helpers/colors");
10
10
  const convert_env_variables_1 = require("../../helpers/convert-env-variables");
11
+ const copy_text_1 = require("../../helpers/copy-text");
12
+ const make_render_command_1 = require("../../helpers/make-render-command");
11
13
  const render_modal_sections_1 = require("../../helpers/render-modal-sections");
12
14
  const use_keybinding_1 = require("../../helpers/use-keybinding");
13
15
  const audio_1 = require("../../icons/audio");
@@ -23,6 +25,7 @@ const Button_1 = require("../Button");
23
25
  const is_menu_item_1 = require("../Menu/is-menu-item");
24
26
  const ModalHeader_1 = require("../ModalHeader");
25
27
  const DismissableModal_1 = require("../NewComposition/DismissableModal");
28
+ const NotificationCenter_1 = require("../Notifications/NotificationCenter");
26
29
  const OptionsPanel_1 = require("../OptionsPanel");
27
30
  const actions_1 = require("../RenderQueue/actions");
28
31
  const SegmentedControl_1 = require("../SegmentedControl");
@@ -58,7 +61,7 @@ const reducer = (state, action) => {
58
61
  }
59
62
  return state;
60
63
  };
61
- const RenderModal = ({ initialFrame, initialVideoImageFormat, initialStillImageFormat, initialJpegQuality, initialScale, initialLogLevel, initialConcurrency, maxConcurrency, minConcurrency, initialMuted, initialEnforceAudioTrack, initialProResProfile, initialx264Preset, initialPixelFormat, initialVideoBitrate, initialAudioBitrate, initialEveryNthFrame, initialNumberOfGifLoops, initialDelayRenderTimeout, initialOffthreadVideoCacheSizeInBytes, initialEnvVariables, initialDisableWebSecurity, initialGl, initialHeadless, initialIgnoreCertificateErrors, initialEncodingBufferSize, initialEncodingMaxRate, initialOffthreadVideoThreads, initialMediaCacheSizeInBytes, initialDarkMode, initialUserAgent, defaultProps, inFrameMark, outFrameMark, initialColorSpace, initialMultiProcessOnLinux, defaultConfigurationAudioCodec, defaultConfigurationVideoCodec, initialBeep, initialRepro, initialForSeamlessAacConcatenation, renderTypeOfLastRender, initialHardwareAcceleration, defaultMetadata, initialChromeMode, renderDefaults, }) => {
64
+ const RenderModal = ({ readOnlyStudio, initialFrame, initialVideoImageFormat, initialStillImageFormat, initialJpegQuality, initialScale, initialLogLevel, initialConcurrency, maxConcurrency, minConcurrency, initialMuted, initialEnforceAudioTrack, initialProResProfile, initialx264Preset, initialPixelFormat, initialVideoBitrate, initialAudioBitrate, initialEveryNthFrame, initialNumberOfGifLoops, initialDelayRenderTimeout, initialOffthreadVideoCacheSizeInBytes, initialEnvVariables, initialDisableWebSecurity, initialGl, initialHeadless, initialIgnoreCertificateErrors, initialEncodingBufferSize, initialEncodingMaxRate, initialOffthreadVideoThreads, initialMediaCacheSizeInBytes, initialDarkMode, initialUserAgent, defaultProps, inFrameMark, outFrameMark, initialColorSpace, initialMultiProcessOnLinux, defaultConfigurationAudioCodec, defaultConfigurationVideoCodec, initialBeep, initialRepro, initialForSeamlessAacConcatenation, renderTypeOfLastRender, initialHardwareAcceleration, defaultMetadata, initialChromeMode, renderDefaults, }) => {
62
65
  const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
63
66
  const context = (0, react_1.useContext)(ResolveCompositionBeforeModal_1.ResolvedCompositionContext);
64
67
  if (!context) {
@@ -753,8 +756,148 @@ const RenderModal = ({ initialFrame, initialVideoImageFormat, initialStillImageF
753
756
  });
754
757
  const { tab, setTab, shownTabs } = (0, render_modal_sections_1.useRenderModalSections)(renderMode, codec);
755
758
  const { registerKeybinding } = (0, use_keybinding_1.useKeybinding)();
756
- const renderDisabled = state.type === 'load' || !outnameValidation.valid;
759
+ const readOnlyRenderCommand = (0, react_1.useMemo)(() => {
760
+ if (!readOnlyStudio) {
761
+ return null;
762
+ }
763
+ return (0, make_render_command_1.makeReadOnlyStudioRenderCommand)({
764
+ remotionVersion: window.remotion_version,
765
+ locationHref: window.location.href,
766
+ compositionId: resolvedComposition.id,
767
+ outName,
768
+ renderMode,
769
+ renderDefaults,
770
+ durationInFrames: resolvedComposition.durationInFrames,
771
+ concurrency,
772
+ frame,
773
+ startFrame,
774
+ endFrame,
775
+ stillImageFormat,
776
+ sequenceImageFormat,
777
+ videoImageFormat,
778
+ jpegQuality: renderMode === 'video'
779
+ ? stillImageFormat === 'jpeg'
780
+ ? jpegQuality
781
+ : null
782
+ : renderMode === 'audio'
783
+ ? null
784
+ : jpegQuality,
785
+ codec,
786
+ muted,
787
+ enforceAudioTrack,
788
+ proResProfile,
789
+ x264Preset,
790
+ pixelFormat,
791
+ crf: qualityControlType === 'crf' &&
792
+ hardwareAcceleration !== 'if-possible' &&
793
+ hardwareAcceleration !== 'required'
794
+ ? crf
795
+ : null,
796
+ videoBitrate,
797
+ audioBitrate,
798
+ audioCodec,
799
+ everyNthFrame,
800
+ numberOfGifLoops,
801
+ disallowParallelEncoding,
802
+ encodingBufferSize,
803
+ encodingMaxRate,
804
+ forSeamlessAacConcatenation,
805
+ separateAudioTo,
806
+ colorSpace,
807
+ scale,
808
+ logLevel,
809
+ delayRenderTimeout,
810
+ hardwareAcceleration,
811
+ chromeMode,
812
+ headless,
813
+ disableWebSecurity,
814
+ ignoreCertificateErrors,
815
+ gl: openGlOption === 'default' ? null : openGlOption,
816
+ userAgent,
817
+ multiProcessOnLinux,
818
+ darkMode,
819
+ offthreadVideoCacheSizeInBytes,
820
+ offthreadVideoThreads,
821
+ mediaCacheSizeInBytes,
822
+ beepOnFinish,
823
+ repro,
824
+ metadata,
825
+ envVariables: (0, convert_env_variables_1.envVariablesArrayToObject)(envVariables),
826
+ inputProps,
827
+ });
828
+ }, [
829
+ audioBitrate,
830
+ audioCodec,
831
+ beepOnFinish,
832
+ chromeMode,
833
+ codec,
834
+ colorSpace,
835
+ concurrency,
836
+ crf,
837
+ darkMode,
838
+ delayRenderTimeout,
839
+ disableWebSecurity,
840
+ disallowParallelEncoding,
841
+ endFrame,
842
+ encodingBufferSize,
843
+ encodingMaxRate,
844
+ enforceAudioTrack,
845
+ envVariables,
846
+ everyNthFrame,
847
+ frame,
848
+ forSeamlessAacConcatenation,
849
+ hardwareAcceleration,
850
+ headless,
851
+ ignoreCertificateErrors,
852
+ inputProps,
853
+ jpegQuality,
854
+ logLevel,
855
+ mediaCacheSizeInBytes,
856
+ metadata,
857
+ multiProcessOnLinux,
858
+ muted,
859
+ numberOfGifLoops,
860
+ offthreadVideoCacheSizeInBytes,
861
+ offthreadVideoThreads,
862
+ openGlOption,
863
+ outName,
864
+ pixelFormat,
865
+ proResProfile,
866
+ qualityControlType,
867
+ readOnlyStudio,
868
+ renderDefaults,
869
+ renderMode,
870
+ repro,
871
+ resolvedComposition.durationInFrames,
872
+ resolvedComposition.id,
873
+ scale,
874
+ separateAudioTo,
875
+ sequenceImageFormat,
876
+ startFrame,
877
+ stillImageFormat,
878
+ userAgent,
879
+ videoImageFormat,
880
+ videoBitrate,
881
+ x264Preset,
882
+ ]);
883
+ const [commandCopiedAt, setCommandCopiedAt] = (0, react_1.useState)(null);
884
+ const renderDisabled = readOnlyStudio
885
+ ? false
886
+ : !outnameValidation.valid || state.type === 'load';
757
887
  const trigger = (0, react_1.useCallback)(() => {
888
+ if (readOnlyStudio) {
889
+ if (!readOnlyRenderCommand) {
890
+ return;
891
+ }
892
+ (0, copy_text_1.copyText)(readOnlyRenderCommand)
893
+ .then(() => {
894
+ setCommandCopiedAt(Date.now());
895
+ })
896
+ .catch((err) => {
897
+ (0, NotificationCenter_1.showNotification)(`Could not copy: ${err.message}`, 2000);
898
+ });
899
+ return;
900
+ }
758
901
  if (renderMode === 'still') {
759
902
  onClickStill();
760
903
  }
@@ -764,7 +907,23 @@ const RenderModal = ({ initialFrame, initialVideoImageFormat, initialStillImageF
764
907
  else {
765
908
  onClickVideo();
766
909
  }
767
- }, [renderMode, onClickStill, onClickSequence, onClickVideo]);
910
+ }, [
911
+ onClickSequence,
912
+ onClickStill,
913
+ onClickVideo,
914
+ readOnlyRenderCommand,
915
+ readOnlyStudio,
916
+ renderMode,
917
+ ]);
918
+ (0, react_1.useEffect)(() => {
919
+ if (commandCopiedAt === null) {
920
+ return;
921
+ }
922
+ const timeout = setTimeout(() => {
923
+ setCommandCopiedAt(null);
924
+ }, 2000);
925
+ return () => clearTimeout(timeout);
926
+ }, [commandCopiedAt]);
768
927
  (0, react_1.useEffect)(() => {
769
928
  if (renderDisabled) {
770
929
  return;
@@ -805,7 +964,13 @@ const RenderModal = ({ initialFrame, initialVideoImageFormat, initialStillImageF
805
964
  jsx_runtime_1.jsx(SegmentedControl_1.SegmentedControl, { items: renderTabOptions, needsWrapping: false }), jsx_runtime_1.jsx("div", { style: render_modals_1.flexer }), jsx_runtime_1.jsxs(Button_1.Button, { autoFocus: true, onClick: trigger, disabled: renderDisabled, style: {
806
965
  ...render_modals_1.buttonStyle,
807
966
  backgroundColor: outnameValidation.valid ? colors_1.BLUE : colors_1.BLUE_DISABLED,
808
- }, children: [state.type === 'idle' ? `Render ${renderMode}` : 'Rendering...', jsx_runtime_1.jsx(ShortcutHint_1.ShortcutHint, { keyToPress: "\u21B5", cmdOrCtrl: true })
967
+ }, children: [readOnlyStudio
968
+ ? commandCopiedAt
969
+ ? 'Copied command!'
970
+ : 'Copy command'
971
+ : state.type === 'idle'
972
+ ? `Render ${renderMode}`
973
+ : 'Rendering...', jsx_runtime_1.jsx(ShortcutHint_1.ShortcutHint, { keyToPress: "\u21B5", cmdOrCtrl: true })
809
974
  ] })
810
975
  ] }), jsx_runtime_1.jsxs("div", { style: render_modals_1.horizontalLayout, children: [
811
976
  jsx_runtime_1.jsxs("div", { style: render_modals_1.leftSidebar, children: [shownTabs.includes('general') ? (jsx_runtime_1.jsxs(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'general', onClick: () => setTab('general'), children: [
@@ -820,7 +985,7 @@ const RenderModal = ({ initialFrame, initialVideoImageFormat, initialStillImageF
820
985
  jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(gif_1.GifIcon, { style: render_modals_1.icon }) }),
821
986
  "GIF"] })) : null, shownTabs.includes('advanced') ? (jsx_runtime_1.jsxs(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'advanced', onClick: () => setTab('advanced'), children: [
822
987
  jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(gear_1.GearIcon, { style: render_modals_1.icon }) }),
823
- "Other"] })) : null] }), jsx_runtime_1.jsx("div", { style: render_modals_1.optionsPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: tab === 'general' ? (jsx_runtime_1.jsx(RenderModalBasic_1.RenderModalBasic, { codec: codec, resolvedComposition: resolvedComposition, frame: frame, imageFormatOptions: imageFormatOptions, outName: outName, proResProfile: proResProfile, renderMode: renderMode, setVideoCodec: setCodec, setFrame: setFrame, setOutName: setOutName, setProResProfile: setProResProfile, endFrame: endFrame, setEndFrame: setEndFrame, setStartFrame: setStartFrame, setVerboseLogging: setLogLevel, logLevel: logLevel, startFrame: startFrame, validationMessage: outnameValidation.valid ? null : outnameValidation.error.message })) : tab === 'picture' ? (jsx_runtime_1.jsx(RenderModalPicture_1.RenderModalPicture, { renderMode: renderMode, scale: scale, setScale: setScale, pixelFormat: pixelFormat, pixelFormatOptions: pixelFormatOptions, imageFormatOptions: imageFormatOptions, crf: crf, setCrf: setCrf, customTargetVideoBitrate: customTargetVideoBitrate, maxCrf: maxCrf, minCrf: minCrf, jpegQuality: jpegQuality, qualityControlType: qualityControlType, setJpegQuality: setJpegQuality, setColorSpace: setColorSpace, colorSpace: colorSpace, setCustomTargetVideoBitrateValue: setCustomTargetVideoBitrateValue, setQualityControl: setQualityControl, videoImageFormat: videoImageFormat, stillImageFormat: stillImageFormat, shouldDisplayQualityControlPicker: supportsBothQualityControls, encodingBufferSize: encodingBufferSize, setEncodingBufferSize: setEncodingBufferSize, encodingMaxRate: encodingMaxRate, setEncodingMaxRate: setEncodingMaxRate, compositionWidth: resolvedComposition.width, compositionHeight: resolvedComposition.height })) : tab === 'audio' ? (jsx_runtime_1.jsx(RenderModalAudio_1.RenderModalAudio, { muted: muted, renderMode: renderMode, setMuted: setMuted, codec: codec, audioCodec: audioCodec, setAudioCodec: setAudioCodec, enforceAudioTrack: enforceAudioTrack, setEnforceAudioTrackState: setEnforceAudioTrackState, customTargetAudioBitrate: customTargetAudioBitrate, setCustomTargetAudioBitrateValue: setCustomTargetAudioBitrateValue, setShouldHaveCustomTargetAudioBitrate: setShouldHaveCustomTargetAudioBitrate, shouldHaveCustomTargetAudioBitrate: shouldHaveCustomTargetAudioBitrate, forSeamlessAacConcatenation: forSeamlessAacConcatenation, setForSeamlessAacConcatenation: setForSeamlessAacConcatenation, separateAudioTo: separateAudioTo, setSeparateAudioTo: setSeparateAudioTo, outName: outName })) : tab === 'gif' ? (jsx_runtime_1.jsx(RenderModalGif_1.RenderModalGif, { everyNthFrame: everyNthFrame, limitNumberOfGifLoops: limitNumberOfGifLoops, numberOfGifLoopsSetting: numberOfGifLoopsSetting, setEveryNthFrameSetting: setEveryNthFrameSetting, setLimitNumberOfGifLoops: setLimitNumberOfGifLoops, setNumberOfGifLoopsSetting: setNumberOfGifLoopsSetting })) : tab === 'data' ? (jsx_runtime_1.jsx(DataEditor_1.DataEditor, { defaultProps: inputProps, setDefaultProps: setInputProps, unresolvedComposition: unresolvedComposition, mayShowSaveButton: false, propsEditType: "input-props", saving: saving, setSaving: setSaving, readOnlyStudio: false })) : (jsx_runtime_1.jsx(RenderModalAdvanced_1.RenderModalAdvanced, { x264Preset: x264Preset, setx264Preset: setx264Preset, concurrency: concurrency, maxConcurrency: maxConcurrency, minConcurrency: minConcurrency, renderMode: renderMode, setConcurrency: setConcurrency, delayRenderTimeout: delayRenderTimeout, setDelayRenderTimeout: setDelayRenderTimeout, disallowParallelEncoding: disallowParallelEncoding, setDisallowParallelEncoding: setDisallowParallelEncoding, setDisableWebSecurity: setDisableWebSecurity, setIgnoreCertificateErrors: setIgnoreCertificateErrors, setHeadless: setHeadless, headless: headless, ignoreCertificateErrors: ignoreCertificateErrors, disableWebSecurity: disableWebSecurity, openGlOption: openGlOption, setOpenGlOption: setOpenGlOption, setEnvVariables: setEnvVariables, envVariables: envVariables, offthreadVideoCacheSizeInBytes: offthreadVideoCacheSizeInBytes, setMediaCacheSizeInBytes: setMediaCacheSizeInBytes, mediaCacheSizeInBytes: mediaCacheSizeInBytes, setOffthreadVideoCacheSizeInBytes: setOffthreadVideoCacheSizeInBytes, offthreadVideoThreads: offthreadVideoThreads, setOffthreadVideoThreads: setOffthreadVideoThreads, enableMultiProcessOnLinux: multiProcessOnLinux, setChromiumMultiProcessOnLinux: setChromiumMultiProcessOnLinux, codec: codec, userAgent: userAgent, setUserAgent: setUserAgent, setBeep: setBeepOnFinish, beep: beepOnFinish, repro: repro, setRepro: setRepro, hardwareAcceleration: hardwareAcceleration, setHardwareAcceleration: setHardwareAcceleration, chromeModeOption: chromeMode, setChromeModeOption: setChromeMode, darkMode: darkMode, setDarkMode: setDarkMode })) })
988
+ "Other"] })) : null] }), jsx_runtime_1.jsx("div", { style: render_modals_1.optionsPanel, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: tab === 'general' ? (jsx_runtime_1.jsx(RenderModalBasic_1.RenderModalBasic, { codec: codec, resolvedComposition: resolvedComposition, frame: frame, imageFormatOptions: imageFormatOptions, outName: outName, proResProfile: proResProfile, renderMode: renderMode, setVideoCodec: setCodec, setFrame: setFrame, setOutName: setOutName, setProResProfile: setProResProfile, endFrame: endFrame, setEndFrame: setEndFrame, setStartFrame: setStartFrame, setVerboseLogging: setLogLevel, logLevel: logLevel, showOutputName: !readOnlyStudio, startFrame: startFrame, validationMessage: outnameValidation.valid ? null : outnameValidation.error.message })) : tab === 'picture' ? (jsx_runtime_1.jsx(RenderModalPicture_1.RenderModalPicture, { renderMode: renderMode, scale: scale, setScale: setScale, pixelFormat: pixelFormat, pixelFormatOptions: pixelFormatOptions, imageFormatOptions: imageFormatOptions, crf: crf, setCrf: setCrf, customTargetVideoBitrate: customTargetVideoBitrate, maxCrf: maxCrf, minCrf: minCrf, jpegQuality: jpegQuality, qualityControlType: qualityControlType, setJpegQuality: setJpegQuality, setColorSpace: setColorSpace, colorSpace: colorSpace, setCustomTargetVideoBitrateValue: setCustomTargetVideoBitrateValue, setQualityControl: setQualityControl, videoImageFormat: videoImageFormat, stillImageFormat: stillImageFormat, shouldDisplayQualityControlPicker: supportsBothQualityControls, encodingBufferSize: encodingBufferSize, setEncodingBufferSize: setEncodingBufferSize, encodingMaxRate: encodingMaxRate, setEncodingMaxRate: setEncodingMaxRate, compositionWidth: resolvedComposition.width, compositionHeight: resolvedComposition.height })) : tab === 'audio' ? (jsx_runtime_1.jsx(RenderModalAudio_1.RenderModalAudio, { muted: muted, renderMode: renderMode, setMuted: setMuted, codec: codec, audioCodec: audioCodec, setAudioCodec: setAudioCodec, enforceAudioTrack: enforceAudioTrack, setEnforceAudioTrackState: setEnforceAudioTrackState, customTargetAudioBitrate: customTargetAudioBitrate, setCustomTargetAudioBitrateValue: setCustomTargetAudioBitrateValue, setShouldHaveCustomTargetAudioBitrate: setShouldHaveCustomTargetAudioBitrate, shouldHaveCustomTargetAudioBitrate: shouldHaveCustomTargetAudioBitrate, forSeamlessAacConcatenation: forSeamlessAacConcatenation, setForSeamlessAacConcatenation: setForSeamlessAacConcatenation, separateAudioTo: separateAudioTo, setSeparateAudioTo: setSeparateAudioTo, outName: outName })) : tab === 'gif' ? (jsx_runtime_1.jsx(RenderModalGif_1.RenderModalGif, { everyNthFrame: everyNthFrame, limitNumberOfGifLoops: limitNumberOfGifLoops, numberOfGifLoopsSetting: numberOfGifLoopsSetting, setEveryNthFrameSetting: setEveryNthFrameSetting, setLimitNumberOfGifLoops: setLimitNumberOfGifLoops, setNumberOfGifLoopsSetting: setNumberOfGifLoopsSetting })) : tab === 'data' ? (jsx_runtime_1.jsx(DataEditor_1.DataEditor, { defaultProps: inputProps, setDefaultProps: setInputProps, unresolvedComposition: unresolvedComposition, mayShowSaveButton: false, propsEditType: "input-props", saving: saving, setSaving: setSaving, readOnlyStudio: readOnlyStudio })) : (jsx_runtime_1.jsx(RenderModalAdvanced_1.RenderModalAdvanced, { x264Preset: x264Preset, setx264Preset: setx264Preset, concurrency: concurrency, maxConcurrency: maxConcurrency, minConcurrency: minConcurrency, renderMode: renderMode, setConcurrency: setConcurrency, delayRenderTimeout: delayRenderTimeout, setDelayRenderTimeout: setDelayRenderTimeout, disallowParallelEncoding: disallowParallelEncoding, setDisallowParallelEncoding: setDisallowParallelEncoding, setDisableWebSecurity: setDisableWebSecurity, setIgnoreCertificateErrors: setIgnoreCertificateErrors, setHeadless: setHeadless, headless: headless, ignoreCertificateErrors: ignoreCertificateErrors, disableWebSecurity: disableWebSecurity, openGlOption: openGlOption, setOpenGlOption: setOpenGlOption, setEnvVariables: setEnvVariables, envVariables: envVariables, offthreadVideoCacheSizeInBytes: offthreadVideoCacheSizeInBytes, setMediaCacheSizeInBytes: setMediaCacheSizeInBytes, mediaCacheSizeInBytes: mediaCacheSizeInBytes, setOffthreadVideoCacheSizeInBytes: setOffthreadVideoCacheSizeInBytes, offthreadVideoThreads: offthreadVideoThreads, setOffthreadVideoThreads: setOffthreadVideoThreads, enableMultiProcessOnLinux: multiProcessOnLinux, setChromiumMultiProcessOnLinux: setChromiumMultiProcessOnLinux, codec: codec, userAgent: userAgent, setUserAgent: setUserAgent, setBeep: setBeepOnFinish, beep: beepOnFinish, repro: repro, setRepro: setRepro, hardwareAcceleration: hardwareAcceleration, setHardwareAcceleration: setHardwareAcceleration, chromeModeOption: chromeMode, setChromeModeOption: setChromeMode, darkMode: darkMode, setDarkMode: setDarkMode })) })
824
989
  ] })
825
990
  ] }));
826
991
  };
@@ -68,14 +68,15 @@ const WebRenderModalAudio = ({ renderMode, muted, setMuted, audioCodec, setAudio
68
68
  const audioBitrateOptions = (0, react_1.useMemo)(() => (0, quality_options_1.getQualityOptions)(audioBitrate, setAudioBitrate), [audioBitrate, setAudioBitrate]);
69
69
  const isAudioOnly = renderMode === 'audio';
70
70
  const showAudioSettings = isAudioOnly || !muted;
71
+ const showAudioCodecSetting = !isAudioOnly || containerSupported.length > 1;
71
72
  return (jsx_runtime_1.jsxs("div", { style: container, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME, children: [isAudioOnly ? null : (jsx_runtime_1.jsx(MutedSetting_1.MutedSetting, { enforceAudioTrack: false, muted: muted, setMuted: setMuted })), showAudioSettings ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [isAudioOnly ? null : jsx_runtime_1.jsx(RenderModalHr_1.RenderModalHr, {}), jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
72
73
  jsx_runtime_1.jsxs("div", { style: layout_2.label, children: ["Audio Quality",
73
74
  jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 })
74
75
  ] }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(ComboBox_1.Combobox, { values: audioBitrateOptions, selectedId: audioBitrate, title: "Audio Quality" }) })
75
- ] }), jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
76
+ ] }), showAudioCodecSetting ? (jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
76
77
  jsx_runtime_1.jsxs("div", { style: layout_2.label, children: ["Audio Codec",
77
78
  jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 })
78
79
  ] }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(ComboBox_1.Combobox, { values: audioCodecOptions, selectedId: audioCodec, title: "Audio Codec" }) })
79
- ] }), effectiveAudioCodec !== audioCodec ? (jsx_runtime_1.jsxs("div", { style: fallbackNoticeStyle, children: [humanReadableWebAudioCodec(audioCodec), " is not available in this browser. Using ", humanReadableWebAudioCodec(effectiveAudioCodec), ' ', "instead."] })) : null] })) : null] }));
80
+ ] })) : null, showAudioCodecSetting && effectiveAudioCodec !== audioCodec ? (jsx_runtime_1.jsxs("div", { style: fallbackNoticeStyle, children: [humanReadableWebAudioCodec(audioCodec), " is not available in this browser. Using ", humanReadableWebAudioCodec(effectiveAudioCodec), ' ', "instead."] })) : null] })) : null] }));
80
81
  };
81
82
  exports.WebRenderModalAudio = WebRenderModalAudio;
@@ -90,6 +90,9 @@ const ClientRenderQueueProcessor = () => {
90
90
  onProgress(job.id, {
91
91
  encodedFrames: progress.encodedFrames,
92
92
  totalFrames,
93
+ doneIn: progress.doneIn,
94
+ renderEstimatedTime: progress.renderEstimatedTime,
95
+ progress: progress.progress,
93
96
  });
94
97
  },
95
98
  outputTarget: 'web-fs',
@@ -80,8 +80,7 @@ const RenderQueueItemStatus = ({ job }) => {
80
80
  if (job.status === 'running') {
81
81
  let progressValue;
82
82
  if (isClientJob) {
83
- const { encodedFrames, totalFrames } = job.progress;
84
- progressValue = totalFrames > 0 ? encodedFrames / totalFrames : 0;
83
+ progressValue = job.progress.progress;
85
84
  }
86
85
  else {
87
86
  progressValue = job.progress.value;
@@ -5,6 +5,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
6
  const modals_1 = require("../../state/modals");
7
7
  const z_index_1 = require("../../state/z-index");
8
+ const client_render_progress_1 = require("./client-render-progress");
8
9
  const context_1 = require("./context");
9
10
  const item_style_1 = require("./item-style");
10
11
  const outputLocation = {
@@ -24,9 +25,7 @@ const RenderQueueProgressMessage = ({ job }) => {
24
25
  });
25
26
  }, [job.id, setSelectedModal]);
26
27
  const message = isClientJob
27
- ? job.progress.totalFrames === 0
28
- ? 'Getting composition'
29
- : `Encoding frame ${job.progress.encodedFrames}/${job.progress.totalFrames}`
28
+ ? (0, client_render_progress_1.getClientRenderProgressMessage)(job.progress)
30
29
  : job.progress.message;
31
30
  return (jsx_runtime_1.jsx("button", { onClick: onClick, type: "button", style: outputLocation, tabIndex: tabIndex, title: message, children: message }));
32
31
  };
@@ -0,0 +1,3 @@
1
+ import type { ClientRenderJobProgress } from './client-side-render-types';
2
+ export declare const formatEtaString: (timeRemainingInMilliseconds: number) => string;
3
+ export declare const getClientRenderProgressMessage: (progress: ClientRenderJobProgress) => string;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getClientRenderProgressMessage = exports.formatEtaString = void 0;
4
+ const formatEtaString = (timeRemainingInMilliseconds) => {
5
+ const remainingTime = timeRemainingInMilliseconds / 1000;
6
+ const remainingTimeHours = Math.floor(remainingTime / 3600);
7
+ const remainingTimeMinutes = Math.floor((remainingTime % 3600) / 60);
8
+ const remainingTimeSeconds = Math.floor(remainingTime % 60);
9
+ return [
10
+ remainingTimeHours ? `${remainingTimeHours}h` : null,
11
+ remainingTimeMinutes ? `${remainingTimeMinutes}m` : null,
12
+ `${remainingTimeSeconds}s`,
13
+ ]
14
+ .filter((value) => Boolean(value))
15
+ .join(' ');
16
+ };
17
+ exports.formatEtaString = formatEtaString;
18
+ const getClientRenderProgressMessage = (progress) => {
19
+ if (progress.totalFrames === 0) {
20
+ return 'Getting composition';
21
+ }
22
+ if (progress.doneIn !== null) {
23
+ return `Encoded ${progress.totalFrames}/${progress.totalFrames}`;
24
+ }
25
+ if (progress.renderEstimatedTime > 0) {
26
+ const etaString = `, time remaining: ${(0, exports.formatEtaString)(progress.renderEstimatedTime)}`;
27
+ return `Rendering ${progress.encodedFrames}/${progress.totalFrames}${etaString}`;
28
+ }
29
+ return `Encoded ${progress.encodedFrames}/${progress.totalFrames}`;
30
+ };
31
+ exports.getClientRenderProgressMessage = getClientRenderProgressMessage;
@@ -4,6 +4,9 @@ import type { LogLevel } from 'remotion';
4
4
  export type ClientRenderJobProgress = {
5
5
  encodedFrames: number;
6
6
  totalFrames: number;
7
+ doneIn: number | null;
8
+ renderEstimatedTime: number;
9
+ progress: number;
7
10
  };
8
11
  export type GetBlobCallback = () => Promise<Blob>;
9
12
  type ClientRenderJobDynamicStatus = {
@@ -86,7 +86,13 @@ const RenderQueueContextProvider = ({ children }) => {
86
86
  ? {
87
87
  ...job,
88
88
  status: 'running',
89
- progress: { encodedFrames: 0, totalFrames: 0 },
89
+ progress: {
90
+ encodedFrames: 0,
91
+ totalFrames: 0,
92
+ doneIn: null,
93
+ renderEstimatedTime: 0,
94
+ progress: 0,
95
+ },
90
96
  }
91
97
  : job));
92
98
  processJobCallbackRef.current(nextJob);
@@ -80,6 +80,7 @@ const SidebarRenderButton = ({ composition, visible }) => {
80
80
  initialMediaCacheSizeInBytes: defaults.mediaCacheSizeInBytes,
81
81
  renderDefaults: defaults,
82
82
  initialDarkMode: defaults.darkMode,
83
+ readOnlyStudio: false,
83
84
  });
84
85
  if (isMobileLayout) {
85
86
  setSidebarCollapsedState({ left: 'collapsed', right: 'collapsed' });