@remotion/studio 4.0.407 → 4.0.409

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 (51) hide show
  1. package/dist/components/Canvas.js +3 -1
  2. package/dist/components/CanvasOrLoading.js +3 -1
  3. package/dist/components/CurrentCompositionSideEffects.js +4 -0
  4. package/dist/components/EditorContexts.js +2 -1
  5. package/dist/components/GlobalKeybindings.js +15 -13
  6. package/dist/components/KeyboardShortcutsExplainer.js +1 -1
  7. package/dist/components/Modals.js +1 -3
  8. package/dist/components/Preview.js +10 -1
  9. package/dist/components/RenderModal/ClientRenderProgress.d.ts +5 -0
  10. package/dist/components/RenderModal/ClientRenderProgress.js +56 -0
  11. package/dist/components/RenderModal/RenderStatusModal.js +34 -14
  12. package/dist/components/RenderModal/WebRenderModal.js +76 -121
  13. package/dist/components/RenderPreview.d.ts +1 -0
  14. package/dist/components/RenderPreview.js +42 -2
  15. package/dist/components/RenderQueue/ClientRenderQueueProcessor.d.ts +1 -0
  16. package/dist/components/RenderQueue/ClientRenderQueueProcessor.js +148 -0
  17. package/dist/components/RenderQueue/RenderQueueCancelledMessage.d.ts +2 -0
  18. package/dist/components/RenderQueue/RenderQueueCancelledMessage.js +15 -0
  19. package/dist/components/RenderQueue/RenderQueueError.d.ts +2 -2
  20. package/dist/components/RenderQueue/RenderQueueItem.d.ts +2 -2
  21. package/dist/components/RenderQueue/RenderQueueItem.js +22 -5
  22. package/dist/components/RenderQueue/RenderQueueItemCancelButton.d.ts +2 -2
  23. package/dist/components/RenderQueue/RenderQueueItemCancelButton.js +8 -1
  24. package/dist/components/RenderQueue/RenderQueueItemStatus.d.ts +2 -2
  25. package/dist/components/RenderQueue/RenderQueueItemStatus.js +15 -3
  26. package/dist/components/RenderQueue/RenderQueueOutputName.d.ts +2 -2
  27. package/dist/components/RenderQueue/RenderQueueOutputName.js +17 -3
  28. package/dist/components/RenderQueue/RenderQueueProgressMessage.d.ts +2 -2
  29. package/dist/components/RenderQueue/RenderQueueProgressMessage.js +7 -2
  30. package/dist/components/RenderQueue/RenderQueueRemoveItem.d.ts +2 -2
  31. package/dist/components/RenderQueue/RenderQueueRemoveItem.js +18 -1
  32. package/dist/components/RenderQueue/RenderQueueRepeat.d.ts +2 -2
  33. package/dist/components/RenderQueue/RenderQueueRepeat.js +17 -3
  34. package/dist/components/RenderQueue/client-render-queue.d.ts +21 -0
  35. package/dist/components/RenderQueue/client-render-queue.js +41 -0
  36. package/dist/components/RenderQueue/client-side-render-types.d.ts +64 -0
  37. package/dist/components/RenderQueue/client-side-render-types.js +2 -0
  38. package/dist/components/RenderQueue/context.d.ts +18 -1
  39. package/dist/components/RenderQueue/context.js +153 -11
  40. package/dist/components/RenderQueue/index.js +23 -9
  41. package/dist/esm/{chunk-b3crgnb3.js → chunk-yhf0gvmn.js} +5600 -3347
  42. package/dist/esm/internals.mjs +5600 -3347
  43. package/dist/esm/previewEntry.mjs +5627 -3374
  44. package/dist/esm/renderEntry.mjs +1 -1
  45. package/dist/helpers/document-title.d.ts +2 -2
  46. package/dist/helpers/document-title.js +10 -1
  47. package/dist/helpers/get-asset-metadata.js +11 -3
  48. package/dist/helpers/retry-payload.d.ts +3 -1
  49. package/dist/helpers/retry-payload.js +14 -1
  50. package/dist/helpers/use-menu-structure.js +17 -15
  51. package/package.json +10 -10
@@ -1,5 +1,5 @@
1
- import type { RenderJob } from '@remotion/studio-shared';
2
1
  import React from 'react';
2
+ import type { AnyRenderJob } from './context';
3
3
  export declare const RenderQueueItemStatus: React.FC<{
4
- readonly job: RenderJob;
4
+ readonly job: AnyRenderJob;
5
5
  }>;
@@ -39,6 +39,7 @@ const react_1 = __importStar(require("react"));
39
39
  const colors_1 = require("../../helpers/colors");
