@remotion/web-renderer 4.0.374 → 4.0.376

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 (46) hide show
  1. package/dist/artifact.d.ts +24 -0
  2. package/dist/artifact.js +59 -0
  3. package/dist/calculate-transforms.d.ts +9 -0
  4. package/dist/calculate-transforms.js +61 -0
  5. package/dist/composable.d.ts +3 -0
  6. package/dist/compose-canvas.d.ts +1 -0
  7. package/dist/compose-canvas.js +14 -0
  8. package/dist/compose-svg.d.ts +9 -0
  9. package/dist/compose-svg.js +29 -0
  10. package/dist/compose.js +6 -36
  11. package/dist/create-scaffold.d.ts +31 -0
  12. package/dist/create-scaffold.js +92 -0
  13. package/dist/esm/index.mjs +7851 -147
  14. package/dist/find-capturable-elements.d.ts +2 -0
  15. package/dist/find-capturable-elements.js +28 -0
  16. package/dist/frame-range.d.ts +2 -0
  17. package/dist/frame-range.js +15 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.js +1 -0
  20. package/dist/mediabunny-mappings.d.ts +9 -0
  21. package/dist/mediabunny-mappings.js +53 -0
  22. package/dist/parse-transform-origin.d.ts +4 -0
  23. package/dist/parse-transform-origin.js +7 -0
  24. package/dist/props-if-has-props.d.ts +37 -0
  25. package/dist/props-if-has-props.js +1 -0
  26. package/dist/render-media-on-web.d.ts +45 -0
  27. package/dist/render-media-on-web.js +190 -0
  28. package/dist/render-still-on-web.d.ts +17 -11
  29. package/dist/render-still-on-web.js +70 -99
  30. package/dist/take-screenshot.d.ts +12 -0
  31. package/dist/take-screenshot.js +18 -0
  32. package/dist/throttle-progress.d.ts +6 -0
  33. package/dist/throttle-progress.js +43 -0
  34. package/dist/update-time.d.ts +14 -0
  35. package/dist/update-time.js +17 -0
  36. package/dist/validate-video-frame.d.ts +16 -0
  37. package/dist/validate-video-frame.js +34 -0
  38. package/dist/wait-for-ready.d.ts +6 -1
  39. package/dist/wait-for-ready.js +20 -9
  40. package/dist/with-resolvers.d.ts +10 -0
  41. package/dist/with-resolvers.js +9 -0
  42. package/package.json +12 -7
  43. package/dist/find-canvas-elements.d.ts +0 -2
  44. package/dist/find-canvas-elements.js +0 -12
  45. package/dist/find-svg-elements.d.ts +0 -2
  46. package/dist/find-svg-elements.js +0 -12
@@ -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
+ };
@@ -1,2 +1,7 @@
1
1
  import type { _InternalTypes } from 'remotion';
2
- export declare const waitForReady: (timeoutInMilliseconds: number, scope: _InternalTypes["DelayRenderScope"]) => Promise<unknown>;
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>;
@@ -1,24 +1,35 @@
1
- export const waitForReady = (timeoutInMilliseconds, scope) => {
2
- const { promise, resolve, reject } = Promise.withResolvers();
1
+ import { withResolvers } from './with-resolvers';
2
+ export const waitForReady = ({ timeoutInMilliseconds, scope, signal, apiName, }) => {
3
3
  const start = Date.now();
4
- const interval = setInterval(() => {
4
+ const { promise, resolve, reject } = withResolvers();
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
+ }
5
15
  if (scope.remotion_renderReady === true) {
6
- resolve(true);
7
- clearInterval(interval);
16
+ resolve();
8
17
  return;
9
18
  }
10
19
  if (scope.remotion_cancelledError !== undefined) {
20
+ cancelled = true;
11
21
  reject(scope.remotion_cancelledError);
12
- clearInterval(interval);
13
22
  return;
14
23
  }
15
24
  if (Date.now() - start > timeoutInMilliseconds + 3000) {
16
- // TODO: Error message should be just as good
25
+ cancelled = true;
17
26
  reject(new Error(Object.values(scope.remotion_delayRenderTimeouts)
18
27
  .map((d) => d.label)
19
28
  .join(', ')));
20
- clearInterval(interval);
29
+ return;
21
30
  }
22
- }, 50);
31
+ requestAnimationFrame(check);
32
+ };
33
+ requestAnimationFrame(check);
23
34
  return promise;
24
35
  };
@@ -0,0 +1,10 @@
1
+ export interface WithResolvers<T> {
2
+ promise: Promise<T>;
3
+ resolve: (value: T | PromiseLike<T>) => void;
4
+ reject: (reason?: any) => void;
5
+ }
6
+ export declare const withResolvers: <T>() => {
7
+ promise: Promise<T>;
8
+ resolve: (value: T | PromiseLike<T>) => void;
9
+ reject: (reason?: any) => void;
10
+ };
@@ -0,0 +1,9 @@
1
+ export const withResolvers = function () {
2
+ let resolve;
3
+ let reject;
4
+ const promise = new Promise((res, rej) => {
5
+ resolve = res;
6
+ reject = rej;
7
+ });
8
+ return { promise, resolve: resolve, reject: reject };
9
+ };
package/package.json CHANGED
@@ -3,29 +3,34 @@
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.374",
6
+ "version": "4.0.376",
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
- "test": "node src/test/execute.mjs"
13
+ "testwebrenderer": "vitest src/test --browser --run"
14
14
  },
15
15
  "author": "Remotion <jonny@remotion.dev>",
16
16
  "license": "UNLICENSED",
17
17
  "dependencies": {
18
- "remotion": "4.0.374"
18
+ "remotion": "4.0.376",
19
+ "mediabunny": "1.24.5"
19
20
  },
20
21
  "devDependencies": {
21
- "@remotion/eslint-config-internal": "4.0.374",
22
+ "@remotion/eslint-config-internal": "4.0.376",
23
+ "@remotion/player": "4.0.376",
24
+ "@remotion/media": "4.0.376",
22
25
  "@vitejs/plugin-react": "^5.1.0",
23
- "@vitest/browser-webdriverio": "4.0.7",
26
+ "@vitest/browser-playwright": "4.0.9",
27
+ "playwright": "1.55.1",
24
28
  "eslint": "9.19.0",
25
29
  "react": "19.0.0",
26
30
  "react-dom": "19.0.0",
27
- "vitest": "4.0.7",
28
- "vitest-browser-react": "^2.0.2"
31
+ "vitest": "4.0.9",
32
+ "vitest-browser-react": "^2.0.2",
33
+ "zod": "3.22.3"
29
34
  },
30
35
  "peerDependencies": {
31
36
  "react": ">=18.0.0",
@@ -1,2 +0,0 @@
1
- import type { Composable } from './composable';
2
- export declare const findCanvasElements: (element: HTMLDivElement) => Composable[];
@@ -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,2 +0,0 @@
1
- import type { Composable } from './composable';
2
- export declare const findSvgElements: (element: HTMLDivElement) => Composable[];
@@ -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
- };