Add .gitignore to exclude all node packages and lock files
This commit is contained in:
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import type { RemotionAnimatedImageProps } from './props';
|
||||
export declare const AnimatedImage: import("react").ForwardRefExoticComponent<RemotionAnimatedImageProps & import("react").RefAttributes<HTMLCanvasElement>>;
|
||||
Generated
Vendored
+114
@@ -0,0 +1,114 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AnimatedImage = void 0;
|
||||
const jsx_runtime_1 = require("react/jsx-runtime");
|
||||
const react_1 = require("react");
|
||||
const cancel_render_js_1 = require("../cancel-render.js");
|
||||
const use_current_frame_js_1 = require("../use-current-frame.js");
|
||||
const use_delay_render_js_1 = require("../use-delay-render.js");
|
||||
const use_video_config_js_1 = require("../use-video-config.js");
|
||||
const canvas_1 = require("./canvas");
|
||||
const decode_image_js_1 = require("./decode-image.js");
|
||||
const resolve_image_source_1 = require("./resolve-image-source");
|
||||
exports.AnimatedImage = (0, react_1.forwardRef)(({ src, width, height, onError, loopBehavior = 'loop', playbackRate = 1, fit = 'fill', ...props }, canvasRef) => {
|
||||
const mountState = (0, react_1.useRef)({ isMounted: true });
|
||||
(0, react_1.useEffect)(() => {
|
||||
const { current } = mountState;
|
||||
current.isMounted = true;
|
||||
return () => {
|
||||
current.isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
const resolvedSrc = (0, resolve_image_source_1.resolveAnimatedImageSource)(src);
|
||||
const [imageDecoder, setImageDecoder] = (0, react_1.useState)(null);
|
||||
const { delayRender, continueRender } = (0, use_delay_render_js_1.useDelayRender)();
|
||||
const [decodeHandle] = (0, react_1.useState)(() => delayRender(`Rendering <AnimatedImage/> with src="${resolvedSrc}"`));
|
||||
const frame = (0, use_current_frame_js_1.useCurrentFrame)();
|
||||
const { fps } = (0, use_video_config_js_1.useVideoConfig)();
|
||||
const currentTime = frame / playbackRate / fps;
|
||||
const currentTimeRef = (0, react_1.useRef)(currentTime);
|
||||
currentTimeRef.current = currentTime;
|
||||
const ref = (0, react_1.useRef)(null);
|
||||
(0, react_1.useImperativeHandle)(canvasRef, () => {
|
||||
var _a;
|
||||
const c = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.getCanvas();
|
||||
if (!c) {
|
||||
throw new Error('Canvas ref is not set');
|
||||
}
|
||||
return c;
|
||||
}, []);
|
||||
const [initialLoopBehavior] = (0, react_1.useState)(() => loopBehavior);
|
||||
(0, react_1.useEffect)(() => {
|
||||
const controller = new AbortController();
|
||||
(0, decode_image_js_1.decodeImage)({
|
||||
resolvedSrc,
|
||||
signal: controller.signal,
|
||||
currentTime: currentTimeRef.current,
|
||||
initialLoopBehavior,
|
||||
})
|
||||
.then((d) => {
|
||||
setImageDecoder(d);
|
||||
continueRender(decodeHandle);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.name === 'AbortError') {
|
||||
continueRender(decodeHandle);
|
||||
return;
|
||||
}
|
||||
if (onError) {
|
||||
onError === null || onError === void 0 ? void 0 : onError(err);
|
||||
continueRender(decodeHandle);
|
||||
}
|
||||
else {
|
||||
(0, cancel_render_js_1.cancelRender)(err);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
controller.abort();
|
||||
};
|
||||
}, [
|
||||
resolvedSrc,
|
||||
decodeHandle,
|
||||
onError,
|
||||
initialLoopBehavior,
|
||||
continueRender,
|
||||
]);
|
||||
(0, react_1.useLayoutEffect)(() => {
|
||||
if (!imageDecoder) {
|
||||
return;
|
||||
}
|
||||
const delay = delayRender(`Rendering frame at ${currentTime} of <AnimatedImage src="${src}"/>`);
|
||||
imageDecoder
|
||||
.getFrame(currentTime, loopBehavior)
|
||||
.then((videoFrame) => {
|
||||
var _a, _b;
|
||||
if (mountState.current.isMounted) {
|
||||
if (videoFrame === null) {
|
||||
(_a = ref.current) === null || _a === void 0 ? void 0 : _a.clear();
|
||||
}
|
||||
else {
|
||||
(_b = ref.current) === null || _b === void 0 ? void 0 : _b.draw(videoFrame.frame);
|
||||
}
|
||||
}
|
||||
continueRender(delay);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (onError) {
|
||||
onError(err);
|
||||
continueRender(delay);
|
||||
}
|
||||
else {
|
||||
(0, cancel_render_js_1.cancelRender)(err);
|
||||
}
|
||||
});
|
||||
}, [
|
||||
currentTime,
|
||||
imageDecoder,
|
||||
loopBehavior,
|
||||
onError,
|
||||
src,
|
||||
continueRender,
|
||||
delayRender,
|
||||
]);
|
||||
return ((0, jsx_runtime_1.jsx)(canvas_1.Canvas, { ref: ref, width: width, height: height, fit: fit, ...props }));
|
||||
});
|
||||
Generated
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import type { AnimatedImageFillMode } from './props';
|
||||
type Props = {
|
||||
readonly width?: number;
|
||||
readonly height?: number;
|
||||
readonly fit: AnimatedImageFillMode;
|
||||
readonly className?: string;
|
||||
readonly style?: React.CSSProperties;
|
||||
};
|
||||
export type AnimatedImageCanvasRef = {
|
||||
readonly draw: (imageData: VideoFrame) => void;
|
||||
readonly getCanvas: () => HTMLCanvasElement | null;
|
||||
clear: () => void;
|
||||
};
|
||||
export declare const Canvas: React.ForwardRefExoticComponent<Props & React.RefAttributes<AnimatedImageCanvasRef>>;
|
||||
export {};
|
||||
Generated
Vendored
+132
@@ -0,0 +1,132 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Canvas = void 0;
|
||||
const jsx_runtime_1 = require("react/jsx-runtime");
|
||||
const react_1 = __importStar(require("react"));
|
||||
const calcArgs = (fit, frameSize, canvasSize) => {
|
||||
switch (fit) {
|
||||
case 'fill': {
|
||||
return [
|
||||
0,
|
||||
0,
|
||||
frameSize.width,
|
||||
frameSize.height,
|
||||
0,
|
||||
0,
|
||||
canvasSize.width,
|
||||
canvasSize.height,
|
||||
];
|
||||
}
|
||||
case 'contain': {
|
||||
const ratio = Math.min(canvasSize.width / frameSize.width, canvasSize.height / frameSize.height);
|
||||
const centerX = (canvasSize.width - frameSize.width * ratio) / 2;
|
||||
const centerY = (canvasSize.height - frameSize.height * ratio) / 2;
|
||||
return [
|
||||
0,
|
||||
0,
|
||||
frameSize.width,
|
||||
frameSize.height,
|
||||
centerX,
|
||||
centerY,
|
||||
frameSize.width * ratio,
|
||||
frameSize.height * ratio,
|
||||
];
|
||||
}
|
||||
case 'cover': {
|
||||
const ratio = Math.max(canvasSize.width / frameSize.width, canvasSize.height / frameSize.height);
|
||||
const centerX = (canvasSize.width - frameSize.width * ratio) / 2;
|
||||
const centerY = (canvasSize.height - frameSize.height * ratio) / 2;
|
||||
return [
|
||||
0,
|
||||
0,
|
||||
frameSize.width,
|
||||
frameSize.height,
|
||||
centerX,
|
||||
centerY,
|
||||
frameSize.width * ratio,
|
||||
frameSize.height * ratio,
|
||||
];
|
||||
}
|
||||
default:
|
||||
throw new Error('Unknown fit: ' + fit);
|
||||
}
|
||||
};
|
||||
const CanvasRefForwardingFunction = ({ width, height, fit, className, style }, ref) => {
|
||||
const canvasRef = (0, react_1.useRef)(null);
|
||||
const draw = (0, react_1.useCallback)((imageData) => {
|
||||
var _a;
|
||||
const canvas = canvasRef.current;
|
||||
const canvasWidth = width !== null && width !== void 0 ? width : imageData.displayWidth;
|
||||
const canvasHeight = height !== null && height !== void 0 ? height : imageData.displayHeight;
|
||||
if (!canvas) {
|
||||
throw new Error('Canvas ref is not set');
|
||||
}
|
||||
const ctx = (_a = canvasRef.current) === null || _a === void 0 ? void 0 : _a.getContext('2d');
|
||||
if (!ctx) {
|
||||
throw new Error('Could not get 2d context');
|
||||
}
|
||||
canvas.width = canvasWidth;
|
||||
canvas.height = canvasHeight;
|
||||
ctx.drawImage(imageData, ...calcArgs(fit, {
|
||||
height: imageData.displayHeight,
|
||||
width: imageData.displayWidth,
|
||||
}, {
|
||||
width: canvasWidth,
|
||||
height: canvasHeight,
|
||||
}));
|
||||
}, [fit, height, width]);
|
||||
(0, react_1.useImperativeHandle)(ref, () => {
|
||||
return {
|
||||
draw,
|
||||
getCanvas: () => {
|
||||
if (!canvasRef.current) {
|
||||
throw new Error('Canvas ref is not set');
|
||||
}
|
||||
return canvasRef.current;
|
||||
},
|
||||
clear: () => {
|
||||
var _a;
|
||||
const ctx = (_a = canvasRef.current) === null || _a === void 0 ? void 0 : _a.getContext('2d');
|
||||
if (!ctx) {
|
||||
throw new Error('Could not get 2d context');
|
||||
}
|
||||
ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
|
||||
},
|
||||
};
|
||||
}, [draw]);
|
||||
return (0, jsx_runtime_1.jsx)("canvas", { ref: canvasRef, className: className, style: style });
|
||||
};
|
||||
exports.Canvas = react_1.default.forwardRef(CanvasRefForwardingFunction);
|
||||
Generated
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
import type { RemotionAnimatedImageLoopBehavior } from './props';
|
||||
export type AnimatedImageCacheItem = {
|
||||
timeInSeconds: number;
|
||||
frameIndex: number;
|
||||
frame: VideoFrame | null;
|
||||
};
|
||||
export type RemotionImageDecoder = {
|
||||
getFrame: (i: number, loopBehavior: RemotionAnimatedImageLoopBehavior) => Promise<AnimatedImageCacheItem | null>;
|
||||
frameCount: number;
|
||||
};
|
||||
export declare const decodeImage: ({ resolvedSrc, signal, currentTime, initialLoopBehavior, }: {
|
||||
resolvedSrc: string;
|
||||
signal: AbortSignal;
|
||||
currentTime: number;
|
||||
initialLoopBehavior: RemotionAnimatedImageLoopBehavior;
|
||||
}) => Promise<RemotionImageDecoder>;
|
||||
Generated
Vendored
+145
@@ -0,0 +1,145 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.decodeImage = void 0;
|
||||
const CACHE_SIZE = 5;
|
||||
const getActualTime = ({ loopBehavior, durationFound, timeInSec, }) => {
|
||||
return loopBehavior === 'loop'
|
||||
? durationFound
|
||||
? timeInSec % durationFound
|
||||
: timeInSec
|
||||
: Math.min(timeInSec, durationFound || Infinity);
|
||||
};
|
||||
const decodeImage = async ({ resolvedSrc, signal, currentTime, initialLoopBehavior, }) => {
|
||||
if (typeof ImageDecoder === 'undefined') {
|
||||
throw new Error('Your browser does not support the WebCodecs ImageDecoder API.');
|
||||
}
|
||||
const res = await fetch(resolvedSrc, { signal });
|
||||
const { body } = res;
|
||||
if (!body) {
|
||||
throw new Error('Got no body');
|
||||
}
|
||||
const decoder = new ImageDecoder({
|
||||
data: body,
|
||||
type: res.headers.get('Content-Type') || 'image/gif',
|
||||
});
|
||||
await decoder.completed;
|
||||
const { selectedTrack } = decoder.tracks;
|
||||
if (!selectedTrack) {
|
||||
throw new Error('No selected track');
|
||||
}
|
||||
const cache = [];
|
||||
let durationFound = null;
|
||||
const getFrameByIndex = async (frameIndex) => {
|
||||
const foundInCache = cache.find((c) => c.frameIndex === frameIndex);
|
||||
if (foundInCache && foundInCache.frame) {
|
||||
return foundInCache;
|
||||
}
|
||||
const frame = await decoder.decode({
|
||||
frameIndex,
|
||||
completeFramesOnly: true,
|
||||
});
|
||||
if (foundInCache) {
|
||||
foundInCache.frame = frame.image;
|
||||
}
|
||||
else {
|
||||
cache.push({
|
||||
frame: frame.image,
|
||||
frameIndex,
|
||||
timeInSeconds: frame.image.timestamp / 1000000,
|
||||
});
|
||||
}
|
||||
return {
|
||||
frame: frame.image,
|
||||
frameIndex,
|
||||
timeInSeconds: frame.image.timestamp / 1000000,
|
||||
};
|
||||
};
|
||||
const clearCache = (closeToTimeInSec) => {
|
||||
const itemsInCache = cache.filter((c) => c.frame);
|
||||
const sortByClosestToCurrentTime = itemsInCache.sort((a, b) => {
|
||||
const aDiff = Math.abs(a.timeInSeconds - closeToTimeInSec);
|
||||
const bDiff = Math.abs(b.timeInSeconds - closeToTimeInSec);
|
||||
return aDiff - bDiff;
|
||||
});
|
||||
for (let i = 0; i < sortByClosestToCurrentTime.length; i++) {
|
||||
if (i < CACHE_SIZE) {
|
||||
continue;
|
||||
}
|
||||
const item = sortByClosestToCurrentTime[i];
|
||||
item.frame = null;
|
||||
}
|
||||
};
|
||||
const ensureFrameBeforeAndAfter = async ({ timeInSec, loopBehavior, }) => {
|
||||
const actualTimeInSec = getActualTime({
|
||||
durationFound,
|
||||
loopBehavior,
|
||||
timeInSec,
|
||||
});
|
||||
const framesBefore = cache.filter((c) => c.timeInSeconds <= actualTimeInSec);
|
||||
const biggestIndex = framesBefore
|
||||
.map((c) => c.frameIndex)
|
||||
.reduce((a, b) => Math.max(a, b), 0);
|
||||
let i = biggestIndex;
|
||||
while (true) {
|
||||
const f = await getFrameByIndex(i);
|
||||
i++;
|
||||
if (!f.frame) {
|
||||
throw new Error('No frame found');
|
||||
}
|
||||
if (!f.frame.duration) {
|
||||
// non-animated, or AVIF in firefox
|
||||
break;
|
||||
}
|
||||
if (i === selectedTrack.frameCount && durationFound === null) {
|
||||
const duration = (f.frame.timestamp + f.frame.duration) / 1000000;
|
||||
durationFound = duration;
|
||||
}
|
||||
if (f.timeInSeconds > actualTimeInSec || i === selectedTrack.frameCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If close to end, also cache first frame for smooth wrap around
|
||||
if (selectedTrack.frameCount - biggestIndex < 3 &&
|
||||
loopBehavior === 'loop') {
|
||||
await getFrameByIndex(0);
|
||||
}
|
||||
clearCache(actualTimeInSec);
|
||||
};
|
||||
// Twice because might be over total duration
|
||||
await ensureFrameBeforeAndAfter({
|
||||
timeInSec: currentTime,
|
||||
loopBehavior: initialLoopBehavior,
|
||||
});
|
||||
await ensureFrameBeforeAndAfter({
|
||||
timeInSec: currentTime,
|
||||
loopBehavior: initialLoopBehavior,
|
||||
});
|
||||
const getFrame = async (timeInSec, loopBehavior) => {
|
||||
if (durationFound !== null &&
|
||||
timeInSec > durationFound &&
|
||||
loopBehavior === 'clear-after-finish') {
|
||||
return null;
|
||||
}
|
||||
const actualTimeInSec = getActualTime({
|
||||
loopBehavior,
|
||||
durationFound,
|
||||
timeInSec,
|
||||
});
|
||||
await ensureFrameBeforeAndAfter({ timeInSec: actualTimeInSec, loopBehavior });
|
||||
const itemsInCache = cache.filter((c) => c.frame);
|
||||
const closest = itemsInCache.reduce((a, b) => {
|
||||
const aDiff = Math.abs(a.timeInSeconds - actualTimeInSec);
|
||||
const bDiff = Math.abs(b.timeInSeconds - actualTimeInSec);
|
||||
return aDiff < bDiff ? a : b;
|
||||
});
|
||||
if (!closest.frame) {
|
||||
throw new Error('No frame found');
|
||||
}
|
||||
return closest;
|
||||
};
|
||||
return {
|
||||
getFrame,
|
||||
frameCount: selectedTrack.frameCount,
|
||||
};
|
||||
};
|
||||
exports.decodeImage = decodeImage;
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export { AnimatedImage } from './AnimatedImage';
|
||||
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AnimatedImage = void 0;
|
||||
var AnimatedImage_1 = require("./AnimatedImage");
|
||||
Object.defineProperty(exports, "AnimatedImage", { enumerable: true, get: function () { return AnimatedImage_1.AnimatedImage; } });
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
export type RemotionAnimatedImageLoopBehavior = 'loop' | 'pause-after-finish' | 'clear-after-finish';
|
||||
export type RemotionAnimatedImageProps = {
|
||||
src: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
onError?: (error: Error) => void;
|
||||
fit?: AnimatedImageFillMode;
|
||||
playbackRate?: number;
|
||||
style?: React.CSSProperties;
|
||||
loopBehavior?: RemotionAnimatedImageLoopBehavior;
|
||||
id?: string;
|
||||
className?: string;
|
||||
};
|
||||
export type AnimatedImageFillMode = 'contain' | 'cover' | 'fill';
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
skills/remotion-prompt-video/node_modules/remotion/dist/cjs/animated-image/resolve-image-source.d.ts
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export declare const resolveAnimatedImageSource: (src: string) => string;
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.resolveAnimatedImageSource = void 0;
|
||||
const resolveAnimatedImageSource = (src) => {
|
||||
if (typeof window === 'undefined') {
|
||||
return src;
|
||||
}
|
||||
return new URL(src, window.origin).href;
|
||||
};
|
||||
exports.resolveAnimatedImageSource = resolveAnimatedImageSource;
|
||||
Reference in New Issue
Block a user