40
40
  const modals_1 = require("../../state/modals");
41
41
  const CircularProgress_1 = require("./CircularProgress");
42
+ const context_1 = require("./context");
42
43
  const iconStyle = {
43
44
  height: CircularProgress_1.RENDER_STATUS_INDICATOR_SIZE,
44
45
  width: CircularProgress_1.RENDER_STATUS_INDICATOR_SIZE,
@@ -53,6 +54,7 @@ const invisibleStyle = {
53
54
  const RenderQueueItemStatus = ({ job }) => {
54
55
  const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
55
56
  const [hovered, setHovered] = react_1.default.useState(false);
57
+ const isClientJob = (0, context_1.isClientRenderJob)(job);
56
58
  const onPointerEnter = (0, react_1.useCallback)(() => {
57
59
  setHovered(true);
58
60
  }, []);
@@ -67,7 +69,7 @@ const RenderQueueItemStatus = ({ job }) => {
67
69
  });
68
70
  }, [job.id, setSelectedModal]);
69
71
  if (job.status === 'failed') {
70
- return ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("svg", { style: iconStyle, viewBox: "0 0 512 512", children: (0, jsx_runtime_1.jsx)("path", { fill: colors_1.FAIL_COLOR, d: "M0 160V352L160 512H352L512 352V160L352 0H160L0 160zm353.9 32l-17 17-47 47 47 47 17 17L320 353.9l-17-17-47-47-47 47-17 17L158.1 320l17-17 47-47-47-47-17-17L192 158.1l17 17 47 47 47-47 17-17L353.9 192z" }) }) }));
72
+ return ((0, jsx_runtime_1.jsx)("button", { type: "button", style: invisibleStyle, onClick: onClick, children: (0, jsx_runtime_1.jsx)("svg", { style: iconStyle, viewBox: "0 0 512 512", children: (0, jsx_runtime_1.jsx)("path", { fill: colors_1.FAIL_COLOR, d: "M0 160V352L160 512H352L512 352V160L352 0H160L0 160zm353.9 32l-17 17-47 47 47 47 17 17L320 353.9l-17-17-47-47-47 47-17 17L158.1 320l17-17 47-47-47-47-17-17L192 158.1l17 17 47 47 47-47 17-17L353.9 192z" }) }) }));
71
73
  }
72
74
  if (job.status === 'idle') {
73
75
  return ((0, jsx_runtime_1.jsx)("svg", { style: iconStyle, viewBox: "0 0 512 512", children: (0, jsx_runtime_1.jsx)("path", { fill: colors_1.LIGHT_TEXT, d: "M256 512C114.6 512 0 397.4 0 256S114.6 0 256 0S512 114.6 512 256s-114.6 256-256 256zM232 120V256c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2V120c0-13.3-10.7-24-24-24s-24 10.7-24 24z" }) }));
@@ -76,8 +78,18 @@ const RenderQueueItemStatus = ({ job }) => {
76
78
  return ((0, jsx_runtime_1.jsx)("button", { type: "button", style: invisibleStyle, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, onClick: onClick, children: (0, jsx_runtime_1.jsx)("svg", { style: iconStyle, viewBox: "0 0 512 512", children: (0, jsx_runtime_1.jsx)("path", { fill: hovered ? 'white' : colors_1.LIGHT_TEXT, d: "M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM369 209L241 337l-17 17-17-17-64-64-17-17L160 222.1l17 17 47 47L335 175l17-17L385.9 192l-17 17z" }) }) }));
77
79
  }
78
80
  if (job.status === 'running') {
79
- // Add a minimum progress to avoid the progress bar from disappearing
80
- return ((0, jsx_runtime_1.jsx)("button", { type: "button", style: invisibleStyle, onClick: onClick, children: (0, jsx_runtime_1.jsx)(CircularProgress_1.CircularProgress, { progress: Math.max(0.07, job.progress.value) }) }));
81
+ let progressValue;
82
+ if (isClientJob) {
83
+ const { renderedFrames, totalFrames } = job.progress;
84
+ progressValue = totalFrames > 0 ? renderedFrames / totalFrames : 0;
85
+ }
86
+ else {
87
+ progressValue = job.progress.value;
88
+ }
89
+ return ((0, jsx_runtime_1.jsx)("button", { type: "button", style: invisibleStyle, onClick: onClick, children: (0, jsx_runtime_1.jsx)(CircularProgress_1.CircularProgress, { progress: Math.max(0.07, progressValue) }) }));
90
+ }
91
+ if (job.status === 'cancelled') {
92
+ return ((0, jsx_runtime_1.jsx)("svg", { style: iconStyle, viewBox: "0 0 512 512", children: (0, jsx_runtime_1.jsx)("path", { fill: colors_1.FAIL_COLOR, d: "M0 160V352L160 512H352L512 352V160L352 0H160L0 160zm353.9 32l-17 17-47 47 47 47 17 17L320 353.9l-17-17-47-47-47 47-17 17L158.1 320l17-17 47-47-47-47-17-17L192 158.1l17 17 47 47 47-47 17-17L353.9 192z" }) }));
81
93
  }
82
94
  throw new Error('Unknown job status');
83
95
  };
