@remotion/studio 4.0.431 → 4.0.432
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.
- package/dist/Studio.d.ts +1 -0
- package/dist/Studio.js +4 -4
- package/dist/components/AssetSelectorItem.js +6 -8
- package/dist/components/NewComposition/DuplicateComposition.js +1 -4
- package/dist/components/RenderModal/SchemaEditor/SchemaLabel.js +1 -3
- package/dist/components/RenderModal/SchemaEditor/ZodArrayEditor.js +1 -3
- package/dist/components/RenderModal/SchemaEditor/ZodFieldValidation.js +1 -3
- package/dist/components/RenderModal/SchemaEditor/ZodTupleEditor.js +1 -3
- package/dist/components/RenderModal/WebRenderModal.d.ts +1 -1
- package/dist/components/RenderModal/WebRenderModal.js +55 -26
- package/dist/components/RenderModal/WebRenderModalAudio.d.ts +2 -0
- package/dist/components/RenderModal/WebRenderModalAudio.js +11 -5
- package/dist/components/RenderModal/WebRenderModalBasic.js +35 -35
- package/dist/components/RenderQueue/ClientRenderQueueProcessor.js +3 -3
- package/dist/components/RenderQueue/client-side-render-types.d.ts +1 -1
- package/dist/components/Timeline/TimelineExpandedSection.js +11 -147
- package/dist/components/Timeline/TimelineFieldRow.d.ts +8 -0
- package/dist/components/Timeline/TimelineFieldRow.js +78 -0
- package/dist/components/Timeline/TimelineListItem.js +6 -17
- package/dist/components/Timeline/TimelineSchemaField.d.ts +2 -1
- package/dist/components/Timeline/TimelineSchemaField.js +4 -6
- package/dist/components/Timeline/use-resolved-stack.d.ts +2 -0
- package/dist/components/Timeline/use-resolved-stack.js +54 -0
- package/dist/components/Timeline/use-sequence-props-subscription.d.ts +3 -0
- package/dist/components/Timeline/use-sequence-props-subscription.js +130 -0
- package/dist/error-overlay/react-overlay/utils/get-source-map.d.ts +5 -0
- package/dist/esm/{chunk-3msfwcwh.js → chunk-t28xqw5n.js} +1932 -1795
- package/dist/esm/internals.mjs +1932 -1795
- package/dist/esm/previewEntry.mjs +1915 -1777
- package/dist/esm/renderEntry.mjs +5 -2
- package/dist/helpers/get-timeline-sequence-layout.js +3 -3
- package/dist/helpers/timeline-layout.d.ts +2 -2
- package/dist/helpers/timeline-layout.js +2 -2
- package/dist/internals.d.ts +1 -0
- package/dist/previewEntry.js +1 -1
- package/dist/renderEntry.js +3 -3
- package/package.json +10 -10
package/dist/Studio.d.ts
CHANGED
package/dist/Studio.js
CHANGED
|
@@ -16,18 +16,18 @@ const ResolveCompositionConfigInStudio_1 = require("./ResolveCompositionConfigIn
|
|
|
16
16
|
const getServerDisconnectedDomElement = () => {
|
|
17
17
|
return document.getElementById('server-disconnected-overlay');
|
|
18
18
|
};
|
|
19
|
-
const StudioInner = ({ rootComponent, readOnly }) => {
|
|
19
|
+
const StudioInner = ({ rootComponent, readOnly, visualModeEnabled }) => {
|
|
20
20
|
var _a;
|
|
21
21
|
const { fastRefreshes, manualRefreshes } = (0, react_1.useContext)(fast_refresh_context_1.FastRefreshContext);
|
|
22
|
-
return (jsx_runtime_1.jsx(remotion_1.Internals.CompositionManagerProvider, { onlyRenderComposition: null, currentCompositionMetadata: null, initialCompositions: [], initialCanvasContent: null, children: jsx_runtime_1.jsx(remotion_1.Internals.RemotionRootContexts, { frameState: null, audioEnabled: window.remotion_audioEnabled, videoEnabled: window.remotion_videoEnabled, logLevel: window.remotion_logLevel, numberOfAudioTags: window.remotion_numberOfAudioTags, audioLatencyHint: (_a = window.remotion_audioLatencyHint) !== null && _a !== void 0 ? _a : 'interactive', nonceContextSeed: fastRefreshes + manualRefreshes, children: jsx_runtime_1.jsx(use_static_files_1.StaticFilesProvider, { children: jsx_runtime_1.jsx(ResolveCompositionConfigInStudio_1.ResolveCompositionConfigInStudio, { children: jsx_runtime_1.jsxs(EditorContexts_1.EditorContexts, { readOnlyStudio: readOnly, children: [
|
|
22
|
+
return (jsx_runtime_1.jsx(remotion_1.Internals.CompositionManagerProvider, { onlyRenderComposition: null, currentCompositionMetadata: null, initialCompositions: [], initialCanvasContent: null, children: jsx_runtime_1.jsx(remotion_1.Internals.RemotionRootContexts, { visualModeEnabled: visualModeEnabled, frameState: null, audioEnabled: window.remotion_audioEnabled, videoEnabled: window.remotion_videoEnabled, logLevel: window.remotion_logLevel, numberOfAudioTags: window.remotion_numberOfAudioTags, audioLatencyHint: (_a = window.remotion_audioLatencyHint) !== null && _a !== void 0 ? _a : 'interactive', nonceContextSeed: fastRefreshes + manualRefreshes, children: jsx_runtime_1.jsx(use_static_files_1.StaticFilesProvider, { children: jsx_runtime_1.jsx(ResolveCompositionConfigInStudio_1.ResolveCompositionConfigInStudio, { children: jsx_runtime_1.jsxs(EditorContexts_1.EditorContexts, { readOnlyStudio: readOnly, children: [
|
|
23
23
|
jsx_runtime_1.jsx(Editor_1.Editor, { readOnlyStudio: readOnly, Root: rootComponent }), readOnly
|
|
24
24
|
? null
|
|
25
25
|
: (0, react_dom_1.createPortal)(jsx_runtime_1.jsx(ServerDisconnected_1.ServerDisconnected, {}), getServerDisconnectedDomElement())] }) }) }) }) }));
|
|
26
26
|
};
|
|
27
|
-
const Studio = ({ rootComponent, readOnly }) => {
|
|
27
|
+
const Studio = ({ rootComponent, readOnly, visualModeEnabled }) => {
|
|
28
28
|
(0, react_1.useLayoutEffect)(() => {
|
|
29
29
|
(0, inject_css_1.injectCSS)();
|
|
30
30
|
}, []);
|
|
31
|
-
return (jsx_runtime_1.jsx(FastRefreshProvider_1.FastRefreshProvider, { children: jsx_runtime_1.jsx(StudioInner, { rootComponent: rootComponent, readOnly: readOnly }) }));
|
|
31
|
+
return (jsx_runtime_1.jsx(FastRefreshProvider_1.FastRefreshProvider, { children: jsx_runtime_1.jsx(StudioInner, { rootComponent: rootComponent, readOnly: readOnly, visualModeEnabled: visualModeEnabled }) }));
|
|
32
32
|
};
|
|
33
33
|
exports.Studio = Studio;
|
|
@@ -161,20 +161,19 @@ const AssetSelectorItem = ({ item, tabIndex, level, parentFolder, readOnlyStudio
|
|
|
161
161
|
}, []);
|
|
162
162
|
const { setCanvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionSetters);
|
|
163
163
|
const { canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
|
|
164
|
+
const relativePath = (0, react_1.useMemo)(() => {
|
|
165
|
+
return parentFolder ? parentFolder + '/' + item.name : item.name;
|
|
166
|
+
}, [parentFolder, item.name]);
|
|
164
167
|
const selected = (0, react_1.useMemo)(() => {
|
|
165
168
|
if (canvasContent && canvasContent.type === 'asset') {
|
|
166
|
-
|
|
167
|
-
return nameWOParent === item.name;
|
|
169
|
+
return canvasContent.asset === relativePath;
|
|
168
170
|
}
|
|
169
171
|
return false;
|
|
170
|
-
}, [canvasContent,
|
|
172
|
+
}, [canvasContent, relativePath]);
|
|
171
173
|
const onPointerLeave = (0, react_1.useCallback)(() => {
|
|
172
174
|
setHovered(false);
|
|
173
175
|
}, []);
|
|
174
176
|
const onClick = (0, react_1.useCallback)(() => {
|
|
175
|
-
const relativePath = parentFolder
|
|
176
|
-
? parentFolder + '/' + item.name
|
|
177
|
-
: item.name;
|
|
178
177
|
setCanvasContent({ type: 'asset', asset: relativePath });
|
|
179
178
|
(0, url_state_1.pushUrl)(`/assets/${relativePath}`);
|
|
180
179
|
if (isMobileLayout) {
|
|
@@ -182,8 +181,7 @@ const AssetSelectorItem = ({ item, tabIndex, level, parentFolder, readOnlyStudio
|
|
|
182
181
|
}
|
|
183
182
|
}, [
|
|
184
183
|
isMobileLayout,
|
|
185
|
-
|
|
186
|
-
parentFolder,
|
|
184
|
+
relativePath,
|
|
187
185
|
setCanvasContent,
|
|
188
186
|
setSidebarCollapsedState,
|
|
189
187
|
]);
|
|
@@ -175,10 +175,7 @@ const DuplicateCompositionLoaded = ({ initialType }) => {
|
|
|
175
175
|
}, []);
|
|
176
176
|
return (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
|
|
177
177
|
jsx_runtime_1.jsx(ModalHeader_1.ModalHeader, { title: `Duplicate ${resolved.result.id}` }), jsx_runtime_1.jsxs("form", { onSubmit: onSubmit, children: [
|
|
178
|
-
jsx_runtime_1.jsxs("div", { style: content, children: [initialCompType === 'composition' ? (
|
|
179
|
-
// We allow converting from a composition to a still, but
|
|
180
|
-
// not the other way around
|
|
181
|
-
jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
|
|
178
|
+
jsx_runtime_1.jsxs("div", { style: content, children: [initialCompType === 'composition' ? (jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
|
|
182
179
|
jsx_runtime_1.jsx("div", { style: layout_2.label, children: "Type" }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(ComboBox_1.Combobox, { title: "Type of composition", style: comboBoxStyle, values: typeValues, selectedId: type }) })
|
|
183
180
|
] })) : null, jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
|
|
184
181
|
jsx_runtime_1.jsx("div", { style: layout_2.label, children: "ID" }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsxs("div", { children: [
|
|
@@ -41,8 +41,6 @@ const SchemaLabel = ({ jsonPath, isDefaultValue, onReset, onSave, showSaveButton
|
|
|
41
41
|
setClickableButtonHovered(false);
|
|
42
42
|
}, []);
|
|
43
43
|
const labelContent = (jsx_runtime_1.jsxs("span", { style: labelStyle, children: [(0, get_schema_label_1.getSchemaLabel)(jsonPath), " ", suffix ? suffix : null] }));
|
|
44
|
-
return (jsx_runtime_1.jsxs("div", { style: compactStyles, className: scroll_to_default_props_path_1.DEFAULT_PROPS_PATH_CLASSNAME, "data-json-path": jsonPath.join('.'), children: [handleClick ? (
|
|
45
|
-
// Minus the padding that a button has (user agent padding-line-start)
|
|
46
|
-
jsx_runtime_1.jsx("button", { onPointerEnter: onClickablePointerEnter, onPointerLeave: onClickablePointerLeave, type: "button", onClick: handleClick, style: { border: 'none', padding: 0 }, children: labelContent })) : (labelContent), jsx_runtime_1.jsx(layout_1.Flex, {}), isDefaultValue ? null : jsx_runtime_1.jsx(SchemaResetButton_1.SchemaResetButton, { onClick: onReset }), isDefaultValue ? null : showSaveButton ? (jsx_runtime_1.jsx(SchemaSaveButton_1.SchemaSaveButton, { onClick: onSave, disabled: disableSave })) : null, onRemove ? jsx_runtime_1.jsx(InlineRemoveButton_1.InlineRemoveButton, { onClick: onRemove }) : null] }));
|
|
44
|
+
return (jsx_runtime_1.jsxs("div", { style: compactStyles, className: scroll_to_default_props_path_1.DEFAULT_PROPS_PATH_CLASSNAME, "data-json-path": jsonPath.join('.'), children: [handleClick ? (jsx_runtime_1.jsx("button", { onPointerEnter: onClickablePointerEnter, onPointerLeave: onClickablePointerLeave, type: "button", onClick: handleClick, style: { border: 'none', padding: 0 }, children: labelContent })) : (labelContent), jsx_runtime_1.jsx(layout_1.Flex, {}), isDefaultValue ? null : jsx_runtime_1.jsx(SchemaResetButton_1.SchemaResetButton, { onClick: onReset }), isDefaultValue ? null : showSaveButton ? (jsx_runtime_1.jsx(SchemaSaveButton_1.SchemaSaveButton, { onClick: onSave, disabled: disableSave })) : null, onRemove ? jsx_runtime_1.jsx(InlineRemoveButton_1.InlineRemoveButton, { onClick: onRemove }) : null] }));
|
|
47
45
|
};
|
|
48
46
|
exports.SchemaLabel = SchemaLabel;
|
|
@@ -75,9 +75,7 @@ const ZodArrayEditor = ({ schema, jsonPath, setValue, defaultValue, value, onSav
|
|
|
75
75
|
onSave(() => localValue.value, false, false);
|
|
76
76
|
}, saveDisabledByParent: saveDisabledByParent, saving: saving, showSaveButton: showSaveButton, valid: localValue.zodValidation.success, handleClick: () => setExpanded(!expanded) }) }), expanded ? (jsx_runtime_1.jsx(RevisionContextProvider, { children: jsx_runtime_1.jsxs(SchemaVerticalGuide_1.SchemaVerticalGuide, { isRoot: false, children: [localValue.value.map((child, i) => {
|
|
77
77
|
var _a;
|
|
78
|
-
return (
|
|
79
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
80
|
-
jsx_runtime_1.jsxs(react_1.default.Fragment, { children: [
|
|
78
|
+
return (jsx_runtime_1.jsxs(react_1.default.Fragment, { children: [
|
|
81
79
|
jsx_runtime_1.jsx(ZodArrayItemEditor_1.ZodArrayItemEditor, { onChange: onChange, value: child, elementSchema: arrayElement, index: i, jsonPath: jsonPath, defaultValue: (_a = defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue[i]) !== null && _a !== void 0 ? _a : (0, create_zod_values_1.createZodValues)(arrayElement, z, zodTypes), onSave: onSave, showSaveButton: showSaveButton, saving: saving, saveDisabledByParent: saveDisabledByParent, mayPad: mayPad, mayRemove: true }), jsx_runtime_1.jsx(SchemaSeparationLine_1.SchemaArrayItemSeparationLine, { schema: schema, index: i, onChange: onChange, isLast: i === localValue.value.length - 1, showAddButton: true })
|
|
82
80
|
] }, `${i}${localValue.keyStabilityRevision}`));
|
|
83
81
|
}), value.length === 0 ? (jsx_runtime_1.jsx(SchemaSeparationLine_1.SchemaArrayItemSeparationLine, { schema: schema, index: 0, onChange: onChange, isLast: true, showAddButton: true })) : null] }) })) : null, jsx_runtime_1.jsx(ZodFieldValidation_1.ZodFieldValidation, { path: jsonPath, localValue: localValue })
|
|
@@ -23,9 +23,7 @@ const ZodFieldValidation = ({ localValue, path }) => {
|
|
|
23
23
|
}
|
|
24
24
|
return (jsx_runtime_1.jsxs("div", { style: legend, children: [
|
|
25
25
|
jsx_runtime_1.jsx(ValidationMessage_1.ValidationMessage, { align: "flex-start", message: zodValidation.error.format()._errors[0], type: "error" }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(InfoBubble_1.InfoBubble, { title: "Zod validation failure", children: jsx_runtime_1.jsxs("div", { style: stackTrace, children: [
|
|
26
|
-
jsx_runtime_1.jsx("div", { style: stackTraceLabel, children: "Zod Validation has failed:" }), zodValidation.error.issues.map((error, index) => (
|
|
27
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
28
|
-
jsx_runtime_1.jsxs("div", { style: stackTraceLabel, children: ["Type: ", error.code, " ",
|
|
26
|
+
jsx_runtime_1.jsx("div", { style: stackTraceLabel, children: "Zod Validation has failed:" }), zodValidation.error.issues.map((error, index) => (jsx_runtime_1.jsxs("div", { style: stackTraceLabel, children: ["Type: ", error.code, " ",
|
|
29
27
|
jsx_runtime_1.jsx("br", {}),
|
|
30
28
|
"Message: ", error.message, jsx_runtime_1.jsx("br", {}),
|
|
31
29
|
"Path: ", path.join('.')] }, index)))] }) }), jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 })
|
|
@@ -75,9 +75,7 @@ const ZodTupleEditor = ({ schema, jsonPath, setValue, defaultValue, value, onSav
|
|
|
75
75
|
onSave(() => localValue.value, false, false);
|
|
76
76
|
}, saveDisabledByParent: saveDisabledByParent, saving: saving, showSaveButton: showSaveButton, valid: localValue.zodValidation.success, handleClick: () => setExpanded(!expanded) }) }), expanded ? (jsx_runtime_1.jsx(RevisionContextProvider, { children: jsx_runtime_1.jsxs(SchemaVerticalGuide_1.SchemaVerticalGuide, { isRoot: false, children: [localValue.value.map((child, i) => {
|
|
77
77
|
var _a;
|
|
78
|
-
return (
|
|
79
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
80
|
-
jsx_runtime_1.jsxs(react_1.default.Fragment, { children: [
|
|
78
|
+
return (jsx_runtime_1.jsxs(react_1.default.Fragment, { children: [
|
|
81
79
|
jsx_runtime_1.jsx(ZodTupleItemEditor_1.ZodTupleItemEditor, { onChange: onChange, value: child, tupleItems: tupleItems, index: i, jsonPath: jsonPath, defaultValue: (_a = defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue[i]) !== null && _a !== void 0 ? _a : (0, create_zod_values_1.createZodValues)(tupleItems[i], z, zodTypes), onSave: onSave, showSaveButton: showSaveButton, saving: saving, saveDisabledByParent: saveDisabledByParent, mayPad: mayPad }), jsx_runtime_1.jsx(SchemaSeparationLine_1.SchemaArrayItemSeparationLine, { schema: schema, index: i, onChange: onChange, isLast: i === localValue.value.length - 1, showAddButton: false })
|
|
82
80
|
] }, `${i}${localValue.keyStabilityRevision}`));
|
|
83
81
|
}), value.length === 0 ? (jsx_runtime_1.jsx(SchemaSeparationLine_1.SchemaArrayItemSeparationLine, { schema: schema, index: 0, onChange: onChange, isLast: true, showAddButton: false })) : null] }) })) : null, jsx_runtime_1.jsx(ZodFieldValidation_1.ZodFieldValidation, { path: jsonPath, localValue: localValue })
|
|
@@ -77,7 +77,6 @@ const validateOutnameForStill = ({ outName, stillImageFormat, }) => {
|
|
|
77
77
|
};
|
|
78
78
|
// TODO: Switch to server-side rendering
|
|
79
79
|
// TODO: Filter out codecs that are not supported for the container
|
|
80
|
-
// TODO: Add more containers
|
|
81
80
|
// TODO: Shortcut: Shift + R
|
|
82
81
|
// TODO: Apply defaultCodec
|
|
83
82
|
// TODO: Apply defaultOutName
|
|
@@ -96,7 +95,11 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
|
|
|
96
95
|
? true
|
|
97
96
|
: resolvedComposition.durationInFrames > 1;
|
|
98
97
|
});
|
|
99
|
-
const [renderMode, setRenderMode] = (0, react_1.useState)(
|
|
98
|
+
const [renderMode, setRenderMode] = (0, react_1.useState)(initialContainer && (0, web_renderer_1.isAudioOnlyContainer)(initialContainer)
|
|
99
|
+
? 'audio'
|
|
100
|
+
: isVideo
|
|
101
|
+
? 'video'
|
|
102
|
+
: 'still');
|
|
100
103
|
const [tab, setTab] = (0, react_1.useState)('general');
|
|
101
104
|
const [imageFormat, setImageFormat] = (0, react_1.useState)(() => initialStillImageFormat !== null && initialStillImageFormat !== void 0 ? initialStillImageFormat : 'png');
|
|
102
105
|
const [frame, setFrame] = (0, react_1.useState)(() => initialFrame);
|
|
@@ -170,36 +173,52 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
|
|
|
170
173
|
return defaultOut;
|
|
171
174
|
});
|
|
172
175
|
const [outName, setOutName] = (0, react_1.useState)(() => initialOutNameState);
|
|
176
|
+
const updateOutNameExtension = (0, react_1.useCallback)((extension) => {
|
|
177
|
+
setOutName((prev) => (0, get_string_before_suffix_1.getStringBeforeSuffix)(prev) + '.' + extension);
|
|
178
|
+
}, []);
|
|
173
179
|
const setStillFormat = (0, react_1.useCallback)((format) => {
|
|
174
180
|
setImageFormat(format);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return newFileName;
|
|
178
|
-
});
|
|
179
|
-
}, []);
|
|
181
|
+
updateOutNameExtension(format);
|
|
182
|
+
}, [updateOutNameExtension]);
|
|
180
183
|
const setContainerFormat = (0, react_1.useCallback)((newContainer) => {
|
|
184
|
+
setContainer(newContainer);
|
|
185
|
+
const defaultVideoCodec = (0, web_renderer_1.getDefaultVideoCodecForContainer)(newContainer);
|
|
186
|
+
if (defaultVideoCodec) {
|
|
187
|
+
setCodec(defaultVideoCodec);
|
|
188
|
+
}
|
|
189
|
+
setAudioCodec((0, web_renderer_1.getDefaultAudioCodecForContainer)(newContainer));
|
|
190
|
+
updateOutNameExtension(newContainer);
|
|
191
|
+
}, [updateOutNameExtension]);
|
|
192
|
+
const setCodecWithContainer = (0, react_1.useCallback)((newCodec) => {
|
|
193
|
+
setCodec(newCodec);
|
|
194
|
+
const newContainer = (0, web_renderer_1.getDefaultContainerForCodec)(newCodec);
|
|
181
195
|
setContainer(newContainer);
|
|
182
196
|
setAudioCodec((0, web_renderer_1.getDefaultAudioCodecForContainer)(newContainer));
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return newFileName;
|
|
186
|
-
});
|
|
187
|
-
}, []);
|
|
197
|
+
updateOutNameExtension(newContainer);
|
|
198
|
+
}, [updateOutNameExtension]);
|
|
188
199
|
const onRenderModeChange = (0, react_1.useCallback)((newMode) => {
|
|
189
200
|
setRenderMode(newMode);
|
|
190
201
|
if (newMode === 'video') {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
202
|
+
const newContainer = (0, web_renderer_1.isAudioOnlyContainer)(container)
|
|
203
|
+
? 'mp4'
|
|
204
|
+
: container;
|
|
205
|
+
setContainer(newContainer);
|
|
206
|
+
setAudioCodec((0, web_renderer_1.getDefaultAudioCodecForContainer)(newContainer));
|
|
207
|
+
updateOutNameExtension(newContainer);
|
|
208
|
+
}
|
|
209
|
+
else if (newMode === 'audio') {
|
|
210
|
+
const newContainer = 'wav';
|
|
211
|
+
setContainer(newContainer);
|
|
212
|
+
setMuted(false);
|
|
213
|
+
setAudioCodec((0, web_renderer_1.getDefaultAudioCodecForContainer)(newContainer));
|
|
214
|
+
updateOutNameExtension(newContainer);
|
|
215
|
+
setTab((prev) => (prev === 'picture' ? 'general' : prev));
|
|
195
216
|
}
|
|
196
217
|
else if (newMode === 'still') {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
return newFileName;
|
|
200
|
-
});
|
|
218
|
+
updateOutNameExtension(imageFormat);
|
|
219
|
+
setTab((prev) => (prev === 'audio' ? 'general' : prev));
|
|
201
220
|
}
|
|
202
|
-
}, [container, imageFormat]);
|
|
221
|
+
}, [container, imageFormat, updateOutNameExtension]);
|
|
203
222
|
const renderTabOptions = (0, react_1.useMemo)(() => {
|
|
204
223
|
const options = [
|
|
205
224
|
{
|
|
@@ -211,7 +230,7 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
|
|
|
211
230
|
selected: renderMode === 'still',
|
|
212
231
|
},
|
|
213
232
|
];
|
|
214
|
-
// Only show video
|
|
233
|
+
// Only show video/audio options if composition has more than 1 frame
|
|
215
234
|
if (resolvedComposition.durationInFrames > 1) {
|
|
216
235
|
options.push({
|
|
217
236
|
label: 'Video',
|
|
@@ -221,6 +240,14 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
|
|
|
221
240
|
key: 'video',
|
|
222
241
|
selected: renderMode === 'video',
|
|
223
242
|
});
|
|
243
|
+
options.push({
|
|
244
|
+
label: 'Audio',
|
|
245
|
+
onClick: () => {
|
|
246
|
+
onRenderModeChange('audio');
|
|
247
|
+
},
|
|
248
|
+
key: 'audio',
|
|
249
|
+
selected: renderMode === 'audio',
|
|
250
|
+
});
|
|
224
251
|
}
|
|
225
252
|
return options;
|
|
226
253
|
}, [renderMode, resolvedComposition.durationInFrames, onRenderModeChange]);
|
|
@@ -315,7 +342,9 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
|
|
|
315
342
|
compositionId: resolvedComposition.id,
|
|
316
343
|
outName,
|
|
317
344
|
container,
|
|
318
|
-
videoCodec:
|
|
345
|
+
videoCodec: (0, web_renderer_1.isAudioOnlyContainer)(container)
|
|
346
|
+
? null
|
|
347
|
+
: effectiveVideoCodec,
|
|
319
348
|
audioCodec: effectiveAudioCodec,
|
|
320
349
|
startFrame: finalStartFrame,
|
|
321
350
|
endFrame: finalEndFrame,
|
|
@@ -382,16 +411,16 @@ const WebRenderModal = ({ initialFrame, defaultProps, inFrameMark, outFrameMark,
|
|
|
382
411
|
jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(file_1.FileIcon, { style: render_modals_1.icon }) }),
|
|
383
412
|
"General"] }), jsx_runtime_1.jsxs(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'data', onClick: () => setTab('data'), children: [
|
|
384
413
|
jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(data_1.DataIcon, { style: render_modals_1.icon }) }),
|
|
385
|
-
"Input Props"] }), jsx_runtime_1.jsxs(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'picture', onClick: () => setTab('picture'), children: [
|
|
414
|
+
"Input Props"] }), renderMode !== 'audio' ? (jsx_runtime_1.jsxs(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'picture', onClick: () => setTab('picture'), children: [
|
|
386
415
|
jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(frame_1.PicIcon, { style: render_modals_1.icon }) }),
|
|
387
|
-
"Picture"] }), renderMode === 'video' ? (jsx_runtime_1.jsxs(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'audio', onClick: () => setTab('audio'), children: [
|
|
416
|
+
"Picture"] })) : null, renderMode === 'video' || renderMode === 'audio' ? (jsx_runtime_1.jsxs(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'audio', onClick: () => setTab('audio'), children: [
|
|
388
417
|
jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(audio_1.AudioIcon, { style: render_modals_1.icon }) }),
|
|
389
418
|
"Audio"] })) : null, jsx_runtime_1.jsxs(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'advanced', onClick: () => setTab('advanced'), children: [
|
|
390
419
|
jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(gear_1.GearIcon, { style: render_modals_1.icon }) }),
|
|
391
420
|
"Other"] }), jsx_runtime_1.jsxs(vertical_1.VerticalTab, { style: render_modals_1.horizontalTab, selected: tab === 'license', onClick: () => setTab('license'), children: [
|
|
392
421
|
jsx_runtime_1.jsx("div", { style: render_modals_1.iconContainer, children: jsx_runtime_1.jsx(certificate_1.CertificateIcon, { style: render_modals_1.icon }) }),
|
|
393
422
|
"License"] })
|
|
394
|
-
] }), 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(WebRenderModalBasic_1.WebRenderModalBasic, { renderMode: renderMode, resolvedComposition: resolvedComposition, imageFormat: imageFormat, setStillFormat: setStillFormat, frame: frame, onFrameChanged: onFrameChanged, onFrameSetDirectly: onFrameSetDirectly, container: container, setContainerFormat: setContainerFormat, setCodec:
|
|
423
|
+
] }), 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(WebRenderModalBasic_1.WebRenderModalBasic, { renderMode: renderMode, resolvedComposition: resolvedComposition, imageFormat: imageFormat, setStillFormat: setStillFormat, frame: frame, onFrameChanged: onFrameChanged, onFrameSetDirectly: onFrameSetDirectly, container: container, setContainerFormat: setContainerFormat, setCodec: setCodecWithContainer, encodableVideoCodecs: encodableVideoCodecs, effectiveVideoCodec: effectiveVideoCodec, startFrame: finalStartFrame, setStartFrame: setStartFrame, endFrame: finalEndFrame, setEndFrame: setEndFrame, outName: outName, onOutNameChange: onOutNameChange, validationMessage: outnameValidation.valid ? null : outnameValidation.error.message, logLevel: logLevel, setLogLevel: setLogLevel })) : 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 })) : tab === 'picture' ? (jsx_runtime_1.jsx(WebRenderModalPicture_1.WebRenderModalPicture, { renderMode: renderMode, videoBitrate: videoBitrate, setVideoBitrate: setVideoBitrate, keyframeIntervalInSeconds: keyframeIntervalInSeconds, setKeyframeIntervalInSeconds: setKeyframeIntervalInSeconds, transparent: transparent, setTransparent: setTransparent, scale: scale, setScale: setScale, compositionWidth: resolvedComposition.width, compositionHeight: resolvedComposition.height })) : tab === 'audio' ? (jsx_runtime_1.jsx(WebRenderModalAudio_1.WebRenderModalAudio, { renderMode: renderMode, muted: muted, setMuted: setMuted, audioCodec: audioCodec, setAudioCodec: setAudioCodec, audioBitrate: audioBitrate, setAudioBitrate: setAudioBitrate, container: container, encodableCodecs: encodableAudioCodecs, effectiveAudioCodec: effectiveAudioCodec })) : tab === 'advanced' ? (jsx_runtime_1.jsx(WebRenderModalAdvanced_1.WebRenderModalAdvanced, { renderMode: renderMode, delayRenderTimeout: delayRenderTimeout, setDelayRenderTimeout: setDelayRenderTimeout, mediaCacheSizeInBytes: mediaCacheSizeInBytes, setMediaCacheSizeInBytes: setMediaCacheSizeInBytes, hardwareAcceleration: hardwareAcceleration, setHardwareAcceleration: setHardwareAcceleration })) : (jsx_runtime_1.jsx(WebRenderModalLicense_1.WebRenderModalLicense, { licenseKey: licenseKey, setLicenseKey: setLicenseKey, initialPublicLicenseKey: initialLicenseKey })) })
|
|
395
424
|
] })
|
|
396
425
|
] }));
|
|
397
426
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { WebRendererAudioCodec, WebRendererContainer, WebRendererQuality } from '@remotion/web-renderer';
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import type { RenderType } from './WebRenderModal';
|
|
3
4
|
export declare const WebRenderModalAudio: React.FC<{
|
|
5
|
+
readonly renderMode: RenderType;
|
|
4
6
|
readonly muted: boolean;
|
|
5
7
|
readonly setMuted: React.Dispatch<React.SetStateAction<boolean>>;
|
|
6
8
|
readonly audioCodec: WebRendererAudioCodec;
|
|
@@ -34,11 +34,17 @@ const humanReadableWebAudioCodec = (audioCodec) => {
|
|
|
34
34
|
return 'AAC';
|
|
35
35
|
case 'opus':
|
|
36
36
|
return 'Opus';
|
|
37
|
+
case 'mp3':
|
|
38
|
+
return 'MP3';
|
|
39
|
+
case 'vorbis':
|
|
40
|
+
return 'Vorbis';
|
|
41
|
+
case 'pcm-s16':
|
|
42
|
+
return 'Lossless (PCM)';
|
|
37
43
|
default:
|
|
38
|
-
|
|
44
|
+
throw new Error(`Unsupported audio codec: ${audioCodec}`);
|
|
39
45
|
}
|
|
40
46
|
};
|
|
41
|
-
const WebRenderModalAudio = ({ muted, setMuted, audioCodec, setAudioCodec, audioBitrate, setAudioBitrate, container: videoContainer, encodableCodecs, effectiveAudioCodec, }) => {
|
|
47
|
+
const WebRenderModalAudio = ({ renderMode, muted, setMuted, audioCodec, setAudioCodec, audioBitrate, setAudioBitrate, container: videoContainer, encodableCodecs, effectiveAudioCodec, }) => {
|
|
42
48
|
const containerSupported = (0, react_1.useMemo)(() => (0, web_renderer_1.getSupportedAudioCodecsForContainer)(videoContainer), [videoContainer]);
|
|
43
49
|
const audioCodecOptions = (0, react_1.useMemo)(() => {
|
|
44
50
|
return containerSupported.map((codec) => {
|
|
@@ -58,9 +64,9 @@ const WebRenderModalAudio = ({ muted, setMuted, audioCodec, setAudioCodec, audio
|
|
|
58
64
|
});
|
|
59
65
|
}, [containerSupported, encodableCodecs, audioCodec, setAudioCodec]);
|
|
60
66
|
const audioBitrateOptions = (0, react_1.useMemo)(() => (0, quality_options_1.getQualityOptions)(audioBitrate, setAudioBitrate), [audioBitrate, setAudioBitrate]);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
const isAudioOnly = renderMode === 'audio';
|
|
68
|
+
const showAudioSettings = isAudioOnly || !muted;
|
|
69
|
+
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: [
|
|
64
70
|
jsx_runtime_1.jsxs("div", { style: layout_2.label, children: ["Audio Quality",
|
|
65
71
|
jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 })
|
|
66
72
|
] }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(ComboBox_1.Combobox, { values: audioBitrateOptions, selectedId: audioBitrate, title: "Audio Quality" }) })
|
|
@@ -17,6 +17,25 @@ const RenderModalOutputName_1 = require("./RenderModalOutputName");
|
|
|
17
17
|
const tabContainer = {
|
|
18
18
|
flex: 1,
|
|
19
19
|
};
|
|
20
|
+
const containerLabels = {
|
|
21
|
+
mp4: 'MP4',
|
|
22
|
+
webm: 'WebM',
|
|
23
|
+
mkv: 'MKV',
|
|
24
|
+
mov: 'MOV',
|
|
25
|
+
wav: 'WAV',
|
|
26
|
+
mp3: 'MP3',
|
|
27
|
+
aac: 'AAC',
|
|
28
|
+
ogg: 'OGG',
|
|
29
|
+
};
|
|
30
|
+
const videoContainers = ['mp4', 'webm', 'mkv', 'mov'];
|
|
31
|
+
const audioContainers = ['wav', 'mp3', 'aac', 'ogg'];
|
|
32
|
+
const codecLabels = {
|
|
33
|
+
h264: 'H.264',
|
|
34
|
+
h265: 'H.265',
|
|
35
|
+
vp8: 'VP8',
|
|
36
|
+
vp9: 'VP9',
|
|
37
|
+
av1: 'AV1',
|
|
38
|
+
};
|
|
20
39
|
const WebRenderModalBasic = ({ renderMode, resolvedComposition, imageFormat, setStillFormat, frame, onFrameChanged, onFrameSetDirectly, container, setContainerFormat, setCodec, encodableVideoCodecs, effectiveVideoCodec, startFrame, setStartFrame, endFrame, setEndFrame, outName, onOutNameChange, validationMessage, logLevel, setLogLevel, }) => {
|
|
21
40
|
const imageFormatOptions = (0, react_1.useMemo)(() => {
|
|
22
41
|
return [
|
|
@@ -56,38 +75,19 @@ const WebRenderModalBasic = ({ renderMode, resolvedComposition, imageFormat, set
|
|
|
56
75
|
});
|
|
57
76
|
}, [logLevel, setLogLevel]);
|
|
58
77
|
const containerOptions = (0, react_1.useMemo)(() => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
label: 'WebM',
|
|
73
|
-
onClick: () => setContainerFormat('webm'),
|
|
74
|
-
leftItem: container === 'webm' ? jsx_runtime_1.jsx(Checkmark_1.Checkmark, {}) : null,
|
|
75
|
-
id: 'webm',
|
|
76
|
-
keyHint: null,
|
|
77
|
-
quickSwitcherLabel: null,
|
|
78
|
-
subMenu: null,
|
|
79
|
-
type: 'item',
|
|
80
|
-
value: 'webm',
|
|
81
|
-
},
|
|
82
|
-
];
|
|
83
|
-
}, [container, setContainerFormat]);
|
|
84
|
-
const codecLabels = (0, react_1.useMemo)(() => ({
|
|
85
|
-
h264: 'H.264',
|
|
86
|
-
h265: 'H.265',
|
|
87
|
-
vp8: 'VP8',
|
|
88
|
-
vp9: 'VP9',
|
|
89
|
-
av1: 'AV1',
|
|
90
|
-
}), []);
|
|
78
|
+
const containers = renderMode === 'audio' ? audioContainers : videoContainers;
|
|
79
|
+
return containers.map((c) => ({
|
|
80
|
+
label: containerLabels[c],
|
|
81
|
+
onClick: () => setContainerFormat(c),
|
|
82
|
+
leftItem: container === c ? jsx_runtime_1.jsx(Checkmark_1.Checkmark, {}) : null,
|
|
83
|
+
id: c,
|
|
84
|
+
keyHint: null,
|
|
85
|
+
quickSwitcherLabel: null,
|
|
86
|
+
subMenu: null,
|
|
87
|
+
type: 'item',
|
|
88
|
+
value: c,
|
|
89
|
+
}));
|
|
90
|
+
}, [container, setContainerFormat, renderMode]);
|
|
91
91
|
const codecOptions = (0, react_1.useMemo)(() => {
|
|
92
92
|
return encodableVideoCodecs.map((c) => ({
|
|
93
93
|
label: codecLabels[c],
|
|
@@ -100,7 +100,7 @@ const WebRenderModalBasic = ({ renderMode, resolvedComposition, imageFormat, set
|
|
|
100
100
|
type: 'item',
|
|
101
101
|
value: c,
|
|
102
102
|
}));
|
|
103
|
-
}, [encodableVideoCodecs, effectiveVideoCodec, setCodec
|
|
103
|
+
}, [encodableVideoCodecs, effectiveVideoCodec, setCodec]);
|
|
104
104
|
return (jsx_runtime_1.jsxs("div", { style: tabContainer, children: [renderMode === 'still' ? (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
|
|
105
105
|
jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
|
|
106
106
|
jsx_runtime_1.jsx("div", { style: layout_2.label, children: "Format" }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(SegmentedControl_1.SegmentedControl, { items: imageFormatOptions, needsWrapping: true }) })
|
|
@@ -109,11 +109,11 @@ const WebRenderModalBasic = ({ renderMode, resolvedComposition, imageFormat, set
|
|
|
109
109
|
] })) : null] })) : (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: [
|
|
110
110
|
jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
|
|
111
111
|
jsx_runtime_1.jsx("div", { style: layout_2.label, children: "Container" }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(ComboBox_1.Combobox, { values: containerOptions, selectedId: container, title: "Container" }) })
|
|
112
|
-
] }), jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
|
|
112
|
+
] }), renderMode === 'video' ? (jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
|
|
113
113
|
jsx_runtime_1.jsxs("div", { style: layout_2.label, children: ["Codec",
|
|
114
114
|
jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(OptionExplainerBubble_1.OptionExplainerBubble, { id: "videoCodecOption" })
|
|
115
115
|
] }), jsx_runtime_1.jsx("div", { style: layout_2.rightRow, children: jsx_runtime_1.jsx(ComboBox_1.Combobox, { values: codecOptions, selectedId: effectiveVideoCodec, title: "Codec" }) })
|
|
116
|
-
] }), jsx_runtime_1.jsx(FrameRangeSetting_1.FrameRangeSetting, { durationInFrames: resolvedComposition.durationInFrames, startFrame: startFrame !== null && startFrame !== void 0 ? startFrame : 0, endFrame: endFrame !== null && endFrame !== void 0 ? endFrame : resolvedComposition.durationInFrames - 1, setStartFrame: setStartFrame, setEndFrame: setEndFrame })
|
|
116
|
+
] })) : null, jsx_runtime_1.jsx(FrameRangeSetting_1.FrameRangeSetting, { durationInFrames: resolvedComposition.durationInFrames, startFrame: startFrame !== null && startFrame !== void 0 ? startFrame : 0, endFrame: endFrame !== null && endFrame !== void 0 ? endFrame : resolvedComposition.durationInFrames - 1, setStartFrame: setStartFrame, setEndFrame: setEndFrame })
|
|
117
117
|
] })), jsx_runtime_1.jsx(RenderModalOutputName_1.RenderModalOutputName, { existence: false, inputStyle: layout_2.input, outName: outName, onValueChange: onOutNameChange, validationMessage: validationMessage, label: window.remotion_isReadOnlyStudio ? 'Download name' : 'Output name' }), jsx_runtime_1.jsxs("div", { style: layout_2.optionRow, children: [
|
|
118
118
|
jsx_runtime_1.jsxs("div", { style: layout_2.label, children: ["Log Level ",
|
|
119
119
|
jsx_runtime_1.jsx(layout_1.Spacing, { x: 0.5 }), jsx_runtime_1.jsx(OptionExplainerBubble_1.OptionExplainerBubble, { id: "logLevelOption" })
|
|
@@ -53,7 +53,7 @@ const ClientRenderQueueProcessor = () => {
|
|
|
53
53
|
};
|
|
54
54
|
}, [getCompositionForJob]);
|
|
55
55
|
const processVideoJob = (0, react_1.useCallback)(async (job, signal, onProgress) => {
|
|
56
|
-
var _a, _b;
|
|
56
|
+
var _a, _b, _c;
|
|
57
57
|
const compositionRef = getCompositionForJob(job.id);
|
|
58
58
|
if (!compositionRef) {
|
|
59
59
|
throw new Error(`Composition not found for job ${job.id}`);
|
|
@@ -74,7 +74,7 @@ const ClientRenderQueueProcessor = () => {
|
|
|
74
74
|
delayRenderTimeoutInMilliseconds: job.delayRenderTimeout,
|
|
75
75
|
mediaCacheSizeInBytes: job.mediaCacheSizeInBytes,
|
|
76
76
|
logLevel: job.logLevel,
|
|
77
|
-
videoCodec: job.videoCodec,
|
|
77
|
+
videoCodec: (_b = job.videoCodec) !== null && _b !== void 0 ? _b : undefined,
|
|
78
78
|
audioCodec: job.audioCodec,
|
|
79
79
|
audioBitrate: job.audioBitrate,
|
|
80
80
|
container: job.container,
|
|
@@ -93,7 +93,7 @@ const ClientRenderQueueProcessor = () => {
|
|
|
93
93
|
});
|
|
94
94
|
},
|
|
95
95
|
outputTarget: 'web-fs',
|
|
96
|
-
licenseKey: (
|
|
96
|
+
licenseKey: (_c = job.licenseKey) !== null && _c !== void 0 ? _c : undefined,
|
|
97
97
|
});
|
|
98
98
|
return {
|
|
99
99
|
getBlob,
|
|
@@ -46,7 +46,7 @@ export type ClientStillRenderJob = ClientRenderJobBase & {
|
|
|
46
46
|
export type ClientVideoRenderJob = ClientRenderJobBase & {
|
|
47
47
|
type: 'client-video';
|
|
48
48
|
container: WebRendererContainer;
|
|
49
|
-
videoCodec: WebRendererVideoCodec;
|
|
49
|
+
videoCodec: WebRendererVideoCodec | null;
|
|
50
50
|
audioCodec: WebRendererAudioCodec;
|
|
51
51
|
startFrame: number;
|
|
52
52
|
endFrame: number;
|