@remotion/web-renderer 4.0.375 → 4.0.377
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/artifact.d.ts +24 -0
- package/dist/artifact.js +59 -0
- package/dist/calculate-transforms.d.ts +9 -0
- package/dist/calculate-transforms.js +61 -0
- package/dist/composable.d.ts +3 -0
- package/dist/compose-canvas.d.ts +1 -0
- package/dist/compose-canvas.js +18 -0
- package/dist/compose-svg.d.ts +1 -0
- package/dist/compose-svg.js +34 -0
- package/dist/compose.js +2 -42
- package/dist/create-scaffold.d.ts +31 -0
- package/dist/create-scaffold.js +92 -0
- package/dist/esm/index.mjs +7846 -179
- package/dist/find-capturable-elements.d.ts +2 -0
- package/dist/find-capturable-elements.js +28 -0
- package/dist/frame-range.d.ts +2 -0
- package/dist/frame-range.js +15 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +1 -0
- package/dist/mediabunny-mappings.d.ts +9 -0
- package/dist/mediabunny-mappings.js +53 -0
- package/dist/parse-transform-origin.d.ts +4 -0
- package/dist/parse-transform-origin.js +7 -0
- package/dist/props-if-has-props.d.ts +37 -0
- package/dist/props-if-has-props.js +1 -0
- package/dist/render-media-on-web.d.ts +45 -0
- package/dist/render-media-on-web.js +190 -0
- package/dist/render-still-on-web.d.ts +17 -12
- package/dist/render-still-on-web.js +69 -104
- package/dist/take-screenshot.d.ts +12 -0
- package/dist/take-screenshot.js +18 -0
- package/dist/throttle-progress.d.ts +6 -0
- package/dist/throttle-progress.js +43 -0
- package/dist/update-time.d.ts +14 -0
- package/dist/update-time.js +17 -0
- package/dist/validate-video-frame.d.ts +16 -0
- package/dist/validate-video-frame.js +34 -0
- package/dist/wait-for-ready.d.ts +6 -1
- package/dist/wait-for-ready.js +18 -14
- package/package.json +13 -8
- package/dist/error-boundary.d.ts +0 -16
- package/dist/error-boundary.js +0 -20
- package/dist/find-canvas-elements.d.ts +0 -2
- package/dist/find-canvas-elements.js +0 -12
- package/dist/find-svg-elements.d.ts +0 -2
- package/dist/find-svg-elements.js +0 -12
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RenderStillOnWebImageFormat } from './render-still-on-web';
|
|
2
|
+
export declare const createFrame: ({ div, width, height, }: {
|
|
3
|
+
div: HTMLDivElement;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
}) => Promise<OffscreenCanvas>;
|
|
7
|
+
export declare const takeScreenshot: ({ div, width, height, imageFormat, }: {
|
|
8
|
+
div: HTMLDivElement;
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
imageFormat: RenderStillOnWebImageFormat;
|
|
12
|
+
}) => Promise<Blob>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { compose } from './compose';
|
|
2
|
+
import { findCapturableElements } from './find-capturable-elements';
|
|
3
|
+
export const createFrame = async ({ div, width, height, }) => {
|
|
4
|
+
const composables = findCapturableElements(div);
|
|
5
|
+
const composed = await compose({
|
|
6
|
+
composables,
|
|
7
|
+
width,
|
|
8
|
+
height,
|
|
9
|
+
});
|
|
10
|
+
return composed;
|
|
11
|
+
};
|
|
12
|
+
export const takeScreenshot = async ({ div, width, height, imageFormat, }) => {
|
|
13
|
+
const frame = await createFrame({ div, width, height });
|
|
14
|
+
const imageData = await frame.convertToBlob({
|
|
15
|
+
type: `image/${imageFormat}`,
|
|
16
|
+
});
|
|
17
|
+
return imageData;
|
|
18
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RenderMediaOnWebProgressCallback } from './render-media-on-web';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a throttled version of a progress callback that ensures it's not called
|
|
4
|
+
* more frequently than the specified interval (default: 250ms)
|
|
5
|
+
*/
|
|
6
|
+
export declare const createThrottledProgressCallback: (callback: RenderMediaOnWebProgressCallback | null, throttleMs?: number) => RenderMediaOnWebProgressCallback | null;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const DEFAULT_THROTTLE_MS = 250;
|
|
2
|
+
/**
|
|
3
|
+
* Creates a throttled version of a progress callback that ensures it's not called
|
|
4
|
+
* more frequently than the specified interval (default: 250ms)
|
|
5
|
+
*/
|
|
6
|
+
export const createThrottledProgressCallback = (callback, throttleMs = DEFAULT_THROTTLE_MS) => {
|
|
7
|
+
if (!callback) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
let lastCallTime = 0;
|
|
11
|
+
let pendingUpdate = null;
|
|
12
|
+
let timeoutId = null;
|
|
13
|
+
const throttled = (progress) => {
|
|
14
|
+
const now = Date.now();
|
|
15
|
+
const timeSinceLastCall = now - lastCallTime;
|
|
16
|
+
// Always store the latest progress
|
|
17
|
+
pendingUpdate = progress;
|
|
18
|
+
// If enough time has passed, call immediately
|
|
19
|
+
if (timeSinceLastCall >= throttleMs) {
|
|
20
|
+
lastCallTime = now;
|
|
21
|
+
callback(progress);
|
|
22
|
+
pendingUpdate = null;
|
|
23
|
+
// Clear any pending timeout
|
|
24
|
+
if (timeoutId !== null) {
|
|
25
|
+
clearTimeout(timeoutId);
|
|
26
|
+
timeoutId = null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else if (timeoutId === null) {
|
|
30
|
+
// Schedule a call for when the throttle period expires
|
|
31
|
+
const remainingTime = throttleMs - timeSinceLastCall;
|
|
32
|
+
timeoutId = setTimeout(() => {
|
|
33
|
+
if (pendingUpdate !== null) {
|
|
34
|
+
lastCallTime = Date.now();
|
|
35
|
+
callback(pendingUpdate);
|
|
36
|
+
pendingUpdate = null;
|
|
37
|
+
}
|
|
38
|
+
timeoutId = null;
|
|
39
|
+
}, remainingTime);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
return throttled;
|
|
43
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { LogLevel } from 'remotion';
|
|
3
|
+
export type TimeUpdaterRef = {
|
|
4
|
+
update: (frame: number) => void;
|
|
5
|
+
};
|
|
6
|
+
export declare const UpdateTime: React.FC<{
|
|
7
|
+
readonly children: React.ReactNode;
|
|
8
|
+
readonly audioEnabled: boolean;
|
|
9
|
+
readonly videoEnabled: boolean;
|
|
10
|
+
readonly logLevel: LogLevel;
|
|
11
|
+
readonly compId: string;
|
|
12
|
+
readonly initialFrame: number;
|
|
13
|
+
readonly timeUpdater: React.RefObject<TimeUpdaterRef | null>;
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useImperativeHandle, useState } from 'react';
|
|
3
|
+
import { flushSync } from 'react-dom';
|
|
4
|
+
import { Internals } from 'remotion';
|
|
5
|
+
export const UpdateTime = ({ children, audioEnabled, videoEnabled, logLevel, compId, initialFrame, timeUpdater, }) => {
|
|
6
|
+
const [frame, setFrame] = useState(initialFrame);
|
|
7
|
+
useImperativeHandle(timeUpdater, () => ({
|
|
8
|
+
update: (f) => {
|
|
9
|
+
flushSync(() => {
|
|
10
|
+
setFrame(f);
|
|
11
|
+
});
|
|
12
|
+
},
|
|
13
|
+
}));
|
|
14
|
+
return (_jsx(Internals.RemotionRootContexts, { audioEnabled: audioEnabled, videoEnabled: videoEnabled, logLevel: logLevel, numberOfAudioTags: 0, audioLatencyHint: "interactive", frameState: {
|
|
15
|
+
[compId]: frame,
|
|
16
|
+
}, children: children }));
|
|
17
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates and processes a VideoFrame returned from an onFrame callback
|
|
3
|
+
*/
|
|
4
|
+
export type OnFrameCallback = (frame: VideoFrame) => VideoFrame | Promise<VideoFrame>;
|
|
5
|
+
export type ValidateVideoFrameOptions = {
|
|
6
|
+
originalFrame: VideoFrame;
|
|
7
|
+
returnedFrame: VideoFrame;
|
|
8
|
+
expectedWidth: number;
|
|
9
|
+
expectedHeight: number;
|
|
10
|
+
expectedTimestamp: number;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Validates that a VideoFrame returned from onFrame callback matches expected dimensions and timestamp
|
|
14
|
+
* If validation fails, closes both frames and throws an error
|
|
15
|
+
*/
|
|
16
|
+
export declare const validateVideoFrame: ({ originalFrame, returnedFrame, expectedWidth, expectedHeight, expectedTimestamp, }: ValidateVideoFrameOptions) => VideoFrame;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates and processes a VideoFrame returned from an onFrame callback
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Validates that a VideoFrame returned from onFrame callback matches expected dimensions and timestamp
|
|
6
|
+
* If validation fails, closes both frames and throws an error
|
|
7
|
+
*/
|
|
8
|
+
export const validateVideoFrame = ({ originalFrame, returnedFrame, expectedWidth, expectedHeight, expectedTimestamp, }) => {
|
|
9
|
+
// Validate that the returned frame is actually a VideoFrame
|
|
10
|
+
if (!(returnedFrame instanceof VideoFrame)) {
|
|
11
|
+
originalFrame.close();
|
|
12
|
+
throw new Error('onFrame callback must return a VideoFrame or void');
|
|
13
|
+
}
|
|
14
|
+
// Check if it's the same frame (no validation needed)
|
|
15
|
+
if (returnedFrame === originalFrame) {
|
|
16
|
+
return returnedFrame;
|
|
17
|
+
}
|
|
18
|
+
// Validate dimensions
|
|
19
|
+
if (returnedFrame.displayWidth !== expectedWidth ||
|
|
20
|
+
returnedFrame.displayHeight !== expectedHeight) {
|
|
21
|
+
originalFrame.close();
|
|
22
|
+
returnedFrame.close();
|
|
23
|
+
throw new Error(`VideoFrame dimensions mismatch: expected ${expectedWidth}x${expectedHeight}, got ${returnedFrame.displayWidth}x${returnedFrame.displayHeight}`);
|
|
24
|
+
}
|
|
25
|
+
// Validate timestamp
|
|
26
|
+
if (returnedFrame.timestamp !== expectedTimestamp) {
|
|
27
|
+
originalFrame.close();
|
|
28
|
+
returnedFrame.close();
|
|
29
|
+
throw new Error(`VideoFrame timestamp mismatch: expected ${expectedTimestamp}, got ${returnedFrame.timestamp}`);
|
|
30
|
+
}
|
|
31
|
+
// If we got a different frame but it's valid, close the original and use the new one
|
|
32
|
+
originalFrame.close();
|
|
33
|
+
return returnedFrame;
|
|
34
|
+
};
|
package/dist/wait-for-ready.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import type { _InternalTypes } from 'remotion';
|
|
2
|
-
export declare const waitForReady: (timeoutInMilliseconds
|
|
2
|
+
export declare const waitForReady: ({ timeoutInMilliseconds, scope, signal, apiName, }: {
|
|
3
|
+
timeoutInMilliseconds: number;
|
|
4
|
+
scope: _InternalTypes["DelayRenderScope"];
|
|
5
|
+
signal: AbortSignal | null;
|
|
6
|
+
apiName: "renderMediaOnWeb" | "renderStillOnWeb";
|
|
7
|
+
}) => Promise<void>;
|
package/dist/wait-for-ready.js
CHANGED
|
@@ -1,31 +1,35 @@
|
|
|
1
1
|
import { withResolvers } from './with-resolvers';
|
|
2
|
-
export const waitForReady = (timeoutInMilliseconds, scope) => {
|
|
3
|
-
if (scope.remotion_renderReady === true) {
|
|
4
|
-
return Promise.resolve();
|
|
5
|
-
}
|
|
2
|
+
export const waitForReady = ({ timeoutInMilliseconds, scope, signal, apiName, }) => {
|
|
6
3
|
const start = Date.now();
|
|
7
4
|
const { promise, resolve, reject } = withResolvers();
|
|
8
|
-
|
|
5
|
+
let cancelled = false;
|
|
6
|
+
const check = () => {
|
|
7
|
+
if (cancelled) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
11
|
+
cancelled = true;
|
|
12
|
+
reject(new Error(`${apiName}() was cancelled`));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
9
15
|
if (scope.remotion_renderReady === true) {
|
|
10
|
-
|
|
11
|
-
requestAnimationFrame(() => {
|
|
12
|
-
resolve();
|
|
13
|
-
});
|
|
14
|
-
clearInterval(interval);
|
|
16
|
+
resolve();
|
|
15
17
|
return;
|
|
16
18
|
}
|
|
17
19
|
if (scope.remotion_cancelledError !== undefined) {
|
|
20
|
+
cancelled = true;
|
|
18
21
|
reject(scope.remotion_cancelledError);
|
|
19
|
-
clearInterval(interval);
|
|
20
22
|
return;
|
|
21
23
|
}
|
|
22
24
|
if (Date.now() - start > timeoutInMilliseconds + 3000) {
|
|
23
|
-
|
|
25
|
+
cancelled = true;
|
|
24
26
|
reject(new Error(Object.values(scope.remotion_delayRenderTimeouts)
|
|
25
27
|
.map((d) => d.label)
|
|
26
28
|
.join(', ')));
|
|
27
|
-
|
|
29
|
+
return;
|
|
28
30
|
}
|
|
29
|
-
|
|
31
|
+
requestAnimationFrame(check);
|
|
32
|
+
};
|
|
33
|
+
requestAnimationFrame(check);
|
|
30
34
|
return promise;
|
|
31
35
|
};
|
package/package.json
CHANGED
|
@@ -3,30 +3,35 @@
|
|
|
3
3
|
"url": "https://github.com/remotion-dev/remotion/tree/main/packages/web-renderer"
|
|
4
4
|
},
|
|
5
5
|
"name": "@remotion/web-renderer",
|
|
6
|
-
"version": "4.0.
|
|
6
|
+
"version": "4.0.377",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"scripts": {
|
|
10
10
|
"formatting": "prettier --experimental-cli src --check",
|
|
11
11
|
"lint": "eslint src",
|
|
12
12
|
"make": "tsc -d && bun --env-file=../.env.bundle bundle.ts",
|
|
13
|
-
"
|
|
13
|
+
"testwebrenderer": "vitest src/test --browser --run",
|
|
14
|
+
"studio": "cd ../example && bunx remotion studio ../web-renderer/src/test/studio.ts"
|
|
14
15
|
},
|
|
15
16
|
"author": "Remotion <jonny@remotion.dev>",
|
|
16
17
|
"license": "UNLICENSED",
|
|
17
18
|
"dependencies": {
|
|
18
|
-
"remotion": "4.0.
|
|
19
|
+
"remotion": "4.0.377",
|
|
20
|
+
"mediabunny": "1.24.5"
|
|
19
21
|
},
|
|
20
22
|
"devDependencies": {
|
|
21
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
22
|
-
"@remotion/player": "4.0.
|
|
23
|
+
"@remotion/eslint-config-internal": "4.0.377",
|
|
24
|
+
"@remotion/player": "4.0.377",
|
|
25
|
+
"@remotion/media": "4.0.377",
|
|
23
26
|
"@vitejs/plugin-react": "^5.1.0",
|
|
24
|
-
"@vitest/browser-
|
|
27
|
+
"@vitest/browser-playwright": "4.0.9",
|
|
28
|
+
"playwright": "1.55.1",
|
|
25
29
|
"eslint": "9.19.0",
|
|
26
30
|
"react": "19.0.0",
|
|
27
31
|
"react-dom": "19.0.0",
|
|
28
|
-
"vitest": "4.0.
|
|
29
|
-
"vitest-browser-react": "^2.0.2"
|
|
32
|
+
"vitest": "4.0.9",
|
|
33
|
+
"vitest-browser-react": "^2.0.2",
|
|
34
|
+
"zod": "3.22.3"
|
|
30
35
|
},
|
|
31
36
|
"peerDependencies": {
|
|
32
37
|
"react": ">=18.0.0",
|
package/dist/error-boundary.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export declare class ErrorBoundary extends React.Component<{
|
|
3
|
-
onError: (error: Error) => void;
|
|
4
|
-
children: React.ReactNode;
|
|
5
|
-
}, {
|
|
6
|
-
hasError: Error | null;
|
|
7
|
-
}> {
|
|
8
|
-
state: {
|
|
9
|
-
hasError: null;
|
|
10
|
-
};
|
|
11
|
-
static getDerivedStateFromError(): {
|
|
12
|
-
hasError: boolean;
|
|
13
|
-
};
|
|
14
|
-
componentDidCatch(error: Error): void;
|
|
15
|
-
render(): React.ReactNode;
|
|
16
|
-
}
|
package/dist/error-boundary.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
export class ErrorBoundary extends React.Component {
|
|
3
|
-
constructor() {
|
|
4
|
-
super(...arguments);
|
|
5
|
-
this.state = { hasError: null };
|
|
6
|
-
}
|
|
7
|
-
static getDerivedStateFromError() {
|
|
8
|
-
// Update state so the next render will show the fallback UI.
|
|
9
|
-
return { hasError: true };
|
|
10
|
-
}
|
|
11
|
-
componentDidCatch(error) {
|
|
12
|
-
this.props.onError(error);
|
|
13
|
-
}
|
|
14
|
-
render() {
|
|
15
|
-
if (this.state.hasError) {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
return this.props.children;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export const findCanvasElements = (element) => {
|
|
2
|
-
const canvasElements = element.querySelectorAll('canvas');
|
|
3
|
-
const composables = [];
|
|
4
|
-
Array.from(canvasElements).forEach((canvasElement) => {
|
|
5
|
-
const canvas = canvasElement;
|
|
6
|
-
composables.push({
|
|
7
|
-
type: 'canvas',
|
|
8
|
-
element: canvas,
|
|
9
|
-
});
|
|
10
|
-
});
|
|
11
|
-
return composables;
|
|
12
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export const findSvgElements = (element) => {
|
|
2
|
-
const svgElements = element.querySelectorAll('svg');
|
|
3
|
-
const composables = [];
|
|
4
|
-
Array.from(svgElements).forEach((svgElement) => {
|
|
5
|
-
const svg = svgElement;
|
|
6
|
-
composables.push({
|
|
7
|
-
type: 'svg',
|
|
8
|
-
element: svg,
|
|
9
|
-
});
|
|
10
|
-
});
|
|
11
|
-
return composables;
|
|
12
|
-
};
|