@@ -1,5 +1,5 @@
1
- import type { RenderJob } from '@remotion/studio-shared';
2
1
  import React from 'react';
2
+ import type { AnyRenderJob } from './context';
3
3
  export declare const RenderQueueOutputName: React.FC<{
4
- readonly job: RenderJob;
4
+ readonly job: AnyRenderJob;
5
5
  }>;
@@ -3,16 +3,30 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RenderQueueOutputName = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
+ const context_1 = require("./context");
6
7
  const item_style_1 = require("./item-style");
7
8
  const RenderQueueOutputName = ({ job }) => {
9
+ const isClientJob = (0, context_1.isClientRenderJob)(job);
10
+ const deletedOutputLocation = isClientJob
11
+ ? false
12
+ : job.deletedOutputLocation;
8
13
  const style = (0, react_1.useMemo)(() => {
9
14
  return {
10
15
  ...item_style_1.renderQueueItemSubtitleStyle,
11
- textDecoration: job.deletedOutputLocation ? 'line-through' : 'none',
16
+ textDecoration: deletedOutputLocation ? 'line-through' : 'none',
12
17
  color: item_style_1.renderQueueItemSubtitleStyle.color,
13
18
  cursor: 'inherit',
14
19
  };
15
- }, [job.deletedOutputLocation]);
16
- return ((0, jsx_runtime_1.jsx)("span", { style: style, title: job.deletedOutputLocation ? 'File was deleted' : job.outName, children: job.outName }));
20
+ }, [deletedOutputLocation]);
21
+ const getTitle = () => {
22
+ if (isClientJob) {
23
+ return `Downloaded as ${job.outName}`;
24
+ }
25
+ if (deletedOutputLocation) {
26
+ return 'File was deleted';
27
+ }
28
+ return job.outName;
29
+ };
30
+ return ((0, jsx_runtime_1.jsx)("span", { style: style, title: getTitle(), children: job.outName }));
17
31
  };
18
32
  exports.RenderQueueOutputName = RenderQueueOutputName;
@@ -1,5 +1,5 @@
1
- import type { RenderJob } from '@remotion/studio-shared';
2
1
  import React from 'react';
2
+ import type { AnyRenderJob } from './context';
3
3
  export declare const RenderQueueProgressMessage: React.FC<{
4
- readonly job: RenderJob;
4
+ readonly job: AnyRenderJob;
5
5
  }>;
@@ -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 context_1 = require("./context");
8
9
  const item_style_1 = require("./item-style");
9
10
  const outputLocation = {
10
11
  ...item_style_1.renderQueueItemSubtitleStyle,
@@ -15,12 +16,16 @@ const RenderQueueProgressMessage = ({ job }) => {
15
16
  }
16
17
  const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
17
18
  const { tabIndex } = (0, z_index_1.useZIndex)();
19
+ const isClientJob = (0, context_1.isClientRenderJob)(job);
18
20
  const onClick = (0, react_1.useCallback)(() => {
19
21
  setSelectedModal({
20
22
  type: 'render-progress',
21
23
  jobId: job.id,
22
24
  });
23
- }, [job, setSelectedModal]);
24
- return ((0, jsx_runtime_1.jsx)("button", { onClick: onClick, type: "button", style: outputLocation, tabIndex: tabIndex, title: job.progress.message, children: job.progress.message }));
25
+ }, [job.id, setSelectedModal]);
26
+ const message = isClientJob
27
+ ? `Rendering frame ${job.progress.renderedFrames}/${job.progress.totalFrames}`
28
+ : job.progress.message;
29
+ return ((0, jsx_runtime_1.jsx)("button", { onClick: onClick, type: "button", style: outputLocation, tabIndex: tabIndex, title: message, children: message }));
25
30
  };
26
31
  exports.RenderQueueProgressMessage = RenderQueueProgressMessage;
@@ -1,5 +1,5 @@
1
- import type { RenderJob } from '@remotion/studio-shared';
2
1
  import React from 'react';
2
+ import type { AnyRenderJob } from './context';
3
3
  export declare const RenderQueueRemoveItem: React.FC<{
4
- readonly job: RenderJob;
4
+ readonly job: AnyRenderJob;
5
5
  }>;
@@ -3,12 +3,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RenderQueueRemoveItem = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
+ const remotion_1 = require("remotion");
6
7
  const InlineAction_1 = require("../InlineAction");
7
8
  const NotificationCenter_1 = require("../Notifications/NotificationCenter");
8
9
  const actions_1 = require("./actions");
10
+ const context_1 = require("./context");
9
11
  const RenderQueueRemoveItem = ({ job }) => {
12
+ const isClientJob = (0, context_1.isClientRenderJob)(job);
13
+ const { removeClientJob } = (0, react_1.useContext)(context_1.RenderQueueContext);
14
+ const { canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager);
15
+ const { setCanvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionSetters);
10
16
  const onClick = (0, react_1.useCallback)((e) => {
11
17
  e.stopPropagation();
18
+ if (isClientJob) {
19
+ if (canvasContent &&
20
+ canvasContent.type === 'output-blob' &&
21
+ job.status === 'done' &&
22
+ canvasContent.getBlob === job.getBlob) {
23
+ setCanvasContent(null);
24
+ }
25
+ removeClientJob(job.id);
26
+ (0, NotificationCenter_1.showNotification)('Removed job', 2000);
27
+ return;
28
+ }
12
29
  (0, actions_1.removeRenderJob)(job)
13
30
  .then(() => {
14
31
  (0, NotificationCenter_1.showNotification)('Removed job', 2000);
@@ -16,7 +33,7 @@ const RenderQueueRemoveItem = ({ job }) => {
16
33
  .catch((err) => {
17
34
  (0, NotificationCenter_1.showNotification)(`Could not remove item: ${err.message}`, 2000);
18
35
  });
19
- }, [job]);
36
+ }, [job, isClientJob, removeClientJob, canvasContent, setCanvasContent]);
20
37
  const icon = (0, react_1.useMemo)(() => {
21
38
  return {
22
39
  height: 16,
@@ -1,5 +1,5 @@
1
- import type { RenderJob } from '@remotion/studio-shared';
2
1
  import React from 'react';
2
+ import type { AnyRenderJob } from './context';
3
3
  export declare const RenderQueueRepeatItem: React.FC<{
4
- readonly job: RenderJob;
4
+ readonly job: AnyRenderJob;
5
5
  }>;
@@ -8,18 +8,32 @@ const retry_payload_1 = require("../../helpers/retry-payload");
8
8
  const modals_1 = require("../../state/modals");
9
9
  const sidebar_1 = require("../../state/sidebar");
10
10
  const InlineAction_1 = require("../InlineAction");
11
+ const context_1 = require("./context");
11
12
  const RenderQueueRepeatItem = ({ job }) => {
12
13
  const { setSelectedModal } = (0, react_1.useContext)(modals_1.ModalsContext);
13
14
  const isMobileLayout = (0, mobile_layout_1.useMobileLayout)();
14
15
  const { setSidebarCollapsedState } = (0, react_1.useContext)(sidebar_1.SidebarContext);
16
+ const isClientJob = (0, context_1.isClientRenderJob)(job);
15
17
  const onClick = (0, react_1.useCallback)((e) => {
16
18
  e.stopPropagation();
17
- const retryPayload = (0, retry_payload_1.makeRetryPayload)(job);
18
- setSelectedModal(retryPayload);
19
+ if (isClientJob) {
20
+ const retryPayload = (0, retry_payload_1.makeClientRetryPayload)(job);
21
+ setSelectedModal(retryPayload);
22
+ }
23
+ else {
24
+ const retryPayload = (0, retry_payload_1.makeRetryPayload)(job);
25
+ setSelectedModal(retryPayload);
26
+ }
19
27
  if (isMobileLayout) {
20
28
  setSidebarCollapsedState({ left: 'collapsed', right: 'collapsed' });
21
29
  }
22
- }, [isMobileLayout, job, setSelectedModal, setSidebarCollapsedState]);
30
+ }, [
31
+ isMobileLayout,
32
+ job,
33
+ isClientJob,
34
+ setSelectedModal,
35
+ setSidebarCollapsedState,
36
+ ]);
23
37
  const icon = (0, react_1.useMemo)(() => {
24
38
  return {
25
39
  height: 12,
@@ -0,0 +1,21 @@
1
+ import type { ComponentType } from 'react';
2
+ import type { CalculateMetadataFunction } from 'remotion';
3
+ import type { ClientStillRenderJob, ClientVideoRenderJob } from './client-side-render-types';
4
+ export type CompositionRef = {
5
+ component: ComponentType<Record<string, unknown>>;
6
+ calculateMetadata: CalculateMetadataFunction<Record<string, unknown>> | null;
7
+ width: number;
8
+ height: number;
9
+ fps: number;
10
+ durationInFrames: number;
11
+ defaultProps: Record<string, unknown>;
12
+ };
13
+ export declare const registerCompositionForJob: (jobId: string, compositionRef: CompositionRef) => void;
14
+ export declare const getCompositionForJob: (jobId: string) => CompositionRef | undefined;
15
+ export declare const cleanupCompositionForJob: (jobId: string) => void;
16
+ export declare const generateJobId: () => string;
17
+ export declare const getAbortController: (jobId: string) => AbortController;
18
+ export declare const deleteAbortController: (jobId: string) => void;
19
+ export declare const cancelAbortController: (jobId: string) => void;
20
+ export type AddClientStillJobParams = Omit<ClientStillRenderJob, 'id' | 'startedAt' | 'status'>;
21
+ export type AddClientVideoJobParams = Omit<ClientVideoRenderJob, 'id' | 'startedAt' | 'status'>;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cancelAbortController = exports.deleteAbortController = exports.getAbortController = exports.generateJobId = exports.cleanupCompositionForJob = exports.getCompositionForJob = exports.registerCompositionForJob = void 0;
4
+ const compositionRegistry = new Map();
5
+ const registerCompositionForJob = (jobId, compositionRef) => {
6
+ compositionRegistry.set(jobId, compositionRef);
7
+ };
8
+ exports.registerCompositionForJob = registerCompositionForJob;
9
+ const getCompositionForJob = (jobId) => {
10
+ return compositionRegistry.get(jobId);
11
+ };
12
+ exports.getCompositionForJob = getCompositionForJob;
13
+ const cleanupCompositionForJob = (jobId) => {
14
+ compositionRegistry.delete(jobId);
15
+ };
16
+ exports.cleanupCompositionForJob = cleanupCompositionForJob;
17
+ const generateJobId = () => {
18
+ return `client-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
19
+ };
20
+ exports.generateJobId = generateJobId;
21
+ const clientJobAbortControllers = new Map();
22
+ const getAbortController = (jobId) => {
23
+ let controller = clientJobAbortControllers.get(jobId);
24
+ if (!controller) {
25
+ controller = new AbortController();
26
+ clientJobAbortControllers.set(jobId, controller);
27
+ }
28
+ return controller;
29
+ };
30
+ exports.getAbortController = getAbortController;
31
+ const deleteAbortController = (jobId) => {
32
+ clientJobAbortControllers.delete(jobId);
33
+ };
34
+ exports.deleteAbortController = deleteAbortController;
35
+ const cancelAbortController = (jobId) => {
36
+ const controller = clientJobAbortControllers.get(jobId);
37
+ if (controller) {
38
+ controller.abort();
39
+ }
40
+ };
41
+ exports.cancelAbortController = cancelAbortController;
@@ -0,0 +1,64 @@
1
+ import type { RenderStillOnWebImageFormat } from '@remotion/web-renderer';
2
+ import type { LogLevel } from 'remotion';
3
+ export type ClientRenderJobProgress = {
4
+ renderedFrames: number;
5
+ encodedFrames: number;
6
+ totalFrames: number;
7
+ };
8
+ export type GetBlobCallback = () => Promise<Blob>;
9
+ export type ClientRenderMetadata = {
10
+ width: number;
11
+ height: number;
12
+ sizeInBytes: number;
13
+ };
14
+ type ClientRenderJobDynamicStatus = {
15
+ status: 'idle';
16
+ } | {
17
+ status: 'running';
18
+ progress: ClientRenderJobProgress;
19
+ } | {
20
+ status: 'done';
21
+ getBlob: GetBlobCallback;
22
+ metadata: ClientRenderMetadata;
23
+ } | {
24
+ status: 'cancelled';
25
+ } | {
26
+ status: 'failed';
27
+ error: {
28
+ message: string;
29
+ stack: string | undefined;
30
+ };
31
+ };
32
+ type ClientRenderJobBase = {
33
+ id: string;
34
+ startedAt: number;
35
+ compositionId: string;
36
+ outName: string;
37
+ inputProps: Record<string, unknown>;
38
+ delayRenderTimeout: number;
39
+ mediaCacheSizeInBytes: number | null;
40
+ logLevel: LogLevel;
41
+ licenseKey: string | null;
42
+ scale: number;
43
+ };
44
+ export type ClientStillRenderJob = ClientRenderJobBase & {
45
+ type: 'client-still';
46
+ imageFormat: RenderStillOnWebImageFormat;
47
+ frame: number;
48
+ } & ClientRenderJobDynamicStatus;
49
+ export type ClientVideoRenderJob = ClientRenderJobBase & {
50
+ type: 'client-video';
51
+ container: string;
52
+ videoCodec: string;
53
+ audioCodec: string;
54
+ startFrame: number;
55
+ endFrame: number;
56
+ audioBitrate: string;
57
+ videoBitrate: string;
58
+ hardwareAcceleration: string;
59
+ keyframeIntervalInSeconds: number;
60
+ transparent: boolean;
61
+ muted: boolean;
62
+ } & ClientRenderJobDynamicStatus;
63
+ export type ClientRenderJob = ClientStillRenderJob | ClientVideoRenderJob;
64
+ export {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,12 +1,29 @@
1
1
  import type { RenderJob } from '@remotion/studio-shared';
2
2
  import React from 'react';
3
+ import { type AddClientStillJobParams, type AddClientVideoJobParams, type CompositionRef } from './client-render-queue';
4
+ import type { ClientRenderJob, ClientRenderJobProgress, ClientRenderMetadata, GetBlobCallback } from './client-side-render-types';
3
5
  declare global {
4
6
  interface Window {
5
7
  remotion_initialRenderQueue: RenderJob[] | null;
6
8
  }
7
9
  }
10
+ export type AnyRenderJob = RenderJob | ClientRenderJob;
11
+ export declare const isClientRenderJob: (job: AnyRenderJob) => job is ClientRenderJob;
8
12
  type RenderQueueContextType = {
9
- jobs: RenderJob[];
13
+ jobs: AnyRenderJob[];
14
+ serverJobs: RenderJob[];
15
+ clientJobs: ClientRenderJob[];
16
+ addClientStillJob: (params: AddClientStillJobParams, compositionRef: CompositionRef) => string;
17
+ addClientVideoJob: (params: AddClientVideoJobParams, compositionRef: CompositionRef) => string;
18
+ updateClientJobProgress: (jobId: string, progress: ClientRenderJobProgress) => void;
19
+ markClientJobDone: (jobId: string, getBlob: GetBlobCallback, metadata: ClientRenderMetadata) => void;
20
+ markClientJobFailed: (jobId: string, error: Error) => void;
21
+ markClientJobCancelled: (jobId: string) => void;
22
+ removeClientJob: (jobId: string) => void;
23
+ cancelClientJob: (jobId: string) => void;
24
+ setProcessJobCallback: (callback: ((job: ClientRenderJob) => Promise<void>) | null) => void;
25
+ getAbortController: (jobId: string) => AbortController;
26
+ getCompositionForJob: (jobId: string) => CompositionRef | undefined;
10
27
  };
11
28
  export declare const RenderQueueContext: React.Context<RenderQueueContextType>;
12
29
  export declare const renderJobsRef: React.RefObject<{
@@ -33,28 +33,170 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.RenderQueueContextProvider = exports.renderJobsRef = exports.RenderQueueContext = void 0;
36
+ exports.RenderQueueContextProvider = exports.renderJobsRef = exports.RenderQueueContext = exports.isClientRenderJob = void 0;
37
37
  const jsx_runtime_1 = require("react/jsx-runtime");
38
38
  const react_1 = __importStar(require("react"));
39
+ const client_render_queue_1 = require("./client-render-queue");
40
+ const isClientRenderJob = (job) => {
41
+ return job.type === 'client-still' || job.type === 'client-video';
42
+ };
43
+ exports.isClientRenderJob = isClientRenderJob;
44
+ const noopString = () => '';
45
+ const noop = () => undefined;
39
46
  exports.RenderQueueContext = react_1.default.createContext({
40
47
  jobs: [],
48
+ serverJobs: [],
49
+ clientJobs: [],
50
+ addClientStillJob: noopString,
51
+ addClientVideoJob: noopString,
52
+ updateClientJobProgress: noop,
53
+ markClientJobDone: noop,
54
+ markClientJobFailed: noop,
55
+ markClientJobCancelled: noop,
56
+ removeClientJob: noop,
57
+ cancelClientJob: noop,
58
+ setProcessJobCallback: noop,
59
+ getAbortController: () => new AbortController(),
60
+ getCompositionForJob: () => undefined,
41
61
  });
42
62
  exports.renderJobsRef = (0, react_1.createRef)();
43
63
  const RenderQueueContextProvider = ({ children }) => {
44
64
  var _a;
45
- const [jobs, setJobs] = (0, react_1.useState)((_a = window.remotion_initialRenderQueue) !== null && _a !== void 0 ? _a : []);
46
- const value = (0, react_1.useMemo)(() => {
47
- return {
48
- jobs,
65
+ const [serverJobs, setServerJobs] = (0, react_1.useState)((_a = window.remotion_initialRenderQueue) !== null && _a !== void 0 ? _a : []);
66
+ const [clientJobs, setClientJobs] = (0, react_1.useState)([]);
67
+ const [currentlyProcessing, setCurrentlyProcessing] = (0, react_1.useState)(null);
68
+ const processJobCallbackRef = (0, react_1.useRef)(null);
69
+ // Process next job when state changes
70
+ (0, react_1.useEffect)(() => {
71
+ if (currentlyProcessing) {
72
+ return;
73
+ }
74
+ const nextJob = clientJobs.find((job) => job.status === 'idle');
75
+ if (!nextJob || !processJobCallbackRef.current) {
76
+ return;
77
+ }
78
+ setCurrentlyProcessing(nextJob.id);
79
+ setClientJobs((prev) => prev.map((job) => job.id === nextJob.id
80
+ ? {
81
+ ...job,
82
+ status: 'running',
83
+ progress: { renderedFrames: 0, encodedFrames: 0, totalFrames: 0 },
84
+ }
85
+ : job));
86
+ processJobCallbackRef.current(nextJob);
87
+ }, [clientJobs, currentlyProcessing]);
88
+ const addClientStillJob = (0, react_1.useCallback)((params, compositionRef) => {
89
+ const id = (0, client_render_queue_1.generateJobId)();
90
+ (0, client_render_queue_1.registerCompositionForJob)(id, compositionRef);
91
+ const newJob = {
92
+ ...params,
93
+ id,
94
+ startedAt: Date.now(),
95
+ status: 'idle',
49
96
  };
50
- }, [jobs]);
51
- (0, react_1.useImperativeHandle)(exports.renderJobsRef, () => {
52
- return {
53
- updateRenderJobs: (newJobs) => {
54
- setJobs(newJobs);
55
- },
97
+ setClientJobs((prev) => [...prev, newJob]);
98
+ return id;
99
+ }, []);
100
+ const addClientVideoJob = (0, react_1.useCallback)((params, compositionRef) => {
101
+ const id = (0, client_render_queue_1.generateJobId)();
102
+ (0, client_render_queue_1.registerCompositionForJob)(id, compositionRef);
103
+ const newJob = {
104
+ ...params,
105
+ id,
106
+ startedAt: Date.now(),
107
+ status: 'idle',
56
108
  };
109
+ setClientJobs((prev) => [...prev, newJob]);
110
+ return id;
111
+ }, []);
112
+ const updateClientJobProgress = (0, react_1.useCallback)((jobId, progress) => {
113
+ setClientJobs((prev) => prev.map((job) => job.id === jobId
114
+ ? { ...job, status: 'running', progress }
115
+ : job));
116
+ }, []);
117
+ const markClientJobDone = (0, react_1.useCallback)((jobId, getBlob, metadata) => {
118
+ (0, client_render_queue_1.deleteAbortController)(jobId);
119
+ (0, client_render_queue_1.cleanupCompositionForJob)(jobId);
120
+ setClientJobs((prev) => prev.map((job) => job.id === jobId
121
+ ? { ...job, status: 'done', getBlob, metadata }
122
+ : job));
123
+ setCurrentlyProcessing(null);
124
+ }, []);
125
+ const markClientJobFailed = (0, react_1.useCallback)((jobId, error) => {
126
+ (0, client_render_queue_1.deleteAbortController)(jobId);
127
+ (0, client_render_queue_1.cleanupCompositionForJob)(jobId);
128
+ setClientJobs((prev) => prev.map((job) => job.id === jobId
129
+ ? {
130
+ ...job,
131
+ status: 'failed',
132
+ error: { message: error.message, stack: error.stack },
133
+ }
134
+ : job));
135
+ setCurrentlyProcessing(null);
57
136
  }, []);
137
+ const markClientJobCancelled = (0, react_1.useCallback)((jobId) => {
138
+ (0, client_render_queue_1.deleteAbortController)(jobId);
139
+ (0, client_render_queue_1.cleanupCompositionForJob)(jobId);
140
+ setClientJobs((prev) => prev.map((job) => job.id === jobId
141
+ ? {
142
+ ...job,
143
+ status: 'cancelled',
144
+ }
145
+ : job));
146
+ setCurrentlyProcessing(null);
147
+ }, []);
148
+ const removeClientJob = (0, react_1.useCallback)((jobId) => {
149
+ setClientJobs((prev) => {
150
+ const jobToRemove = prev.find((j) => j.id === jobId);
151
+ if ((jobToRemove === null || jobToRemove === void 0 ? void 0 : jobToRemove.status) === 'running') {
152
+ return prev;
153
+ }
154
+ (0, client_render_queue_1.deleteAbortController)(jobId);
155
+ (0, client_render_queue_1.cleanupCompositionForJob)(jobId);
156
+ return prev.filter((job) => job.id !== jobId);
157
+ });
158
+ }, []);
159
+ const cancelClientJob = (0, react_1.useCallback)((jobId) => {
160
+ (0, client_render_queue_1.cancelAbortController)(jobId);
161
+ }, []);
162
+ const setProcessJobCallback = (0, react_1.useCallback)((callback) => {
163
+ processJobCallbackRef.current = callback;
164
+ }, []);
165
+ (0, react_1.useImperativeHandle)(exports.renderJobsRef, () => ({
166
+ updateRenderJobs: (newJobs) => {
167
+ setServerJobs(newJobs);
168
+ },
169
+ }), []);
170
+ const value = (0, react_1.useMemo)(() => {
171
+ return {
172
+ jobs: [...serverJobs, ...clientJobs],
173
+ serverJobs,
174
+ clientJobs,
175
+ addClientStillJob,
176
+ addClientVideoJob,
177
+ updateClientJobProgress,
178
+ markClientJobDone,
179
+ markClientJobFailed,
180
+ markClientJobCancelled,
181
+ removeClientJob,
182
+ cancelClientJob,
183
+ setProcessJobCallback,
184
+ getAbortController: client_render_queue_1.getAbortController,
185
+ getCompositionForJob: client_render_queue_1.getCompositionForJob,
186
+ };
187
+ }, [
188
+ serverJobs,
189
+ clientJobs,
190
+ addClientStillJob,
191
+ addClientVideoJob,
192
+ updateClientJobProgress,
193
+ markClientJobDone,
194
+ markClientJobFailed,
195
+ markClientJobCancelled,
196
+ removeClientJob,
197
+ cancelClientJob,
198
+ setProcessJobCallback,
199
+ ]);
58
200
  return ((0, jsx_runtime_1.jsx)(exports.RenderQueueContext.Provider, { value: value, children: children }));
59
201
  };
60
202
  exports.RenderQueueContextProvider = RenderQueueContextProvider;
@@ -89,17 +89,31 @@ const RenderQueue = () => {
89
89
  previousJobCount.current = jobCount;
90
90
  }, [jobCount]);
91
91
  const selectedJob = (0, react_1.useMemo)(() => {
92
- let selectedIndex = -1;
93
- for (let i = 0; i < jobs.length; i++) {
94
- const job = jobs[i];
95
- if (canvasContent &&
96
- canvasContent.type === 'output' &&
97
- canvasContent.path === `/${job.outName}` &&
98
- job.status === 'done') {
99
- selectedIndex = i;
92
+ if (!canvasContent) {
93
+ return -1;
94
+ }
95
+ if (canvasContent.type === 'output-blob') {
96
+ for (let i = 0; i < jobs.length; i++) {
97
+ const job = jobs[i];
98
+ if ((0, context_1.isClientRenderJob)(job) && job.status === 'done') {
99
+ if (canvasContent.getBlob === job.getBlob) {
100
+ return i;
101
+ }
102
+ }
103
+ }
104
+ return -1;
105
+ }
106
+ if (canvasContent.type === 'output') {
107
+ for (let i = 0; i < jobs.length; i++) {
108
+ const job = jobs[i];
109
+ if (!(0, context_1.isClientRenderJob)(job) &&
110
+ job.status === 'done' &&
111
+ canvasContent.path === `/${job.outName}`) {
112
+ return i;
113
+ }
100
114
  }
101
115
  }
102
- return selectedIndex;
116
+ return -1;
103
117
  }, [canvasContent, jobs]);
104
118
  if (connectionStatus === 'disconnected') {
105
119
  return ((0, jsx_runtime_1.jsxs)("div", { style: explainer, children: [(0, jsx_runtime_1.jsx)(layout_1.Spacing, { y: 5 }), (0, jsx_runtime_1.jsx)("div", { style: errorExplanation, children: "The studio server has disconnected." }), (0, jsx_runtime_1.jsx)(layout_1.Spacing, { y: 2, block: true })] }));