3649 lines
110 KiB
JavaScript
3649 lines
110 KiB
JavaScript
"use client";
|
||
// src/icons.tsx
|
||
import { jsx, jsxs } from "react/jsx-runtime";
|
||
var ICON_SIZE = 25;
|
||
var fullscreenIconSize = 16;
|
||
var PlayIcon = () => {
|
||
return /* @__PURE__ */ jsx("svg", {
|
||
width: ICON_SIZE,
|
||
height: ICON_SIZE,
|
||
viewBox: "0 0 25 25",
|
||
fill: "none",
|
||
children: /* @__PURE__ */ jsx("path", {
|
||
d: "M8 6.375C7.40904 8.17576 7.06921 10.2486 7.01438 12.3871C6.95955 14.5255 7.19163 16.6547 7.6875 18.5625C9.95364 18.2995 12.116 17.6164 14.009 16.5655C15.902 15.5147 17.4755 14.124 18.6088 12.5C17.5158 10.8949 15.9949 9.51103 14.1585 8.45082C12.3222 7.3906 10.2174 6.68116 8 6.375Z",
|
||
fill: "white",
|
||
stroke: "white",
|
||
strokeWidth: "6.25",
|
||
strokeLinejoin: "round"
|
||
})
|
||
});
|
||
};
|
||
var PauseIcon = () => {
|
||
return /* @__PURE__ */ jsxs("svg", {
|
||
viewBox: "0 0 100 100",
|
||
width: ICON_SIZE,
|
||
height: ICON_SIZE,
|
||
children: [
|
||
/* @__PURE__ */ jsx("rect", {
|
||
x: "25",
|
||
y: "20",
|
||
width: "20",
|
||
height: "60",
|
||
fill: "#fff",
|
||
ry: "5",
|
||
rx: "5"
|
||
}),
|
||
/* @__PURE__ */ jsx("rect", {
|
||
x: "55",
|
||
y: "20",
|
||
width: "20",
|
||
height: "60",
|
||
fill: "#fff",
|
||
ry: "5",
|
||
rx: "5"
|
||
})
|
||
]
|
||
});
|
||
};
|
||
var FullscreenIcon = ({
|
||
isFullscreen
|
||
}) => {
|
||
const strokeWidth = 6;
|
||
const viewSize = 32;
|
||
const out = isFullscreen ? 0 : strokeWidth / 2;
|
||
const middleInset = isFullscreen ? strokeWidth * 1.6 : strokeWidth / 2;
|
||
const inset = isFullscreen ? strokeWidth * 1.6 : strokeWidth * 2;
|
||
return /* @__PURE__ */ jsxs("svg", {
|
||
viewBox: `0 0 ${viewSize} ${viewSize}`,
|
||
height: fullscreenIconSize,
|
||
width: fullscreenIconSize,
|
||
children: [
|
||
/* @__PURE__ */ jsx("path", {
|
||
d: `
|
||
M ${out} ${inset}
|
||
L ${middleInset} ${middleInset}
|
||
L ${inset} ${out}
|
||
`,
|
||
stroke: "#fff",
|
||
strokeWidth,
|
||
fill: "none"
|
||
}),
|
||
/* @__PURE__ */ jsx("path", {
|
||
d: `
|
||
M ${viewSize - out} ${inset}
|
||
L ${viewSize - middleInset} ${middleInset}
|
||
L ${viewSize - inset} ${out}
|
||
`,
|
||
stroke: "#fff",
|
||
strokeWidth,
|
||
fill: "none"
|
||
}),
|
||
/* @__PURE__ */ jsx("path", {
|
||
d: `
|
||
M ${out} ${viewSize - inset}
|
||
L ${middleInset} ${viewSize - middleInset}
|
||
L ${inset} ${viewSize - out}
|
||
`,
|
||
stroke: "#fff",
|
||
strokeWidth,
|
||
fill: "none"
|
||
}),
|
||
/* @__PURE__ */ jsx("path", {
|
||
d: `
|
||
M ${viewSize - out} ${viewSize - inset}
|
||
L ${viewSize - middleInset} ${viewSize - middleInset}
|
||
L ${viewSize - inset} ${viewSize - out}
|
||
`,
|
||
stroke: "#fff",
|
||
strokeWidth,
|
||
fill: "none"
|
||
})
|
||
]
|
||
});
|
||
};
|
||
var VolumeOffIcon = () => {
|
||
return /* @__PURE__ */ jsx("svg", {
|
||
width: ICON_SIZE,
|
||
height: ICON_SIZE,
|
||
viewBox: "0 0 24 24",
|
||
children: /* @__PURE__ */ jsx("path", {
|
||
d: "M3.63 3.63a.996.996 0 000 1.41L7.29 8.7 7 9H4c-.55 0-1 .45-1 1v4c0 .55.45 1 1 1h3l3.29 3.29c.63.63 1.71.18 1.71-.71v-4.17l4.18 4.18c-.49.37-1.02.68-1.6.91-.36.15-.58.53-.58.92 0 .72.73 1.18 1.39.91.8-.33 1.55-.77 2.22-1.31l1.34 1.34a.996.996 0 101.41-1.41L5.05 3.63c-.39-.39-1.02-.39-1.42 0zM19 12c0 .82-.15 1.61-.41 2.34l1.53 1.53c.56-1.17.88-2.48.88-3.87 0-3.83-2.4-7.11-5.78-8.4-.59-.23-1.22.23-1.22.86v.19c0 .38.25.71.61.85C17.18 6.54 19 9.06 19 12zm-8.71-6.29l-.17.17L12 7.76V6.41c0-.89-1.08-1.33-1.71-.7zM16.5 12A4.5 4.5 0 0014 7.97v1.79l2.48 2.48c.01-.08.02-.16.02-.24z",
|
||
fill: "#fff"
|
||
})
|
||
});
|
||
};
|
||
var VolumeOnIcon = () => {
|
||
return /* @__PURE__ */ jsx("svg", {
|
||
width: ICON_SIZE,
|
||
height: ICON_SIZE,
|
||
viewBox: "0 0 24 24",
|
||
children: /* @__PURE__ */ jsx("path", {
|
||
d: "M3 10v4c0 .55.45 1 1 1h3l3.29 3.29c.63.63 1.71.18 1.71-.71V6.41c0-.89-1.08-1.34-1.71-.71L7 9H4c-.55 0-1 .45-1 1zm13.5 2A4.5 4.5 0 0014 7.97v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 4.45v.2c0 .38.25.71.6.85C17.18 6.53 19 9.06 19 12s-1.82 5.47-4.4 6.5c-.36.14-.6.47-.6.85v.2c0 .63.63 1.07 1.21.85C18.6 19.11 21 15.84 21 12s-2.4-7.11-5.79-8.4c-.58-.23-1.21.22-1.21.85z",
|
||
fill: "#fff"
|
||
})
|
||
});
|
||
};
|
||
|
||
// src/BufferingIndicator.tsx
|
||
import { jsx as jsx2, jsxs as jsxs2, Fragment } from "react/jsx-runtime";
|
||
var className = "__remotion_buffering_indicator";
|
||
var remotionBufferingAnimation = "__remotion_buffering_animation";
|
||
var playerStyle = {
|
||
width: ICON_SIZE,
|
||
height: ICON_SIZE,
|
||
overflow: "hidden",
|
||
lineHeight: "normal",
|
||
fontSize: "inherit"
|
||
};
|
||
var studioStyle = {
|
||
width: 14,
|
||
height: 14,
|
||
overflow: "hidden",
|
||
lineHeight: "normal",
|
||
fontSize: "inherit"
|
||
};
|
||
var BufferingIndicator = ({ type }) => {
|
||
const style = type === "player" ? playerStyle : studioStyle;
|
||
return /* @__PURE__ */ jsxs2(Fragment, {
|
||
children: [
|
||
/* @__PURE__ */ jsx2("style", {
|
||
type: "text/css",
|
||
children: `
|
||
@keyframes ${remotionBufferingAnimation} {
|
||
0% {
|
||
rotate: 0deg;
|
||
}
|
||
100% {
|
||
rotate: 360deg;
|
||
}
|
||
}
|
||
|
||
.${className} {
|
||
animation: ${remotionBufferingAnimation} 1s linear infinite;
|
||
}
|
||
`
|
||
}),
|
||
/* @__PURE__ */ jsx2("div", {
|
||
style,
|
||
children: /* @__PURE__ */ jsx2("svg", {
|
||
viewBox: type === "player" ? "0 0 22 22" : "0 0 18 18",
|
||
style,
|
||
className,
|
||
children: /* @__PURE__ */ jsx2("path", {
|
||
d: type === "player" ? "M 11 4 A 7 7 0 0 1 15.1145 16.66312" : "M 9 2 A 7 7 0 0 1 13.1145 14.66312",
|
||
stroke: "white",
|
||
strokeLinecap: "round",
|
||
fill: "none",
|
||
strokeWidth: 3
|
||
})
|
||
})
|
||
})
|
||
]
|
||
});
|
||
};
|
||
|
||
// src/calculate-scale.ts
|
||
import { Internals } from "remotion";
|
||
|
||
// src/utils/calculate-player-size.ts
|
||
var calculatePlayerSize = ({
|
||
currentSize,
|
||
width,
|
||
height,
|
||
compositionWidth,
|
||
compositionHeight
|
||
}) => {
|
||
if (width !== undefined && height === undefined) {
|
||
return {
|
||
aspectRatio: [compositionWidth, compositionHeight].join("/")
|
||
};
|
||
}
|
||
if (height !== undefined && width === undefined) {
|
||
return {
|
||
aspectRatio: [compositionWidth, compositionHeight].join("/")
|
||
};
|
||
}
|
||
if (!currentSize) {
|
||
return {
|
||
width: compositionWidth,
|
||
height: compositionHeight
|
||
};
|
||
}
|
||
return {
|
||
width: compositionWidth,
|
||
height: compositionHeight
|
||
};
|
||
};
|
||
|
||
// src/calculate-scale.ts
|
||
var calculateCanvasTransformation = ({
|
||
previewSize,
|
||
compositionWidth,
|
||
compositionHeight,
|
||
canvasSize
|
||
}) => {
|
||
const scale = Internals.calculateScale({
|
||
canvasSize,
|
||
compositionHeight,
|
||
compositionWidth,
|
||
previewSize
|
||
});
|
||
const correction = 0 - (1 - scale) / 2;
|
||
const xCorrection = correction * compositionWidth;
|
||
const yCorrection = correction * compositionHeight;
|
||
const width = compositionWidth * scale;
|
||
const height = compositionHeight * scale;
|
||
const centerX = canvasSize.width / 2 - width / 2;
|
||
const centerY = canvasSize.height / 2 - height / 2;
|
||
return {
|
||
centerX,
|
||
centerY,
|
||
xCorrection,
|
||
yCorrection,
|
||
scale
|
||
};
|
||
};
|
||
var calculateOuterStyle = ({
|
||
config,
|
||
style,
|
||
canvasSize,
|
||
overflowVisible,
|
||
layout
|
||
}) => {
|
||
if (!config) {
|
||
return {};
|
||
}
|
||
return {
|
||
position: "relative",
|
||
overflow: overflowVisible ? "visible" : "hidden",
|
||
...calculatePlayerSize({
|
||
compositionHeight: config.height,
|
||
compositionWidth: config.width,
|
||
currentSize: canvasSize,
|
||
height: style?.height,
|
||
width: style?.width
|
||
}),
|
||
opacity: layout ? 1 : 0,
|
||
...style
|
||
};
|
||
};
|
||
var calculateContainerStyle = ({
|
||
config,
|
||
layout,
|
||
scale,
|
||
overflowVisible
|
||
}) => {
|
||
if (!config) {
|
||
return {};
|
||
}
|
||
if (!layout) {
|
||
return {
|
||
position: "absolute",
|
||
width: config.width,
|
||
height: config.height,
|
||
display: "flex",
|
||
transform: `scale(${scale})`,
|
||
overflow: overflowVisible ? "visible" : "hidden"
|
||
};
|
||
}
|
||
return {
|
||
position: "absolute",
|
||
width: config.width,
|
||
height: config.height,
|
||
display: "flex",
|
||
transform: `scale(${scale})`,
|
||
marginLeft: layout.xCorrection,
|
||
marginTop: layout.yCorrection,
|
||
overflow: overflowVisible ? "visible" : "hidden"
|
||
};
|
||
};
|
||
var calculateOuter = ({
|
||
layout,
|
||
scale,
|
||
config,
|
||
overflowVisible
|
||
}) => {
|
||
if (!config) {
|
||
return {};
|
||
}
|
||
if (!layout) {
|
||
return {
|
||
width: config.width * scale,
|
||
height: config.height * scale,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
position: "absolute",
|
||
overflow: overflowVisible ? "visible" : "hidden"
|
||
};
|
||
}
|
||
const { centerX, centerY } = layout;
|
||
return {
|
||
width: config.width * scale,
|
||
height: config.height * scale,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
position: "absolute",
|
||
left: centerX,
|
||
top: centerY,
|
||
overflow: overflowVisible ? "visible" : "hidden"
|
||
};
|
||
};
|
||
|
||
// src/emitter-context.ts
|
||
import React from "react";
|
||
var PlayerEventEmitterContext = React.createContext(undefined);
|
||
var ThumbnailEmitterContext = React.createContext(undefined);
|
||
|
||
// src/EmitterProvider.tsx
|
||
import { useContext as useContext2, useEffect, useState } from "react";
|
||
import { Internals as Internals3 } from "remotion";
|
||
|
||
// src/event-emitter.ts
|
||
class PlayerEmitter {
|
||
listeners = {
|
||
ended: [],
|
||
error: [],
|
||
pause: [],
|
||
play: [],
|
||
ratechange: [],
|
||
scalechange: [],
|
||
seeked: [],
|
||
timeupdate: [],
|
||
frameupdate: [],
|
||
fullscreenchange: [],
|
||
volumechange: [],
|
||
mutechange: [],
|
||
waiting: [],
|
||
resume: []
|
||
};
|
||
addEventListener(name, callback) {
|
||
this.listeners[name].push(callback);
|
||
}
|
||
removeEventListener(name, callback) {
|
||
this.listeners[name] = this.listeners[name].filter((l) => l !== callback);
|
||
}
|
||
dispatchEvent(dispatchName, context) {
|
||
this.listeners[dispatchName].forEach((callback) => {
|
||
callback({ detail: context });
|
||
});
|
||
}
|
||
dispatchSeek = (frame) => {
|
||
this.dispatchEvent("seeked", {
|
||
frame
|
||
});
|
||
};
|
||
dispatchVolumeChange = (volume) => {
|
||
this.dispatchEvent("volumechange", {
|
||
volume
|
||
});
|
||
};
|
||
dispatchPause = () => {
|
||
this.dispatchEvent("pause", undefined);
|
||
};
|
||
dispatchPlay = () => {
|
||
this.dispatchEvent("play", undefined);
|
||
};
|
||
dispatchEnded = () => {
|
||
this.dispatchEvent("ended", undefined);
|
||
};
|
||
dispatchRateChange = (playbackRate) => {
|
||
this.dispatchEvent("ratechange", {
|
||
playbackRate
|
||
});
|
||
};
|
||
dispatchScaleChange = (scale) => {
|
||
this.dispatchEvent("scalechange", {
|
||
scale
|
||
});
|
||
};
|
||
dispatchError = (error) => {
|
||
this.dispatchEvent("error", {
|
||
error
|
||
});
|
||
};
|
||
dispatchTimeUpdate = (event) => {
|
||
this.dispatchEvent("timeupdate", event);
|
||
};
|
||
dispatchFrameUpdate = (event) => {
|
||
this.dispatchEvent("frameupdate", event);
|
||
};
|
||
dispatchFullscreenChange = (event) => {
|
||
this.dispatchEvent("fullscreenchange", event);
|
||
};
|
||
dispatchMuteChange = (event) => {
|
||
this.dispatchEvent("mutechange", event);
|
||
};
|
||
dispatchWaiting = (event) => {
|
||
this.dispatchEvent("waiting", event);
|
||
};
|
||
dispatchResume = (event) => {
|
||
this.dispatchEvent("resume", event);
|
||
};
|
||
}
|
||
|
||
class ThumbnailEmitter {
|
||
listeners = {
|
||
error: [],
|
||
waiting: [],
|
||
resume: []
|
||
};
|
||
addEventListener(name, callback) {
|
||
this.listeners[name].push(callback);
|
||
}
|
||
removeEventListener(name, callback) {
|
||
this.listeners[name] = this.listeners[name].filter((l) => l !== callback);
|
||
}
|
||
dispatchEvent(dispatchName, context) {
|
||
this.listeners[dispatchName].forEach((callback) => {
|
||
callback({ detail: context });
|
||
});
|
||
}
|
||
dispatchError = (error) => {
|
||
this.dispatchEvent("error", {
|
||
error
|
||
});
|
||
};
|
||
dispatchWaiting = (event) => {
|
||
this.dispatchEvent("waiting", event);
|
||
};
|
||
dispatchResume = (event) => {
|
||
this.dispatchEvent("resume", event);
|
||
};
|
||
}
|
||
|
||
// src/use-buffer-state-emitter.ts
|
||
import { useContext, useLayoutEffect } from "react";
|
||
import { Internals as Internals2 } from "remotion";
|
||
var useBufferStateEmitter = (emitter) => {
|
||
const bufferManager = useContext(Internals2.BufferingContextReact);
|
||
if (!bufferManager) {
|
||
throw new Error("BufferingContextReact not found");
|
||
}
|
||
useLayoutEffect(() => {
|
||
const clear1 = bufferManager.listenForBuffering(() => {
|
||
bufferManager.buffering.current = true;
|
||
emitter.dispatchWaiting({});
|
||
});
|
||
const clear2 = bufferManager.listenForResume(() => {
|
||
bufferManager.buffering.current = false;
|
||
emitter.dispatchResume({});
|
||
});
|
||
return () => {
|
||
clear1.remove();
|
||
clear2.remove();
|
||
};
|
||
}, [bufferManager, emitter]);
|
||
};
|
||
|
||
// src/EmitterProvider.tsx
|
||
import { jsx as jsx3 } from "react/jsx-runtime";
|
||
var PlayerEmitterProvider = ({ children, currentPlaybackRate }) => {
|
||
const [emitter] = useState(() => new PlayerEmitter);
|
||
const bufferManager = useContext2(Internals3.BufferingContextReact);
|
||
if (!bufferManager) {
|
||
throw new Error("BufferingContextReact not found");
|
||
}
|
||
useEffect(() => {
|
||
if (currentPlaybackRate) {
|
||
emitter.dispatchRateChange(currentPlaybackRate);
|
||
}
|
||
}, [emitter, currentPlaybackRate]);
|
||
useBufferStateEmitter(emitter);
|
||
return /* @__PURE__ */ jsx3(PlayerEventEmitterContext.Provider, {
|
||
value: emitter,
|
||
children
|
||
});
|
||
};
|
||
|
||
// src/use-frame-imperative.ts
|
||
import { useCallback, useRef } from "react";
|
||
import { Internals as Internals4 } from "remotion";
|
||
var useFrameImperative = () => {
|
||
const frame = Internals4.Timeline.useTimelinePosition();
|
||
const frameRef = useRef(frame);
|
||
frameRef.current = frame;
|
||
const getCurrentFrame = useCallback(() => {
|
||
return frameRef.current;
|
||
}, []);
|
||
return getCurrentFrame;
|
||
};
|
||
|
||
// src/use-hover-state.ts
|
||
import { useEffect as useEffect2, useState as useState2 } from "react";
|
||
var useHoverState = (ref, hideControlsWhenPointerDoesntMove) => {
|
||
const [hovered, setHovered] = useState2(false);
|
||
useEffect2(() => {
|
||
const { current } = ref;
|
||
if (!current) {
|
||
return;
|
||
}
|
||
let hoverTimeout;
|
||
const addHoverTimeout = () => {
|
||
if (hideControlsWhenPointerDoesntMove) {
|
||
clearTimeout(hoverTimeout);
|
||
hoverTimeout = setTimeout(() => {
|
||
setHovered(false);
|
||
}, hideControlsWhenPointerDoesntMove === true ? 3000 : hideControlsWhenPointerDoesntMove);
|
||
}
|
||
};
|
||
const onHover = () => {
|
||
setHovered(true);
|
||
addHoverTimeout();
|
||
};
|
||
const onLeave = () => {
|
||
setHovered(false);
|
||
clearTimeout(hoverTimeout);
|
||
};
|
||
const onMove = () => {
|
||
setHovered(true);
|
||
addHoverTimeout();
|
||
};
|
||
current.addEventListener("mouseenter", onHover);
|
||
current.addEventListener("mouseleave", onLeave);
|
||
current.addEventListener("mousemove", onMove);
|
||
return () => {
|
||
current.removeEventListener("mouseenter", onHover);
|
||
current.removeEventListener("mouseleave", onLeave);
|
||
current.removeEventListener("mousemove", onMove);
|
||
clearTimeout(hoverTimeout);
|
||
};
|
||
}, [hideControlsWhenPointerDoesntMove, ref]);
|
||
return hovered;
|
||
};
|
||
|
||
// src/use-playback.ts
|
||
import { useContext as useContext4, useEffect as useEffect5, useRef as useRef4 } from "react";
|
||
import { Internals as Internals6 } from "remotion";
|
||
|
||
// src/browser-mediasession.ts
|
||
import { useEffect as useEffect3 } from "react";
|
||
|
||
// src/use-player.ts
|
||
import { useCallback as useCallback2, useContext as useContext3, useMemo, useRef as useRef2, useState as useState3 } from "react";
|
||
import { Internals as Internals5 } from "remotion";
|
||
var usePlayer = () => {
|
||
const [playing, setPlaying, imperativePlaying] = Internals5.Timeline.usePlayingState();
|
||
const [hasPlayed, setHasPlayed] = useState3(false);
|
||
const frame = Internals5.Timeline.useTimelinePosition();
|
||
const playStart = useRef2(frame);
|
||
const setFrame = Internals5.Timeline.useTimelineSetFrame();
|
||
const setTimelinePosition = Internals5.Timeline.useTimelineSetFrame();
|
||
const audioContext = useContext3(Internals5.SharedAudioContext);
|
||
const { audioAndVideoTags } = useContext3(Internals5.TimelineContext);
|
||
const frameRef = useRef2(frame);
|
||
frameRef.current = frame;
|
||
const video = Internals5.useVideo();
|
||
const config = Internals5.useUnsafeVideoConfig();
|
||
const emitter = useContext3(PlayerEventEmitterContext);
|
||
const lastFrame = (config?.durationInFrames ?? 1) - 1;
|
||
const isLastFrame = frame === lastFrame;
|
||
const isFirstFrame = frame === 0;
|
||
if (!emitter) {
|
||
throw new TypeError("Expected Player event emitter context");
|
||
}
|
||
const bufferingContext = useContext3(Internals5.BufferingContextReact);
|
||
if (!bufferingContext) {
|
||
throw new Error("Missing the buffering context. Most likely you have a Remotion version mismatch.");
|
||
}
|
||
const { buffering } = bufferingContext;
|
||
const seek = useCallback2((newFrame) => {
|
||
if (video?.id) {
|
||
setTimelinePosition((c) => ({ ...c, [video.id]: newFrame }));
|
||
}
|
||
frameRef.current = newFrame;
|
||
emitter.dispatchSeek(newFrame);
|
||
}, [emitter, setTimelinePosition, video?.id]);
|
||
const play = useCallback2((e) => {
|
||
if (imperativePlaying.current) {
|
||
return;
|
||
}
|
||
setHasPlayed(true);
|
||
if (isLastFrame) {
|
||
seek(0);
|
||
}
|
||
audioContext?.audioContext?.resume();
|
||
if (audioContext && audioContext.numberOfAudioTags > 0 && e) {
|
||
audioContext.playAllAudios();
|
||
}
|
||
audioAndVideoTags.current.forEach((a) => a.play("player play() was called and playing audio from a click"));
|
||
imperativePlaying.current = true;
|
||
setPlaying(true);
|
||
playStart.current = frameRef.current;
|
||
emitter.dispatchPlay();
|
||
}, [
|
||
imperativePlaying,
|
||
isLastFrame,
|
||
audioContext,
|
||
setPlaying,
|
||
emitter,
|
||
seek,
|
||
audioAndVideoTags
|
||
]);
|
||
const pause = useCallback2(() => {
|
||
if (imperativePlaying.current) {
|
||
imperativePlaying.current = false;
|
||
setPlaying(false);
|
||
emitter.dispatchPause();
|
||
audioContext?.audioContext?.suspend();
|
||
}
|
||
}, [emitter, imperativePlaying, setPlaying, audioContext]);
|
||
const pauseAndReturnToPlayStart = useCallback2(() => {
|
||
if (imperativePlaying.current) {
|
||
imperativePlaying.current = false;
|
||
frameRef.current = playStart.current;
|
||
if (config) {
|
||
setTimelinePosition((c) => ({
|
||
...c,
|
||
[config.id]: playStart.current
|
||
}));
|
||
setPlaying(false);
|
||
emitter.dispatchPause();
|
||
}
|
||
}
|
||
}, [config, emitter, imperativePlaying, setPlaying, setTimelinePosition]);
|
||
const videoId = video?.id;
|
||
const frameBack = useCallback2((frames) => {
|
||
if (!videoId) {
|
||
return null;
|
||
}
|
||
if (imperativePlaying.current) {
|
||
return;
|
||
}
|
||
setFrame((c) => {
|
||
const prevFrame = c[videoId] ?? window.remotion_initialFrame ?? 0;
|
||
const newFrame = Math.max(0, prevFrame - frames);
|
||
if (prevFrame === newFrame) {
|
||
return c;
|
||
}
|
||
return {
|
||
...c,
|
||
[videoId]: newFrame
|
||
};
|
||
});
|
||
}, [imperativePlaying, setFrame, videoId]);
|
||
const frameForward = useCallback2((frames) => {
|
||
if (!videoId) {
|
||
return null;
|
||
}
|
||
if (imperativePlaying.current) {
|
||
return;
|
||
}
|
||
setFrame((c) => {
|
||
const prevFrame = c[videoId] ?? window.remotion_initialFrame ?? 0;
|
||
const newFrame = Math.min(lastFrame, prevFrame + frames);
|
||
if (prevFrame === newFrame) {
|
||
return c;
|
||
}
|
||
return {
|
||
...c,
|
||
[videoId]: newFrame
|
||
};
|
||
});
|
||
}, [videoId, imperativePlaying, lastFrame, setFrame]);
|
||
const toggle = useCallback2((e) => {
|
||
if (imperativePlaying.current) {
|
||
pause();
|
||
} else {
|
||
play(e);
|
||
}
|
||
}, [imperativePlaying, pause, play]);
|
||
const isPlaying = useCallback2(() => {
|
||
return imperativePlaying.current;
|
||
}, [imperativePlaying]);
|
||
const getCurrentFrame = useCallback2(() => {
|
||
return frameRef.current;
|
||
}, [frameRef]);
|
||
const isBuffering = useCallback2(() => {
|
||
return buffering.current;
|
||
}, [buffering]);
|
||
const returnValue = useMemo(() => {
|
||
return {
|
||
frameBack,
|
||
frameForward,
|
||
isLastFrame,
|
||
emitter,
|
||
playing,
|
||
play,
|
||
pause,
|
||
seek,
|
||
isFirstFrame,
|
||
getCurrentFrame,
|
||
isPlaying,
|
||
isBuffering,
|
||
pauseAndReturnToPlayStart,
|
||
hasPlayed,
|
||
toggle
|
||
};
|
||
}, [
|
||
emitter,
|
||
frameBack,
|
||
frameForward,
|
||
hasPlayed,
|
||
isFirstFrame,
|
||
isLastFrame,
|
||
getCurrentFrame,
|
||
pause,
|
||
pauseAndReturnToPlayStart,
|
||
play,
|
||
playing,
|
||
seek,
|
||
toggle,
|
||
isPlaying,
|
||
isBuffering
|
||
]);
|
||
return returnValue;
|
||
};
|
||
|
||
// src/browser-mediasession.ts
|
||
var useBrowserMediaSession = ({
|
||
browserMediaControlsBehavior,
|
||
videoConfig,
|
||
playbackRate
|
||
}) => {
|
||
const { playing, pause, play, emitter, getCurrentFrame, seek } = usePlayer();
|
||
useEffect3(() => {
|
||
if (!navigator.mediaSession) {
|
||
return;
|
||
}
|
||
if (browserMediaControlsBehavior.mode === "do-nothing") {
|
||
return;
|
||
}
|
||
if (playing) {
|
||
navigator.mediaSession.playbackState = "playing";
|
||
} else {
|
||
navigator.mediaSession.playbackState = "paused";
|
||
}
|
||
}, [browserMediaControlsBehavior.mode, playing]);
|
||
useEffect3(() => {
|
||
if (!navigator.mediaSession) {
|
||
return;
|
||
}
|
||
if (browserMediaControlsBehavior.mode === "do-nothing") {
|
||
return;
|
||
}
|
||
const onTimeUpdate = () => {
|
||
if (!videoConfig) {
|
||
return;
|
||
}
|
||
if (navigator.mediaSession) {
|
||
navigator.mediaSession.setPositionState({
|
||
duration: videoConfig.durationInFrames / videoConfig.fps,
|
||
playbackRate,
|
||
position: getCurrentFrame() / videoConfig.fps
|
||
});
|
||
}
|
||
};
|
||
emitter.addEventListener("timeupdate", onTimeUpdate);
|
||
return () => {
|
||
emitter.removeEventListener("timeupdate", onTimeUpdate);
|
||
};
|
||
}, [
|
||
browserMediaControlsBehavior.mode,
|
||
emitter,
|
||
getCurrentFrame,
|
||
playbackRate,
|
||
videoConfig
|
||
]);
|
||
useEffect3(() => {
|
||
if (!navigator.mediaSession) {
|
||
return;
|
||
}
|
||
if (browserMediaControlsBehavior.mode === "do-nothing") {
|
||
return;
|
||
}
|
||
navigator.mediaSession.setActionHandler("play", () => {
|
||
if (browserMediaControlsBehavior.mode === "register-media-session") {
|
||
play();
|
||
}
|
||
});
|
||
navigator.mediaSession.setActionHandler("pause", () => {
|
||
if (browserMediaControlsBehavior.mode === "register-media-session") {
|
||
pause();
|
||
}
|
||
});
|
||
navigator.mediaSession.setActionHandler("seekto", (event) => {
|
||
if (browserMediaControlsBehavior.mode === "register-media-session" && event.seekTime !== undefined && videoConfig) {
|
||
seek(Math.round(event.seekTime * videoConfig.fps));
|
||
}
|
||
});
|
||
navigator.mediaSession.setActionHandler("seekbackward", () => {
|
||
if (browserMediaControlsBehavior.mode === "register-media-session" && videoConfig) {
|
||
seek(Math.max(0, Math.round((getCurrentFrame() - 10) * videoConfig.fps)));
|
||
}
|
||
});
|
||
navigator.mediaSession.setActionHandler("seekforward", () => {
|
||
if (browserMediaControlsBehavior.mode === "register-media-session" && videoConfig) {
|
||
seek(Math.max(videoConfig.durationInFrames - 1, Math.round((getCurrentFrame() + 10) * videoConfig.fps)));
|
||
}
|
||
});
|
||
navigator.mediaSession.setActionHandler("previoustrack", () => {
|
||
if (browserMediaControlsBehavior.mode === "register-media-session") {
|
||
seek(0);
|
||
}
|
||
});
|
||
return () => {
|
||
navigator.mediaSession.metadata = null;
|
||
navigator.mediaSession.setActionHandler("play", null);
|
||
navigator.mediaSession.setActionHandler("pause", null);
|
||
navigator.mediaSession.setActionHandler("seekto", null);
|
||
navigator.mediaSession.setActionHandler("seekbackward", null);
|
||
navigator.mediaSession.setActionHandler("seekforward", null);
|
||
navigator.mediaSession.setActionHandler("previoustrack", null);
|
||
};
|
||
}, [
|
||
browserMediaControlsBehavior.mode,
|
||
getCurrentFrame,
|
||
pause,
|
||
play,
|
||
seek,
|
||
videoConfig
|
||
]);
|
||
};
|
||
|
||
// src/calculate-next-frame.ts
|
||
var calculateNextFrame = ({
|
||
time,
|
||
currentFrame: startFrame,
|
||
playbackSpeed,
|
||
fps,
|
||
actualLastFrame,
|
||
actualFirstFrame,
|
||
framesAdvanced,
|
||
shouldLoop
|
||
}) => {
|
||
const op = playbackSpeed < 0 ? Math.ceil : Math.floor;
|
||
const framesToAdvance = op(time * playbackSpeed / (1000 / fps)) - framesAdvanced;
|
||
const nextFrame = framesToAdvance + startFrame;
|
||
const isCurrentFrameOutside = startFrame > actualLastFrame || startFrame < actualFirstFrame;
|
||
const isNextFrameOutside = nextFrame > actualLastFrame || nextFrame < actualFirstFrame;
|
||
const hasEnded = !shouldLoop && isNextFrameOutside && !isCurrentFrameOutside;
|
||
if (playbackSpeed > 0) {
|
||
if (isNextFrameOutside) {
|
||
return {
|
||
nextFrame: actualFirstFrame,
|
||
framesToAdvance,
|
||
hasEnded
|
||
};
|
||
}
|
||
return { nextFrame, framesToAdvance, hasEnded };
|
||
}
|
||
if (isNextFrameOutside) {
|
||
return { nextFrame: actualLastFrame, framesToAdvance, hasEnded };
|
||
}
|
||
return { nextFrame, framesToAdvance, hasEnded };
|
||
};
|
||
|
||
// src/is-backgrounded.ts
|
||
import { useEffect as useEffect4, useRef as useRef3 } from "react";
|
||
var getIsBackgrounded = () => {
|
||
if (typeof document === "undefined") {
|
||
return false;
|
||
}
|
||
return document.visibilityState === "hidden";
|
||
};
|
||
var useIsBackgrounded = () => {
|
||
const isBackgrounded = useRef3(getIsBackgrounded());
|
||
useEffect4(() => {
|
||
const onVisibilityChange = () => {
|
||
isBackgrounded.current = getIsBackgrounded();
|
||
};
|
||
document.addEventListener("visibilitychange", onVisibilityChange);
|
||
return () => {
|
||
document.removeEventListener("visibilitychange", onVisibilityChange);
|
||
};
|
||
}, []);
|
||
return isBackgrounded;
|
||
};
|
||
|
||
// src/use-playback.ts
|
||
var usePlayback = ({
|
||
loop,
|
||
playbackRate,
|
||
moveToBeginningWhenEnded,
|
||
inFrame,
|
||
outFrame,
|
||
browserMediaControlsBehavior,
|
||
getCurrentFrame
|
||
}) => {
|
||
const config = Internals6.useUnsafeVideoConfig();
|
||
const frame = Internals6.Timeline.useTimelinePosition();
|
||
const { playing, pause, emitter, isPlaying } = usePlayer();
|
||
const setFrame = Internals6.Timeline.useTimelineSetFrame();
|
||
const isBackgroundedRef = useIsBackgrounded();
|
||
const lastTimeUpdateEvent = useRef4(null);
|
||
const context = useContext4(Internals6.BufferingContextReact);
|
||
if (!context) {
|
||
throw new Error("Missing the buffering context. Most likely you have a Remotion version mismatch.");
|
||
}
|
||
useBrowserMediaSession({
|
||
browserMediaControlsBehavior,
|
||
playbackRate,
|
||
videoConfig: config
|
||
});
|
||
useEffect5(() => {
|
||
if (!config) {
|
||
return;
|
||
}
|
||
if (!playing) {
|
||
return;
|
||
}
|
||
let hasBeenStopped = false;
|
||
let reqAnimFrameCall = null;
|
||
let startedTime = performance.now();
|
||
let framesAdvanced = 0;
|
||
const cancelQueuedFrame = () => {
|
||
if (reqAnimFrameCall !== null) {
|
||
if (reqAnimFrameCall.type === "raf") {
|
||
cancelAnimationFrame(reqAnimFrameCall.id);
|
||
} else {
|
||
clearTimeout(reqAnimFrameCall.id);
|
||
}
|
||
}
|
||
};
|
||
const stop = () => {
|
||
hasBeenStopped = true;
|
||
cancelQueuedFrame();
|
||
};
|
||
const callback = () => {
|
||
if (hasBeenStopped) {
|
||
return;
|
||
}
|
||
if (!isPlaying()) {
|
||
return;
|
||
}
|
||
const time = performance.now() - startedTime;
|
||
const actualLastFrame = outFrame ?? config.durationInFrames - 1;
|
||
const actualFirstFrame = inFrame ?? 0;
|
||
const currentFrame = getCurrentFrame();
|
||
const { nextFrame, framesToAdvance, hasEnded } = calculateNextFrame({
|
||
time,
|
||
currentFrame,
|
||
playbackSpeed: playbackRate,
|
||
fps: config.fps,
|
||
actualFirstFrame,
|
||
actualLastFrame,
|
||
framesAdvanced,
|
||
shouldLoop: loop
|
||
});
|
||
framesAdvanced += framesToAdvance;
|
||
if (nextFrame !== getCurrentFrame() && (!hasEnded || moveToBeginningWhenEnded)) {
|
||
setFrame((c) => ({ ...c, [config.id]: nextFrame }));
|
||
}
|
||
if (hasEnded) {
|
||
stop();
|
||
pause();
|
||
emitter.dispatchEnded();
|
||
return;
|
||
}
|
||
queueNextFrame();
|
||
};
|
||
const queueNextFrame = () => {
|
||
if (context.buffering.current) {
|
||
const stopListening = context.listenForResume(() => {
|
||
stopListening.remove();
|
||
startedTime = performance.now();
|
||
framesAdvanced = 0;
|
||
queueNextFrame();
|
||
});
|
||
return;
|
||
}
|
||
if (isBackgroundedRef.current) {
|
||
reqAnimFrameCall = {
|
||
type: "timeout",
|
||
id: setTimeout(callback, 1000 / config.fps)
|
||
};
|
||
return;
|
||
}
|
||
reqAnimFrameCall = { type: "raf", id: requestAnimationFrame(callback) };
|
||
};
|
||
queueNextFrame();
|
||
const onVisibilityChange = () => {
|
||
if (document.visibilityState === "visible") {
|
||
return;
|
||
}
|
||
cancelQueuedFrame();
|
||
callback();
|
||
};
|
||
window.addEventListener("visibilitychange", onVisibilityChange);
|
||
return () => {
|
||
window.removeEventListener("visibilitychange", onVisibilityChange);
|
||
stop();
|
||
};
|
||
}, [
|
||
config,
|
||
loop,
|
||
pause,
|
||
playing,
|
||
setFrame,
|
||
emitter,
|
||
playbackRate,
|
||
inFrame,
|
||
outFrame,
|
||
moveToBeginningWhenEnded,
|
||
isBackgroundedRef,
|
||
getCurrentFrame,
|
||
context,
|
||
isPlaying
|
||
]);
|
||
useEffect5(() => {
|
||
const interval = setInterval(() => {
|
||
if (lastTimeUpdateEvent.current === getCurrentFrame()) {
|
||
return;
|
||
}
|
||
emitter.dispatchTimeUpdate({ frame: getCurrentFrame() });
|
||
lastTimeUpdateEvent.current = getCurrentFrame();
|
||
}, 250);
|
||
return () => clearInterval(interval);
|
||
}, [emitter, getCurrentFrame]);
|
||
useEffect5(() => {
|
||
emitter.dispatchFrameUpdate({ frame });
|
||
}, [emitter, frame]);
|
||
};
|
||
|
||
// src/utils/use-element-size.ts
|
||
import { useCallback as useCallback3, useEffect as useEffect6, useMemo as useMemo2, useState as useState4 } from "react";
|
||
var elementSizeHooks = [];
|
||
var updateAllElementsSizes = () => {
|
||
for (const listener of elementSizeHooks) {
|
||
listener();
|
||
}
|
||
};
|
||
var useElementSize = (ref, options) => {
|
||
const [size, setSize] = useState4(() => {
|
||
if (!ref.current) {
|
||
return null;
|
||
}
|
||
const rect = ref.current.getClientRects();
|
||
if (!rect[0]) {
|
||
return null;
|
||
}
|
||
return {
|
||
width: rect[0].width,
|
||
height: rect[0].height,
|
||
left: rect[0].x,
|
||
top: rect[0].y,
|
||
windowSize: {
|
||
height: window.innerHeight,
|
||
width: window.innerWidth
|
||
}
|
||
};
|
||
});
|
||
const observer = useMemo2(() => {
|
||
if (typeof ResizeObserver === "undefined") {
|
||
return null;
|
||
}
|
||
return new ResizeObserver((entries) => {
|
||
const { contentRect, target } = entries[0];
|
||
const newSize = target.getClientRects();
|
||
if (!newSize?.[0]) {
|
||
setSize(null);
|
||
return;
|
||
}
|
||
const probableCssParentScale = contentRect.width === 0 ? 1 : newSize[0].width / contentRect.width;
|
||
const width = options.shouldApplyCssTransforms || probableCssParentScale === 0 ? newSize[0].width : newSize[0].width * (1 / probableCssParentScale);
|
||
const height = options.shouldApplyCssTransforms || probableCssParentScale === 0 ? newSize[0].height : newSize[0].height * (1 / probableCssParentScale);
|
||
setSize((prevState) => {
|
||
const isSame = prevState && prevState.width === width && prevState.height === height && prevState.left === newSize[0].x && prevState.top === newSize[0].y && prevState.windowSize.height === window.innerHeight && prevState.windowSize.width === window.innerWidth;
|
||
if (isSame) {
|
||
return prevState;
|
||
}
|
||
return {
|
||
width,
|
||
height,
|
||
left: newSize[0].x,
|
||
top: newSize[0].y,
|
||
windowSize: {
|
||
height: window.innerHeight,
|
||
width: window.innerWidth
|
||
}
|
||
};
|
||
});
|
||
});
|
||
}, [options.shouldApplyCssTransforms]);
|
||
const updateSize = useCallback3(() => {
|
||
if (!ref.current) {
|
||
return;
|
||
}
|
||
const rect = ref.current.getClientRects();
|
||
if (!rect[0]) {
|
||
setSize(null);
|
||
return;
|
||
}
|
||
setSize((prevState) => {
|
||
const isSame = prevState && prevState.width === rect[0].width && prevState.height === rect[0].height && prevState.left === rect[0].x && prevState.top === rect[0].y && prevState.windowSize.height === window.innerHeight && prevState.windowSize.width === window.innerWidth;
|
||
if (isSame) {
|
||
return prevState;
|
||
}
|
||
return {
|
||
width: rect[0].width,
|
||
height: rect[0].height,
|
||
left: rect[0].x,
|
||
top: rect[0].y,
|
||
windowSize: {
|
||
height: window.innerHeight,
|
||
width: window.innerWidth
|
||
}
|
||
};
|
||
});
|
||
}, [ref]);
|
||
useEffect6(() => {
|
||
if (!observer) {
|
||
return;
|
||
}
|
||
const { current } = ref;
|
||
if (current) {
|
||
observer.observe(current);
|
||
}
|
||
return () => {
|
||
if (current) {
|
||
observer.unobserve(current);
|
||
}
|
||
};
|
||
}, [observer, ref, updateSize]);
|
||
useEffect6(() => {
|
||
if (!options.triggerOnWindowResize) {
|
||
return;
|
||
}
|
||
window.addEventListener("resize", updateSize);
|
||
return () => {
|
||
window.removeEventListener("resize", updateSize);
|
||
};
|
||
}, [options.triggerOnWindowResize, updateSize]);
|
||
useEffect6(() => {
|
||
elementSizeHooks.push(updateSize);
|
||
return () => {
|
||
elementSizeHooks = elementSizeHooks.filter((e) => e !== updateSize);
|
||
};
|
||
}, [updateSize]);
|
||
return useMemo2(() => {
|
||
if (!size) {
|
||
return null;
|
||
}
|
||
return { ...size, refresh: updateSize };
|
||
}, [size, updateSize]);
|
||
};
|
||
|
||
// src/Player.tsx
|
||
import {
|
||
forwardRef as forwardRef2,
|
||
useEffect as useEffect13,
|
||
useImperativeHandle as useImperativeHandle2,
|
||
useLayoutEffect as useLayoutEffect2,
|
||
useMemo as useMemo14,
|
||
useRef as useRef11,
|
||
useState as useState13
|
||
} from "react";
|
||
import { Composition, Internals as Internals15 } from "remotion";
|
||
|
||
// src/PlayerUI.tsx
|
||
import React10, {
|
||
Suspense,
|
||
forwardRef,
|
||
useCallback as useCallback11,
|
||
useContext as useContext6,
|
||
useEffect as useEffect12,
|
||
useImperativeHandle,
|
||
useMemo as useMemo12,
|
||
useRef as useRef10,
|
||
useState as useState11
|
||
} from "react";
|
||
import { Internals as Internals11 } from "remotion";
|
||
|
||
// src/PlayerControls.tsx
|
||
import { useCallback as useCallback8, useEffect as useEffect10, useMemo as useMemo9, useRef as useRef8, useState as useState10 } from "react";
|
||
|
||
// src/DefaultPlayPauseButton.tsx
|
||
import { jsx as jsx4 } from "react/jsx-runtime";
|
||
var DefaultPlayPauseButton = ({ playing, buffering }) => {
|
||
if (playing && buffering) {
|
||
return /* @__PURE__ */ jsx4(BufferingIndicator, {
|
||
type: "player"
|
||
});
|
||
}
|
||
if (playing) {
|
||
return /* @__PURE__ */ jsx4(PauseIcon, {});
|
||
}
|
||
return /* @__PURE__ */ jsx4(PlayIcon, {});
|
||
};
|
||
|
||
// src/MediaVolumeSlider.tsx
|
||
import { useCallback as useCallback5, useMemo as useMemo4, useRef as useRef5, useState as useState6 } from "react";
|
||
import { Internals as Internals7 } from "remotion";
|
||
|
||
// src/render-volume-slider.tsx
|
||
import React3, { useCallback as useCallback4, useMemo as useMemo3, useState as useState5 } from "react";
|
||
import { random } from "remotion";
|
||
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
||
var KNOB_SIZE = 12;
|
||
var BAR_HEIGHT = 5;
|
||
var DefaultVolumeSlider = ({
|
||
volume,
|
||
isVertical,
|
||
onBlur,
|
||
inputRef,
|
||
setVolume
|
||
}) => {
|
||
const sliderContainer = useMemo3(() => {
|
||
const paddingLeft = 5;
|
||
const common = {
|
||
paddingLeft,
|
||
height: ICON_SIZE,
|
||
width: VOLUME_SLIDER_WIDTH,
|
||
display: "inline-flex",
|
||
alignItems: "center"
|
||
};
|
||
if (isVertical) {
|
||
return {
|
||
...common,
|
||
position: "absolute",
|
||
transform: `rotate(-90deg) translateX(${VOLUME_SLIDER_WIDTH / 2 + ICON_SIZE / 2}px)`
|
||
};
|
||
}
|
||
return {
|
||
...common
|
||
};
|
||
}, [isVertical]);
|
||
const randomId = typeof React3.useId === "undefined" ? "volume-slider" : React3.useId();
|
||
const [randomClass] = useState5(() => `__remotion-volume-slider-${random(randomId)}`.replace(".", ""));
|
||
const onVolumeChange = useCallback4((e) => {
|
||
setVolume(parseFloat(e.target.value));
|
||
}, [setVolume]);
|
||
const inputStyle = useMemo3(() => {
|
||
const commonStyle = {
|
||
WebkitAppearance: "none",
|
||
backgroundColor: "rgba(255, 255, 255, 0.5)",
|
||
borderRadius: BAR_HEIGHT / 2,
|
||
cursor: "pointer",
|
||
height: BAR_HEIGHT,
|
||
width: VOLUME_SLIDER_WIDTH,
|
||
backgroundImage: `linear-gradient(
|
||
to right,
|
||
white ${volume * 100}%, rgba(255, 255, 255, 0) ${volume * 100}%
|
||
)`
|
||
};
|
||
if (isVertical) {
|
||
return {
|
||
...commonStyle,
|
||
bottom: ICON_SIZE + VOLUME_SLIDER_WIDTH / 2
|
||
};
|
||
}
|
||
return commonStyle;
|
||
}, [isVertical, volume]);
|
||
const sliderStyle = `
|
||
.${randomClass}::-webkit-slider-thumb {
|
||
-webkit-appearance: none;
|
||
background-color: white;
|
||
border-radius: ${KNOB_SIZE / 2}px;
|
||
box-shadow: 0 0 2px black;
|
||
height: ${KNOB_SIZE}px;
|
||
width: ${KNOB_SIZE}px;
|
||
}
|
||
|
||
.${randomClass}::-moz-range-thumb {
|
||
-webkit-appearance: none;
|
||
background-color: white;
|
||
border-radius: ${KNOB_SIZE / 2}px;
|
||
box-shadow: 0 0 2px black;
|
||
height: ${KNOB_SIZE}px;
|
||
width: ${KNOB_SIZE}px;
|
||
}
|
||
`;
|
||
return /* @__PURE__ */ jsxs3("div", {
|
||
style: sliderContainer,
|
||
children: [
|
||
/* @__PURE__ */ jsx5("style", {
|
||
dangerouslySetInnerHTML: {
|
||
__html: sliderStyle
|
||
}
|
||
}),
|
||
/* @__PURE__ */ jsx5("input", {
|
||
ref: inputRef,
|
||
"aria-label": "Change volume",
|
||
className: randomClass,
|
||
max: 1,
|
||
min: 0,
|
||
onBlur,
|
||
onChange: onVolumeChange,
|
||
step: 0.01,
|
||
type: "range",
|
||
value: volume,
|
||
style: inputStyle
|
||
})
|
||
]
|
||
});
|
||
};
|
||
var renderDefaultVolumeSlider = (props) => {
|
||
return /* @__PURE__ */ jsx5(DefaultVolumeSlider, {
|
||
...props
|
||
});
|
||
};
|
||
|
||
// src/MediaVolumeSlider.tsx
|
||
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
||
var VOLUME_SLIDER_WIDTH = 100;
|
||
var MediaVolumeSlider = ({ displayVerticalVolumeSlider, renderMuteButton, renderVolumeSlider }) => {
|
||
const [mediaMuted, setMediaMuted] = Internals7.useMediaMutedState();
|
||
const [mediaVolume, setMediaVolume] = Internals7.useMediaVolumeState();
|
||
const [focused, setFocused] = useState6(false);
|
||
const parentDivRef = useRef5(null);
|
||
const inputRef = useRef5(null);
|
||
const hover = useHoverState(parentDivRef, false);
|
||
const onBlur = useCallback5(() => {
|
||
setTimeout(() => {
|
||
if (inputRef.current && document.activeElement !== inputRef.current) {
|
||
setFocused(false);
|
||
}
|
||
}, 10);
|
||
}, []);
|
||
const isVolume0 = mediaVolume === 0;
|
||
const onClick = useCallback5(() => {
|
||
if (isVolume0) {
|
||
setMediaVolume(1);
|
||
setMediaMuted(false);
|
||
return;
|
||
}
|
||
setMediaMuted((mute) => !mute);
|
||
}, [isVolume0, setMediaMuted, setMediaVolume]);
|
||
const parentDivStyle = useMemo4(() => {
|
||
return {
|
||
display: "inline-flex",
|
||
background: "none",
|
||
border: "none",
|
||
justifyContent: "center",
|
||
alignItems: "center",
|
||
touchAction: "none",
|
||
...displayVerticalVolumeSlider && { position: "relative" }
|
||
};
|
||
}, [displayVerticalVolumeSlider]);
|
||
const volumeContainer = useMemo4(() => {
|
||
return {
|
||
display: "inline",
|
||
width: ICON_SIZE,
|
||
height: ICON_SIZE,
|
||
cursor: "pointer",
|
||
appearance: "none",
|
||
background: "none",
|
||
border: "none",
|
||
padding: 0
|
||
};
|
||
}, []);
|
||
const renderDefaultMuteButton = useCallback5(({ muted, volume }) => {
|
||
const isMutedOrZero = muted || volume === 0;
|
||
return /* @__PURE__ */ jsx6("button", {
|
||
"aria-label": isMutedOrZero ? "Unmute sound" : "Mute sound",
|
||
title: isMutedOrZero ? "Unmute sound" : "Mute sound",
|
||
onClick,
|
||
onBlur,
|
||
onFocus: () => setFocused(true),
|
||
style: volumeContainer,
|
||
type: "button",
|
||
children: isMutedOrZero ? /* @__PURE__ */ jsx6(VolumeOffIcon, {}) : /* @__PURE__ */ jsx6(VolumeOnIcon, {})
|
||
});
|
||
}, [onBlur, onClick, volumeContainer]);
|
||
const muteButton = useMemo4(() => {
|
||
return renderMuteButton ? renderMuteButton({ muted: mediaMuted, volume: mediaVolume }) : renderDefaultMuteButton({ muted: mediaMuted, volume: mediaVolume });
|
||
}, [mediaMuted, mediaVolume, renderDefaultMuteButton, renderMuteButton]);
|
||
const volumeSlider = useMemo4(() => {
|
||
return (focused || hover) && !mediaMuted && !Internals7.isIosSafari() ? (renderVolumeSlider ?? renderDefaultVolumeSlider)({
|
||
isVertical: displayVerticalVolumeSlider,
|
||
volume: mediaVolume,
|
||
onBlur: () => setFocused(false),
|
||
inputRef,
|
||
setVolume: setMediaVolume
|
||
}) : null;
|
||
}, [
|
||
displayVerticalVolumeSlider,
|
||
focused,
|
||
hover,
|
||
mediaMuted,
|
||
mediaVolume,
|
||
renderVolumeSlider,
|
||
setMediaVolume
|
||
]);
|
||
return /* @__PURE__ */ jsxs4("div", {
|
||
ref: parentDivRef,
|
||
style: parentDivStyle,
|
||
children: [
|
||
muteButton,
|
||
volumeSlider
|
||
]
|
||
});
|
||
};
|
||
|
||
// src/PlaybackrateControl.tsx
|
||
import {
|
||
useCallback as useCallback6,
|
||
useContext as useContext5,
|
||
useEffect as useEffect8,
|
||
useMemo as useMemo5,
|
||
useState as useState8
|
||
} from "react";
|
||
import { Internals as Internals8 } from "remotion";
|
||
|
||
// src/utils/use-component-visible.ts
|
||
import { useEffect as useEffect7, useRef as useRef6, useState as useState7 } from "react";
|
||
function useComponentVisible(initialIsVisible) {
|
||
const [isComponentVisible, setIsComponentVisible] = useState7(initialIsVisible);
|
||
const ref = useRef6(null);
|
||
useEffect7(() => {
|
||
const handleClickOutside = (event) => {
|
||
if (ref.current && !ref.current.contains(event.target)) {
|
||
setIsComponentVisible(false);
|
||
}
|
||
};
|
||
document.addEventListener("pointerup", handleClickOutside, true);
|
||
return () => {
|
||
document.removeEventListener("pointerup", handleClickOutside, true);
|
||
};
|
||
}, []);
|
||
return { ref, isComponentVisible, setIsComponentVisible };
|
||
}
|
||
|
||
// src/PlaybackrateControl.tsx
|
||
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
||
var BOTTOM = 35;
|
||
var THRESHOLD = 70;
|
||
var rateDiv = {
|
||
height: 30,
|
||
paddingRight: 15,
|
||
paddingLeft: 12,
|
||
display: "flex",
|
||
flexDirection: "row",
|
||
alignItems: "center"
|
||
};
|
||
var checkmarkContainer = {
|
||
width: 22,
|
||
display: "flex",
|
||
alignItems: "center"
|
||
};
|
||
var checkmarkStyle = {
|
||
width: 14,
|
||
height: 14,
|
||
color: "black"
|
||
};
|
||
var Checkmark = () => /* @__PURE__ */ jsx7("svg", {
|
||
viewBox: "0 0 512 512",
|
||
style: checkmarkStyle,
|
||
children: /* @__PURE__ */ jsx7("path", {
|
||
fill: "currentColor",
|
||
d: "M435.848 83.466L172.804 346.51l-96.652-96.652c-4.686-4.686-12.284-4.686-16.971 0l-28.284 28.284c-4.686 4.686-4.686 12.284 0 16.971l133.421 133.421c4.686 4.686 12.284 4.686 16.971 0l299.813-299.813c4.686-4.686 4.686-12.284 0-16.971l-28.284-28.284c-4.686-4.686-12.284-4.686-16.97 0z"
|
||
})
|
||
});
|
||
var formatPlaybackRate = (rate) => {
|
||
const str = rate.toString();
|
||
return str.includes(".") ? str : str + ".0";
|
||
};
|
||
var PlaybackrateOption = ({ rate, onSelect, selectedRate, keyboardSelectedRate }) => {
|
||
const onClick = useCallback6((e) => {
|
||
e.stopPropagation();
|
||
e.preventDefault();
|
||
onSelect(rate);
|
||
}, [onSelect, rate]);
|
||
const [hovered, setHovered] = useState8(false);
|
||
const onMouseEnter = useCallback6(() => {
|
||
setHovered(true);
|
||
}, []);
|
||
const onMouseLeave = useCallback6(() => {
|
||
setHovered(false);
|
||
}, []);
|
||
const isFocused = keyboardSelectedRate === rate;
|
||
const actualStyle = useMemo5(() => {
|
||
return {
|
||
...rateDiv,
|
||
backgroundColor: hovered || isFocused ? "#eee" : "transparent"
|
||
};
|
||
}, [hovered, isFocused]);
|
||
return /* @__PURE__ */ jsxs5("div", {
|
||
onPointerEnter: onMouseEnter,
|
||
onPointerLeave: onMouseLeave,
|
||
tabIndex: 0,
|
||
style: actualStyle,
|
||
onClick,
|
||
children: [
|
||
/* @__PURE__ */ jsx7("div", {
|
||
style: checkmarkContainer,
|
||
children: rate === selectedRate ? /* @__PURE__ */ jsx7(Checkmark, {}) : null
|
||
}),
|
||
formatPlaybackRate(rate),
|
||
"x"
|
||
]
|
||
}, rate);
|
||
};
|
||
var PlaybackPopup = ({ setIsComponentVisible, playbackRates, canvasSize }) => {
|
||
const { setPlaybackRate, playbackRate } = useContext5(Internals8.TimelineContext);
|
||
const [keyboardSelectedRate, setKeyboardSelectedRate] = useState8(playbackRate);
|
||
useEffect8(() => {
|
||
const listener = (e) => {
|
||
e.preventDefault();
|
||
if (e.key === "ArrowUp") {
|
||
const currentIndex = playbackRates.findIndex((rate) => rate === keyboardSelectedRate);
|
||
if (currentIndex === 0) {
|
||
return;
|
||
}
|
||
if (currentIndex === -1) {
|
||
setKeyboardSelectedRate(playbackRates[0]);
|
||
} else {
|
||
setKeyboardSelectedRate(playbackRates[currentIndex - 1]);
|
||
}
|
||
} else if (e.key === "ArrowDown") {
|
||
const currentIndex = playbackRates.findIndex((rate) => rate === keyboardSelectedRate);
|
||
if (currentIndex === playbackRates.length - 1) {
|
||
return;
|
||
}
|
||
if (currentIndex === -1) {
|
||
setKeyboardSelectedRate(playbackRates[playbackRates.length - 1]);
|
||
} else {
|
||
setKeyboardSelectedRate(playbackRates[currentIndex + 1]);
|
||
}
|
||
} else if (e.key === "Enter") {
|
||
setPlaybackRate(keyboardSelectedRate);
|
||
setIsComponentVisible(false);
|
||
}
|
||
};
|
||
window.addEventListener("keydown", listener);
|
||
return () => {
|
||
window.removeEventListener("keydown", listener);
|
||
};
|
||
}, [
|
||
playbackRates,
|
||
keyboardSelectedRate,
|
||
setPlaybackRate,
|
||
setIsComponentVisible
|
||
]);
|
||
const onSelect = useCallback6((rate) => {
|
||
setPlaybackRate(rate);
|
||
setIsComponentVisible(false);
|
||
}, [setIsComponentVisible, setPlaybackRate]);
|
||
const playbackPopup = useMemo5(() => {
|
||
return {
|
||
position: "absolute",
|
||
right: 0,
|
||
width: 125,
|
||
maxHeight: canvasSize.height - THRESHOLD - BOTTOM,
|
||
bottom: 35,
|
||
background: "#fff",
|
||
borderRadius: 4,
|
||
overflow: "auto",
|
||
color: "black",
|
||
textAlign: "left"
|
||
};
|
||
}, [canvasSize.height]);
|
||
return /* @__PURE__ */ jsx7("div", {
|
||
style: playbackPopup,
|
||
children: playbackRates.map((rate) => {
|
||
return /* @__PURE__ */ jsx7(PlaybackrateOption, {
|
||
selectedRate: playbackRate,
|
||
onSelect,
|
||
rate,
|
||
keyboardSelectedRate
|
||
}, rate);
|
||
})
|
||
});
|
||
};
|
||
var label = {
|
||
fontSize: 13,
|
||
fontWeight: "bold",
|
||
color: "white",
|
||
border: "2px solid white",
|
||
borderRadius: 20,
|
||
paddingLeft: 8,
|
||
paddingRight: 8,
|
||
paddingTop: 2,
|
||
paddingBottom: 2
|
||
};
|
||
var playerButtonStyle = {
|
||
appearance: "none",
|
||
backgroundColor: "transparent",
|
||
border: "none",
|
||
cursor: "pointer",
|
||
paddingLeft: 0,
|
||
paddingRight: 0,
|
||
paddingTop: 6,
|
||
paddingBottom: 6,
|
||
height: 37,
|
||
display: "inline-flex",
|
||
marginBottom: 0,
|
||
marginTop: 0,
|
||
alignItems: "center"
|
||
};
|
||
var button = {
|
||
...playerButtonStyle,
|
||
position: "relative"
|
||
};
|
||
var PlaybackrateControl = ({ playbackRates, canvasSize }) => {
|
||
const { ref, isComponentVisible, setIsComponentVisible } = useComponentVisible(false);
|
||
const { playbackRate } = useContext5(Internals8.TimelineContext);
|
||
const onClick = useCallback6((e) => {
|
||
e.stopPropagation();
|
||
e.preventDefault();
|
||
setIsComponentVisible((prevIsComponentVisible) => !prevIsComponentVisible);
|
||
}, [setIsComponentVisible]);
|
||
return /* @__PURE__ */ jsx7("div", {
|
||
ref,
|
||
children: /* @__PURE__ */ jsxs5("button", {
|
||
type: "button",
|
||
"aria-label": "Change playback rate",
|
||
style: button,
|
||
onClick,
|
||
children: [
|
||
/* @__PURE__ */ jsxs5("div", {
|
||
style: label,
|
||
children: [
|
||
playbackRate,
|
||
"x"
|
||
]
|
||
}),
|
||
isComponentVisible && /* @__PURE__ */ jsx7(PlaybackPopup, {
|
||
canvasSize,
|
||
playbackRates,
|
||
setIsComponentVisible
|
||
})
|
||
]
|
||
})
|
||
});
|
||
};
|
||
|
||
// src/PlayerSeekBar.tsx
|
||
import { useCallback as useCallback7, useEffect as useEffect9, useMemo as useMemo6, useRef as useRef7, useState as useState9 } from "react";
|
||
import { Internals as Internals9, interpolate } from "remotion";
|
||
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
||
var getFrameFromX = (clientX, durationInFrames, width) => {
|
||
const pos = clientX;
|
||
const frame = Math.round(interpolate(pos, [0, width], [0, durationInFrames - 1], {
|
||
extrapolateLeft: "clamp",
|
||
extrapolateRight: "clamp"
|
||
}));
|
||
return frame;
|
||
};
|
||
var BAR_HEIGHT2 = 5;
|
||
var KNOB_SIZE2 = 12;
|
||
var VERTICAL_PADDING = 4;
|
||
var containerStyle = {
|
||
userSelect: "none",
|
||
WebkitUserSelect: "none",
|
||
paddingTop: VERTICAL_PADDING,
|
||
paddingBottom: VERTICAL_PADDING,
|
||
boxSizing: "border-box",
|
||
cursor: "pointer",
|
||
position: "relative",
|
||
touchAction: "none"
|
||
};
|
||
var barBackground = {
|
||
height: BAR_HEIGHT2,
|
||
backgroundColor: "rgba(255, 255, 255, 0.25)",
|
||
width: "100%",
|
||
borderRadius: BAR_HEIGHT2 / 2
|
||
};
|
||
var findBodyInWhichDivIsLocated = (div) => {
|
||
let current = div;
|
||
while (current.parentElement) {
|
||
current = current.parentElement;
|
||
}
|
||
return current;
|
||
};
|
||
var PlayerSeekBar = ({ durationInFrames, onSeekEnd, onSeekStart, inFrame, outFrame }) => {
|
||
const containerRef = useRef7(null);
|
||
const barHovered = useHoverState(containerRef, false);
|
||
const size = useElementSize(containerRef, {
|
||
triggerOnWindowResize: true,
|
||
shouldApplyCssTransforms: true
|
||
});
|
||
const { seek, play, pause, playing } = usePlayer();
|
||
const frame = Internals9.Timeline.useTimelinePosition();
|
||
const [dragging, setDragging] = useState9({
|
||
dragging: false
|
||
});
|
||
const width = size?.width ?? 0;
|
||
const onPointerDown = useCallback7((e) => {
|
||
if (e.button !== 0) {
|
||
return;
|
||
}
|
||
const posLeft = containerRef.current?.getBoundingClientRect().left;
|
||
const _frame = getFrameFromX(e.clientX - posLeft, durationInFrames, width);
|
||
pause();
|
||
seek(_frame);
|
||
setDragging({
|
||
dragging: true,
|
||
wasPlaying: playing
|
||
});
|
||
onSeekStart();
|
||
}, [durationInFrames, width, pause, seek, playing, onSeekStart]);
|
||
const onPointerMove = useCallback7((e) => {
|
||
if (!size) {
|
||
throw new Error("Player has no size");
|
||
}
|
||
if (!dragging.dragging) {
|
||
return;
|
||
}
|
||
const posLeft = containerRef.current?.getBoundingClientRect().left;
|
||
const _frame = getFrameFromX(e.clientX - posLeft, durationInFrames, size.width);
|
||
seek(_frame);
|
||
}, [dragging.dragging, durationInFrames, seek, size]);
|
||
const onPointerUp = useCallback7(() => {
|
||
setDragging({
|
||
dragging: false
|
||
});
|
||
if (!dragging.dragging) {
|
||
return;
|
||
}
|
||
if (dragging.wasPlaying) {
|
||
play();
|
||
} else {
|
||
pause();
|
||
}
|
||
onSeekEnd();
|
||
}, [dragging, onSeekEnd, pause, play]);
|
||
useEffect9(() => {
|
||
if (!dragging.dragging) {
|
||
return;
|
||
}
|
||
const body = findBodyInWhichDivIsLocated(containerRef.current);
|
||
body.addEventListener("pointermove", onPointerMove);
|
||
body.addEventListener("pointerup", onPointerUp);
|
||
return () => {
|
||
body.removeEventListener("pointermove", onPointerMove);
|
||
body.removeEventListener("pointerup", onPointerUp);
|
||
};
|
||
}, [dragging.dragging, onPointerMove, onPointerUp]);
|
||
const knobStyle = useMemo6(() => {
|
||
return {
|
||
height: KNOB_SIZE2,
|
||
width: KNOB_SIZE2,
|
||
borderRadius: KNOB_SIZE2 / 2,
|
||
position: "absolute",
|
||
top: VERTICAL_PADDING - KNOB_SIZE2 / 2 + 5 / 2,
|
||
backgroundColor: "white",
|
||
left: Math.max(0, frame / Math.max(1, durationInFrames - 1) * width - KNOB_SIZE2 / 2),
|
||
boxShadow: "0 0 2px black",
|
||
opacity: Number(barHovered || dragging.dragging)
|
||
};
|
||
}, [barHovered, dragging.dragging, durationInFrames, frame, width]);
|
||
const fillStyle = useMemo6(() => {
|
||
return {
|
||
height: BAR_HEIGHT2,
|
||
backgroundColor: "rgba(255, 255, 255, 1)",
|
||
width: (frame - (inFrame ?? 0)) / (durationInFrames - 1) * width,
|
||
marginLeft: (inFrame ?? 0) / (durationInFrames - 1) * width,
|
||
borderRadius: BAR_HEIGHT2 / 2
|
||
};
|
||
}, [durationInFrames, frame, inFrame, width]);
|
||
const active = useMemo6(() => {
|
||
return {
|
||
height: BAR_HEIGHT2,
|
||
backgroundColor: "rgba(255, 255, 255, 0.25)",
|
||
width: ((outFrame ?? durationInFrames - 1) - (inFrame ?? 0)) / (durationInFrames - 1) * 100 + "%",
|
||
marginLeft: (inFrame ?? 0) / (durationInFrames - 1) * 100 + "%",
|
||
borderRadius: BAR_HEIGHT2 / 2,
|
||
position: "absolute"
|
||
};
|
||
}, [durationInFrames, inFrame, outFrame]);
|
||
return /* @__PURE__ */ jsxs6("div", {
|
||
ref: containerRef,
|
||
onPointerDown,
|
||
style: containerStyle,
|
||
children: [
|
||
/* @__PURE__ */ jsxs6("div", {
|
||
style: barBackground,
|
||
children: [
|
||
/* @__PURE__ */ jsx8("div", {
|
||
style: active
|
||
}),
|
||
/* @__PURE__ */ jsx8("div", {
|
||
style: fillStyle
|
||
})
|
||
]
|
||
}),
|
||
/* @__PURE__ */ jsx8("div", {
|
||
style: knobStyle
|
||
})
|
||
]
|
||
});
|
||
};
|
||
|
||
// src/PlayerTimeLabel.tsx
|
||
import { useMemo as useMemo7 } from "react";
|
||
import { Internals as Internals10 } from "remotion";
|
||
|
||
// src/format-time.ts
|
||
var formatTime = (timeInSeconds) => {
|
||
const minutes = Math.floor(timeInSeconds / 60);
|
||
const seconds = Math.floor(timeInSeconds - minutes * 60);
|
||
return `${String(minutes)}:${String(seconds).padStart(2, "0")}`;
|
||
};
|
||
|
||
// src/PlayerTimeLabel.tsx
|
||
import { jsxs as jsxs7 } from "react/jsx-runtime";
|
||
var PlayerTimeLabel = ({ durationInFrames, maxTimeLabelWidth, fps }) => {
|
||
const frame = Internals10.Timeline.useTimelinePosition();
|
||
const timeLabel = useMemo7(() => {
|
||
return {
|
||
color: "white",
|
||
fontFamily: "sans-serif",
|
||
fontSize: 14,
|
||
maxWidth: maxTimeLabelWidth === null ? undefined : maxTimeLabelWidth,
|
||
overflow: "hidden",
|
||
textOverflow: "ellipsis"
|
||
};
|
||
}, [maxTimeLabelWidth]);
|
||
const isLastFrame = frame === durationInFrames - 1;
|
||
const frameToDisplay = isLastFrame ? frame + 1 : frame;
|
||
return /* @__PURE__ */ jsxs7("div", {
|
||
style: timeLabel,
|
||
children: [
|
||
formatTime(frameToDisplay / fps),
|
||
" / ",
|
||
formatTime(durationInFrames / fps)
|
||
]
|
||
});
|
||
};
|
||
|
||
// src/use-video-controls-resize.ts
|
||
import { useMemo as useMemo8 } from "react";
|
||
var X_SPACER = 10;
|
||
var X_PADDING = 12;
|
||
var useVideoControlsResize = ({
|
||
allowFullscreen: allowFullScreen,
|
||
playerWidth
|
||
}) => {
|
||
const resizeInfo = useMemo8(() => {
|
||
const playPauseIconSize = ICON_SIZE;
|
||
const volumeIconSize = ICON_SIZE;
|
||
const _fullscreenIconSize = allowFullScreen ? fullscreenIconSize : 0;
|
||
const elementsSize = volumeIconSize + playPauseIconSize + _fullscreenIconSize + X_PADDING * 2 + X_SPACER * 2;
|
||
const maxTimeLabelWidth = playerWidth - elementsSize;
|
||
const maxTimeLabelWidthWithoutNegativeValue = Math.max(maxTimeLabelWidth, 0);
|
||
const availableTimeLabelWidthIfVolumeOpen = maxTimeLabelWidthWithoutNegativeValue - VOLUME_SLIDER_WIDTH;
|
||
const computedLabelWidth = availableTimeLabelWidthIfVolumeOpen < VOLUME_SLIDER_WIDTH ? maxTimeLabelWidthWithoutNegativeValue : availableTimeLabelWidthIfVolumeOpen;
|
||
const minWidthForHorizontalDisplay = computedLabelWidth + elementsSize + VOLUME_SLIDER_WIDTH;
|
||
const displayVerticalVolumeSlider = playerWidth < minWidthForHorizontalDisplay;
|
||
return {
|
||
maxTimeLabelWidth: maxTimeLabelWidthWithoutNegativeValue === 0 ? null : maxTimeLabelWidthWithoutNegativeValue,
|
||
displayVerticalVolumeSlider
|
||
};
|
||
}, [allowFullScreen, playerWidth]);
|
||
return resizeInfo;
|
||
};
|
||
|
||
// src/PlayerControls.tsx
|
||
import { jsx as jsx9, jsxs as jsxs8, Fragment as Fragment2 } from "react/jsx-runtime";
|
||
var gradientSteps = [
|
||
0,
|
||
0.013,
|
||
0.049,
|
||
0.104,
|
||
0.175,
|
||
0.259,
|
||
0.352,
|
||
0.45,
|
||
0.55,
|
||
0.648,
|
||
0.741,
|
||
0.825,
|
||
0.896,
|
||
0.951,
|
||
0.987
|
||
];
|
||
var gradientOpacities = [
|
||
0,
|
||
8.1,
|
||
15.5,
|
||
22.5,
|
||
29,
|
||
35.3,
|
||
41.2,
|
||
47.1,
|
||
52.9,
|
||
58.8,
|
||
64.7,
|
||
71,
|
||
77.5,
|
||
84.5,
|
||
91.9
|
||
];
|
||
var globalGradientOpacity = 1 / 0.7;
|
||
var containerStyle2 = {
|
||
boxSizing: "border-box",
|
||
position: "absolute",
|
||
bottom: 0,
|
||
width: "100%",
|
||
paddingTop: 40,
|
||
paddingBottom: 10,
|
||
backgroundImage: `linear-gradient(to bottom,${gradientSteps.map((g, i) => {
|
||
return `hsla(0, 0%, 0%, ${g}) ${gradientOpacities[i] * globalGradientOpacity}%`;
|
||
}).join(", ")}, hsl(0, 0%, 0%) 100%)`,
|
||
backgroundSize: "auto 145px",
|
||
display: "flex",
|
||
paddingRight: X_PADDING,
|
||
paddingLeft: X_PADDING,
|
||
flexDirection: "column",
|
||
transition: "opacity 0.3s"
|
||
};
|
||
var controlsRow = {
|
||
display: "flex",
|
||
flexDirection: "row",
|
||
width: "100%",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
userSelect: "none",
|
||
WebkitUserSelect: "none"
|
||
};
|
||
var leftPartStyle = {
|
||
display: "flex",
|
||
flexDirection: "row",
|
||
userSelect: "none",
|
||
WebkitUserSelect: "none",
|
||
alignItems: "center"
|
||
};
|
||
var xSpacer = {
|
||
width: 12
|
||
};
|
||
var ySpacer = {
|
||
height: 8
|
||
};
|
||
var flex1 = {
|
||
flex: 1
|
||
};
|
||
var fullscreen = {};
|
||
var Controls = ({
|
||
durationInFrames,
|
||
isFullscreen,
|
||
fps,
|
||
showVolumeControls,
|
||
onFullscreenButtonClick,
|
||
allowFullscreen,
|
||
onExitFullscreenButtonClick,
|
||
spaceKeyToPlayOrPause,
|
||
onSeekEnd,
|
||
onSeekStart,
|
||
inFrame,
|
||
outFrame,
|
||
initiallyShowControls,
|
||
canvasSize,
|
||
renderPlayPauseButton,
|
||
renderFullscreenButton,
|
||
alwaysShowControls,
|
||
showPlaybackRateControl,
|
||
containerRef,
|
||
buffering,
|
||
hideControlsWhenPointerDoesntMove,
|
||
onPointerDown,
|
||
onDoubleClick,
|
||
renderMuteButton,
|
||
renderVolumeSlider,
|
||
playing,
|
||
toggle,
|
||
renderCustomControls
|
||
}) => {
|
||
const playButtonRef = useRef8(null);
|
||
const [supportsFullscreen, setSupportsFullscreen] = useState10(false);
|
||
const hovered = useHoverState(containerRef, hideControlsWhenPointerDoesntMove);
|
||
const { maxTimeLabelWidth, displayVerticalVolumeSlider } = useVideoControlsResize({
|
||
allowFullscreen,
|
||
playerWidth: canvasSize?.width ?? 0
|
||
});
|
||
const [shouldShowInitially, setInitiallyShowControls] = useState10(() => {
|
||
if (typeof initiallyShowControls === "boolean") {
|
||
return initiallyShowControls;
|
||
}
|
||
if (typeof initiallyShowControls === "number") {
|
||
if (initiallyShowControls % 1 !== 0) {
|
||
throw new Error("initiallyShowControls must be an integer or a boolean");
|
||
}
|
||
if (Number.isNaN(initiallyShowControls)) {
|
||
throw new Error("initiallyShowControls must not be NaN");
|
||
}
|
||
if (!Number.isFinite(initiallyShowControls)) {
|
||
throw new Error("initiallyShowControls must be finite");
|
||
}
|
||
if (initiallyShowControls <= 0) {
|
||
throw new Error("initiallyShowControls must be a positive integer");
|
||
}
|
||
return initiallyShowControls;
|
||
}
|
||
throw new TypeError("initiallyShowControls must be a number or a boolean");
|
||
});
|
||
const containerCss = useMemo9(() => {
|
||
const shouldShow = hovered || !playing || shouldShowInitially || alwaysShowControls;
|
||
return {
|
||
...containerStyle2,
|
||
opacity: Number(shouldShow)
|
||
};
|
||
}, [hovered, shouldShowInitially, playing, alwaysShowControls]);
|
||
useEffect10(() => {
|
||
if (playButtonRef.current && spaceKeyToPlayOrPause) {
|
||
playButtonRef.current.focus({
|
||
preventScroll: true
|
||
});
|
||
}
|
||
}, [playing, spaceKeyToPlayOrPause]);
|
||
useEffect10(() => {
|
||
setSupportsFullscreen((typeof document !== "undefined" && (document.fullscreenEnabled || document.webkitFullscreenEnabled)) ?? false);
|
||
}, []);
|
||
useEffect10(() => {
|
||
if (shouldShowInitially === false) {
|
||
return;
|
||
}
|
||
const time = shouldShowInitially === true ? 2000 : shouldShowInitially;
|
||
const timeout = setTimeout(() => {
|
||
setInitiallyShowControls(false);
|
||
}, time);
|
||
return () => {
|
||
clearInterval(timeout);
|
||
};
|
||
}, [shouldShowInitially]);
|
||
const playbackRates = useMemo9(() => {
|
||
if (showPlaybackRateControl === true) {
|
||
return [0.5, 0.8, 1, 1.2, 1.5, 1.8, 2, 2.5, 3];
|
||
}
|
||
if (Array.isArray(showPlaybackRateControl)) {
|
||
for (const rate of showPlaybackRateControl) {
|
||
if (typeof rate !== "number") {
|
||
throw new Error("Every item in showPlaybackRateControl must be a number");
|
||
}
|
||
if (rate <= 0) {
|
||
throw new Error("Every item in showPlaybackRateControl must be positive");
|
||
}
|
||
}
|
||
return showPlaybackRateControl;
|
||
}
|
||
return null;
|
||
}, [showPlaybackRateControl]);
|
||
const customControlsElement = renderCustomControls ? renderCustomControls() : null;
|
||
const ref = useRef8(null);
|
||
const flexRef = useRef8(null);
|
||
const onPointerDownIfContainer = useCallback8((e) => {
|
||
if (e.target === ref.current || e.target === flexRef.current) {
|
||
onPointerDown?.(e);
|
||
}
|
||
}, [onPointerDown]);
|
||
const onDoubleClickIfContainer = useCallback8((e) => {
|
||
if (e.target === ref.current || e.target === flexRef.current) {
|
||
onDoubleClick?.(e);
|
||
}
|
||
}, [onDoubleClick]);
|
||
return /* @__PURE__ */ jsxs8("div", {
|
||
ref,
|
||
style: containerCss,
|
||
onPointerDown: onPointerDownIfContainer,
|
||
onDoubleClick: onDoubleClickIfContainer,
|
||
children: [
|
||
/* @__PURE__ */ jsxs8("div", {
|
||
ref: flexRef,
|
||
style: controlsRow,
|
||
children: [
|
||
/* @__PURE__ */ jsxs8("div", {
|
||
style: leftPartStyle,
|
||
children: [
|
||
/* @__PURE__ */ jsx9("button", {
|
||
ref: playButtonRef,
|
||
type: "button",
|
||
style: playerButtonStyle,
|
||
onClick: toggle,
|
||
"aria-label": playing ? "Pause video" : "Play video",
|
||
title: playing ? "Pause video" : "Play video",
|
||
children: renderPlayPauseButton === null ? /* @__PURE__ */ jsx9(DefaultPlayPauseButton, {
|
||
buffering,
|
||
playing
|
||
}) : renderPlayPauseButton({
|
||
playing,
|
||
isBuffering: buffering
|
||
}) ?? /* @__PURE__ */ jsx9(DefaultPlayPauseButton, {
|
||
buffering,
|
||
playing
|
||
})
|
||
}),
|
||
showVolumeControls ? /* @__PURE__ */ jsxs8(Fragment2, {
|
||
children: [
|
||
/* @__PURE__ */ jsx9("div", {
|
||
style: xSpacer
|
||
}),
|
||
/* @__PURE__ */ jsx9(MediaVolumeSlider, {
|
||
renderMuteButton,
|
||
renderVolumeSlider,
|
||
displayVerticalVolumeSlider
|
||
})
|
||
]
|
||
}) : null,
|
||
/* @__PURE__ */ jsx9("div", {
|
||
style: xSpacer
|
||
}),
|
||
/* @__PURE__ */ jsx9(PlayerTimeLabel, {
|
||
durationInFrames,
|
||
fps,
|
||
maxTimeLabelWidth
|
||
}),
|
||
/* @__PURE__ */ jsx9("div", {
|
||
style: xSpacer
|
||
})
|
||
]
|
||
}),
|
||
/* @__PURE__ */ jsx9("div", {
|
||
style: flex1
|
||
}),
|
||
customControlsElement,
|
||
customControlsElement && playbackRates && canvasSize ? /* @__PURE__ */ jsx9("div", {
|
||
style: xSpacer
|
||
}) : null,
|
||
playbackRates && canvasSize && /* @__PURE__ */ jsx9(PlaybackrateControl, {
|
||
canvasSize,
|
||
playbackRates
|
||
}),
|
||
playbackRates && supportsFullscreen && allowFullscreen ? /* @__PURE__ */ jsx9("div", {
|
||
style: xSpacer
|
||
}) : null,
|
||
/* @__PURE__ */ jsx9("div", {
|
||
style: fullscreen,
|
||
children: supportsFullscreen && allowFullscreen ? /* @__PURE__ */ jsx9("button", {
|
||
type: "button",
|
||
"aria-label": isFullscreen ? "Exit fullscreen" : "Enter Fullscreen",
|
||
title: isFullscreen ? "Exit fullscreen" : "Enter Fullscreen",
|
||
style: playerButtonStyle,
|
||
onClick: isFullscreen ? onExitFullscreenButtonClick : onFullscreenButtonClick,
|
||
children: renderFullscreenButton === null ? /* @__PURE__ */ jsx9(FullscreenIcon, {
|
||
isFullscreen
|
||
}) : renderFullscreenButton({ isFullscreen })
|
||
}) : null
|
||
})
|
||
]
|
||
}),
|
||
/* @__PURE__ */ jsx9("div", {
|
||
style: ySpacer
|
||
}),
|
||
/* @__PURE__ */ jsx9(PlayerSeekBar, {
|
||
onSeekEnd,
|
||
onSeekStart,
|
||
durationInFrames,
|
||
inFrame,
|
||
outFrame
|
||
})
|
||
]
|
||
});
|
||
};
|
||
|
||
// src/error-boundary.tsx
|
||
import React8 from "react";
|
||
import { jsx as jsx10 } from "react/jsx-runtime";
|
||
var errorStyle = {
|
||
display: "flex",
|
||
justifyContent: "center",
|
||
alignItems: "center",
|
||
flex: 1,
|
||
height: "100%",
|
||
width: "100%"
|
||
};
|
||
|
||
class ErrorBoundary extends React8.Component {
|
||
state = { hasError: null };
|
||
static getDerivedStateFromError(error) {
|
||
return { hasError: error };
|
||
}
|
||
componentDidCatch(error) {
|
||
this.props.onError(error);
|
||
}
|
||
render() {
|
||
if (this.state.hasError) {
|
||
return /* @__PURE__ */ jsx10("div", {
|
||
style: errorStyle,
|
||
children: this.props.errorFallback({
|
||
error: this.state.hasError
|
||
})
|
||
});
|
||
}
|
||
return this.props.children;
|
||
}
|
||
}
|
||
|
||
// src/license-blacklist.tsx
|
||
import React9, { useEffect as useEffect11 } from "react";
|
||
import { jsx as jsx11 } from "react/jsx-runtime";
|
||
var getHashOfDomain = async () => {
|
||
if (typeof window === "undefined") {
|
||
return null;
|
||
}
|
||
if (typeof window.crypto === "undefined") {
|
||
return null;
|
||
}
|
||
if (typeof window.crypto.subtle === "undefined") {
|
||
return null;
|
||
}
|
||
try {
|
||
const hashBuffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(window.location.hostname));
|
||
return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
||
} catch {
|
||
return null;
|
||
}
|
||
};
|
||
var style = {
|
||
backgroundColor: "red",
|
||
position: "absolute",
|
||
padding: 12,
|
||
fontFamily: "Arial"
|
||
};
|
||
var DOMAIN_BLACKLIST = [
|
||
"28d262b44cc61fa750f1686b16ad0604dabfe193fbc263eec05c89b7ad4c2cd6",
|
||
"4db1b0a94be33165dfefcb3ba03d04c7a2666dd27c496d3dc9fa41858e94925e",
|
||
"fbc48530bbf245da790f63675e84e06bab38c3b114fab07eb350025119922bdc",
|
||
"7baf10a8932757b1b3a22b3fce10a048747ac2f8eaf638603487e3705b07eb83",
|
||
"8a6c21a598d8c667272b5207c051b85997bf5b45d5fb712378be3f27cd72c6a6",
|
||
"a2f7aaac9c50a9255e7fc376110c4e0bfe153722dc66ed3c5d3bf2a135f65518"
|
||
];
|
||
var ran = false;
|
||
var RenderWarningIfBlacklist = () => {
|
||
const [unlicensed, setUnlicensed] = React9.useState(false);
|
||
useEffect11(() => {
|
||
if (ran) {
|
||
return;
|
||
}
|
||
ran = true;
|
||
getHashOfDomain().then((hash) => {
|
||
if (hash && DOMAIN_BLACKLIST.includes(hash)) {
|
||
setUnlicensed(true);
|
||
}
|
||
}).catch(() => {});
|
||
}, []);
|
||
useEffect11(() => {
|
||
if (!unlicensed) {
|
||
return;
|
||
}
|
||
const ensureBanner = () => {
|
||
const banner = document.querySelector(".warning-banner");
|
||
if (!banner) {
|
||
const div = document.createElement("div");
|
||
div.className = "warning-banner";
|
||
Object.assign(div.style, style, {
|
||
zIndex: "9999",
|
||
cssText: `${style.cssText} !important;`
|
||
});
|
||
div.innerHTML = `
|
||
<a href="https://github.com/remotion-dev/remotion/pull/4589" style="color: white;">
|
||
Remotion Unlicensed – Contact hi@remotion.dev
|
||
</a>
|
||
`;
|
||
document.body.appendChild(div);
|
||
}
|
||
};
|
||
const observer = new MutationObserver(() => ensureBanner());
|
||
observer.observe(document.body, { childList: true, subtree: true });
|
||
return () => {
|
||
observer.disconnect();
|
||
};
|
||
}, [unlicensed]);
|
||
if (!unlicensed) {
|
||
return null;
|
||
}
|
||
return /* @__PURE__ */ jsx11("div", {
|
||
style,
|
||
className: "warning-banner",
|
||
children: /* @__PURE__ */ jsx11("a", {
|
||
style: { color: "white" },
|
||
href: "https://github.com/remotion-dev/remotion/pull/4589",
|
||
children: "Remotion Unlicensed – Contact hi@remotion.dev"
|
||
})
|
||
});
|
||
};
|
||
|
||
// src/player-css-classname.ts
|
||
var playerCssClassname = (override) => {
|
||
return override ?? "__remotion-player";
|
||
};
|
||
|
||
// src/utils/is-node.ts
|
||
var IS_NODE = typeof document === "undefined";
|
||
|
||
// src/utils/use-click-prevention-on-double-click.ts
|
||
import { useCallback as useCallback10, useMemo as useMemo11 } from "react";
|
||
|
||
// src/utils/cancellable-promise.ts
|
||
var cancellablePromise = (promise) => {
|
||
let isCanceled = false;
|
||
const wrappedPromise = new Promise((resolve, reject) => {
|
||
promise.then((value) => {
|
||
if (isCanceled) {
|
||
reject({ isCanceled, value });
|
||
return;
|
||
}
|
||
resolve(value);
|
||
}).catch((error) => {
|
||
reject({ isCanceled, error });
|
||
});
|
||
});
|
||
return {
|
||
promise: wrappedPromise,
|
||
cancel: () => {
|
||
isCanceled = true;
|
||
}
|
||
};
|
||
};
|
||
|
||
// src/utils/delay.ts
|
||
var delay = (n) => new Promise((resolve) => setTimeout(resolve, n));
|
||
|
||
// src/utils/use-cancellable-promises.ts
|
||
import { useCallback as useCallback9, useMemo as useMemo10, useRef as useRef9 } from "react";
|
||
var useCancellablePromises = () => {
|
||
const pendingPromises = useRef9([]);
|
||
const appendPendingPromise = useCallback9((promise) => {
|
||
pendingPromises.current = [...pendingPromises.current, promise];
|
||
}, []);
|
||
const removePendingPromise = useCallback9((promise) => {
|
||
pendingPromises.current = pendingPromises.current.filter((p) => p !== promise);
|
||
}, []);
|
||
const clearPendingPromises = useCallback9(() => pendingPromises.current.map((p) => p.cancel()), []);
|
||
const api = useMemo10(() => ({
|
||
appendPendingPromise,
|
||
removePendingPromise,
|
||
clearPendingPromises
|
||
}), [appendPendingPromise, clearPendingPromises, removePendingPromise]);
|
||
return api;
|
||
};
|
||
|
||
// src/utils/use-click-prevention-on-double-click.ts
|
||
var useClickPreventionOnDoubleClick = (onClick, onDoubleClick, doubleClickToFullscreen) => {
|
||
const api = useCancellablePromises();
|
||
const handleClick = useCallback10(async (e) => {
|
||
if (e instanceof PointerEvent ? e.pointerType === "touch" : e.nativeEvent.pointerType === "touch") {
|
||
onClick(e);
|
||
return;
|
||
}
|
||
api.clearPendingPromises();
|
||
const waitForClick = cancellablePromise(delay(200));
|
||
api.appendPendingPromise(waitForClick);
|
||
try {
|
||
await waitForClick.promise;
|
||
api.removePendingPromise(waitForClick);
|
||
onClick(e);
|
||
} catch (errorInfo) {
|
||
const info = errorInfo;
|
||
api.removePendingPromise(waitForClick);
|
||
if (!info.isCanceled) {
|
||
throw info.error;
|
||
}
|
||
}
|
||
}, [api, onClick]);
|
||
const handlePointerDown = useCallback10(() => {
|
||
document.addEventListener("pointerup", (newEvt) => {
|
||
handleClick(newEvt);
|
||
}, {
|
||
once: true
|
||
});
|
||
}, [handleClick]);
|
||
const handleDoubleClick = useCallback10(() => {
|
||
api.clearPendingPromises();
|
||
onDoubleClick();
|
||
}, [api, onDoubleClick]);
|
||
const returnValue = useMemo11(() => {
|
||
if (!doubleClickToFullscreen) {
|
||
return { handlePointerDown: onClick, handleDoubleClick: () => {
|
||
return;
|
||
} };
|
||
}
|
||
return { handlePointerDown, handleDoubleClick };
|
||
}, [doubleClickToFullscreen, handleDoubleClick, handlePointerDown, onClick]);
|
||
return returnValue;
|
||
};
|
||
|
||
// src/PlayerUI.tsx
|
||
import { jsx as jsx12, jsxs as jsxs9, Fragment as Fragment3 } from "react/jsx-runtime";
|
||
var reactVersion = React10.version.split(".")[0];
|
||
if (reactVersion === "0") {
|
||
throw new Error(`Version ${reactVersion} of "react" is not supported by Remotion`);
|
||
}
|
||
var doesReactVersionSupportSuspense = parseInt(reactVersion, 10) >= 18;
|
||
var PlayerUI = ({
|
||
controls,
|
||
style: style2,
|
||
loop,
|
||
autoPlay,
|
||
allowFullscreen,
|
||
inputProps,
|
||
clickToPlay,
|
||
showVolumeControls,
|
||
doubleClickToFullscreen,
|
||
spaceKeyToPlayOrPause,
|
||
errorFallback,
|
||
playbackRate,
|
||
renderLoading,
|
||
renderPoster,
|
||
className: className2,
|
||
moveToBeginningWhenEnded,
|
||
showPosterWhenUnplayed,
|
||
showPosterWhenEnded,
|
||
showPosterWhenPaused,
|
||
showPosterWhenBuffering,
|
||
showPosterWhenBufferingAndPaused,
|
||
inFrame,
|
||
outFrame,
|
||
initiallyShowControls,
|
||
renderFullscreen: renderFullscreenButton,
|
||
renderPlayPauseButton,
|
||
renderMuteButton,
|
||
renderVolumeSlider,
|
||
renderCustomControls,
|
||
alwaysShowControls,
|
||
showPlaybackRateControl,
|
||
posterFillMode,
|
||
bufferStateDelayInMilliseconds,
|
||
hideControlsWhenPointerDoesntMove,
|
||
overflowVisible,
|
||
browserMediaControlsBehavior,
|
||
overrideInternalClassName,
|
||
noSuspense
|
||
}, ref) => {
|
||
const config = Internals11.useUnsafeVideoConfig();
|
||
const video = Internals11.useVideo();
|
||
const container = useRef10(null);
|
||
const canvasSize = useElementSize(container, {
|
||
triggerOnWindowResize: false,
|
||
shouldApplyCssTransforms: false
|
||
});
|
||
const [hasPausedToResume, setHasPausedToResume] = useState11(false);
|
||
const [shouldAutoplay, setShouldAutoPlay] = useState11(autoPlay);
|
||
const [isFullscreen, setIsFullscreen] = useState11(() => false);
|
||
const [seeking, setSeeking] = useState11(false);
|
||
const supportsFullScreen = useMemo12(() => {
|
||
if (typeof document === "undefined") {
|
||
return false;
|
||
}
|
||
return Boolean(document.fullscreenEnabled || document.webkitFullscreenEnabled);
|
||
}, []);
|
||
const player = usePlayer();
|
||
const playerToggle = player.toggle;
|
||
usePlayback({
|
||
loop,
|
||
playbackRate,
|
||
moveToBeginningWhenEnded,
|
||
inFrame,
|
||
outFrame,
|
||
getCurrentFrame: player.getCurrentFrame,
|
||
browserMediaControlsBehavior
|
||
});
|
||
useEffect12(() => {
|
||
if (hasPausedToResume && !player.playing) {
|
||
setHasPausedToResume(false);
|
||
player.play();
|
||
}
|
||
}, [hasPausedToResume, player]);
|
||
useEffect12(() => {
|
||
const { current } = container;
|
||
if (!current) {
|
||
return;
|
||
}
|
||
const onFullscreenChange = () => {
|
||
const newValue = document.fullscreenElement === current || document.webkitFullscreenElement === current;
|
||
setIsFullscreen(newValue);
|
||
};
|
||
document.addEventListener("fullscreenchange", onFullscreenChange);
|
||
document.addEventListener("webkitfullscreenchange", onFullscreenChange);
|
||
return () => {
|
||
document.removeEventListener("fullscreenchange", onFullscreenChange);
|
||
document.removeEventListener("webkitfullscreenchange", onFullscreenChange);
|
||
};
|
||
}, []);
|
||
const toggle = useCallback11((e) => {
|
||
playerToggle(e);
|
||
}, [playerToggle]);
|
||
const requestFullscreen = useCallback11(() => {
|
||
if (!allowFullscreen) {
|
||
throw new Error("allowFullscreen is false");
|
||
}
|
||
if (!supportsFullScreen) {
|
||
throw new Error("Browser doesnt support fullscreen");
|
||
}
|
||
if (!container.current) {
|
||
throw new Error("No player ref found");
|
||
}
|
||
if (container.current.webkitRequestFullScreen) {
|
||
container.current.webkitRequestFullScreen();
|
||
} else {
|
||
container.current.requestFullscreen();
|
||
}
|
||
}, [allowFullscreen, supportsFullScreen]);
|
||
const exitFullscreen = useCallback11(() => {
|
||
if (document.webkitExitFullscreen) {
|
||
document.webkitExitFullscreen();
|
||
} else {
|
||
document.exitFullscreen();
|
||
}
|
||
}, []);
|
||
useEffect12(() => {
|
||
const { current } = container;
|
||
if (!current) {
|
||
return;
|
||
}
|
||
const fullscreenChange = () => {
|
||
const element = document.webkitFullscreenElement ?? document.fullscreenElement;
|
||
if (element && element === container.current) {
|
||
player.emitter.dispatchFullscreenChange({
|
||
isFullscreen: true
|
||
});
|
||
} else {
|
||
player.emitter.dispatchFullscreenChange({
|
||
isFullscreen: false
|
||
});
|
||
}
|
||
};
|
||
current.addEventListener("webkitfullscreenchange", fullscreenChange);
|
||
current.addEventListener("fullscreenchange", fullscreenChange);
|
||
return () => {
|
||
current.removeEventListener("webkitfullscreenchange", fullscreenChange);
|
||
current.removeEventListener("fullscreenchange", fullscreenChange);
|
||
};
|
||
}, [player.emitter]);
|
||
const durationInFrames = config?.durationInFrames ?? 1;
|
||
const layout = useMemo12(() => {
|
||
if (!config || !canvasSize) {
|
||
return null;
|
||
}
|
||
return calculateCanvasTransformation({
|
||
canvasSize,
|
||
compositionHeight: config.height,
|
||
compositionWidth: config.width,
|
||
previewSize: "auto"
|
||
});
|
||
}, [canvasSize, config]);
|
||
const scale = layout?.scale ?? 1;
|
||
const initialScaleIgnored = useRef10(false);
|
||
useEffect12(() => {
|
||
if (!initialScaleIgnored.current) {
|
||
initialScaleIgnored.current = true;
|
||
return;
|
||
}
|
||
player.emitter.dispatchScaleChange(scale);
|
||
}, [player.emitter, scale]);
|
||
const { setMediaVolume, setMediaMuted } = useContext6(Internals11.SetMediaVolumeContext);
|
||
const { mediaMuted, mediaVolume } = useContext6(Internals11.MediaVolumeContext);
|
||
useEffect12(() => {
|
||
player.emitter.dispatchVolumeChange(mediaVolume);
|
||
}, [player.emitter, mediaVolume]);
|
||
const isMuted = mediaMuted || mediaVolume === 0;
|
||
useEffect12(() => {
|
||
player.emitter.dispatchMuteChange({
|
||
isMuted
|
||
});
|
||
}, [player.emitter, isMuted]);
|
||
const [showBufferIndicator, setShowBufferState] = useState11(false);
|
||
useEffect12(() => {
|
||
let timeout = null;
|
||
let stopped = false;
|
||
const onBuffer = () => {
|
||
stopped = false;
|
||
requestAnimationFrame(() => {
|
||
if (bufferStateDelayInMilliseconds === 0) {
|
||
setShowBufferState(true);
|
||
} else {
|
||
timeout = setTimeout(() => {
|
||
if (!stopped) {
|
||
setShowBufferState(true);
|
||
}
|
||
}, bufferStateDelayInMilliseconds);
|
||
}
|
||
});
|
||
};
|
||
const onResume = () => {
|
||
requestAnimationFrame(() => {
|
||
stopped = true;
|
||
setShowBufferState(false);
|
||
if (timeout) {
|
||
clearTimeout(timeout);
|
||
}
|
||
});
|
||
};
|
||
player.emitter.addEventListener("waiting", onBuffer);
|
||
player.emitter.addEventListener("resume", onResume);
|
||
return () => {
|
||
player.emitter.removeEventListener("waiting", onBuffer);
|
||
player.emitter.removeEventListener("resume", onResume);
|
||
setShowBufferState(false);
|
||
if (timeout) {
|
||
clearTimeout(timeout);
|
||
}
|
||
stopped = true;
|
||
};
|
||
}, [bufferStateDelayInMilliseconds, player.emitter]);
|
||
useImperativeHandle(ref, () => {
|
||
const methods = {
|
||
play: player.play,
|
||
pause: () => {
|
||
setHasPausedToResume(false);
|
||
player.pause();
|
||
},
|
||
toggle,
|
||
getContainerNode: () => container.current,
|
||
getCurrentFrame: player.getCurrentFrame,
|
||
isPlaying: player.isPlaying,
|
||
seekTo: (f) => {
|
||
const lastFrame = durationInFrames - 1;
|
||
const frameToSeekTo = Math.max(0, Math.min(lastFrame, f));
|
||
if (player.isPlaying()) {
|
||
const pauseToResume = frameToSeekTo !== lastFrame || loop;
|
||
setHasPausedToResume(pauseToResume);
|
||
player.pause();
|
||
}
|
||
if (frameToSeekTo === lastFrame && !loop) {
|
||
player.emitter.dispatchEnded();
|
||
}
|
||
player.seek(frameToSeekTo);
|
||
},
|
||
isFullscreen: () => {
|
||
const { current } = container;
|
||
if (!current) {
|
||
return false;
|
||
}
|
||
return document.fullscreenElement === current || document.webkitFullscreenElement === current;
|
||
},
|
||
requestFullscreen,
|
||
exitFullscreen,
|
||
getVolume: () => {
|
||
if (mediaMuted) {
|
||
return 0;
|
||
}
|
||
return mediaVolume;
|
||
},
|
||
setVolume: (vol) => {
|
||
if (typeof vol !== "number") {
|
||
throw new TypeError(`setVolume() takes a number, got value of type ${typeof vol}`);
|
||
}
|
||
if (isNaN(vol)) {
|
||
throw new TypeError(`setVolume() got a number that is NaN. Volume must be between 0 and 1.`);
|
||
}
|
||
if (vol < 0 || vol > 1) {
|
||
throw new TypeError(`setVolume() got a number that is out of range. Must be between 0 and 1, got ${typeof vol}`);
|
||
}
|
||
setMediaVolume(vol);
|
||
},
|
||
isMuted: () => isMuted,
|
||
mute: () => {
|
||
setMediaMuted(true);
|
||
},
|
||
unmute: () => {
|
||
setMediaMuted(false);
|
||
},
|
||
getScale: () => scale,
|
||
pauseAndReturnToPlayStart: () => {
|
||
player.pauseAndReturnToPlayStart();
|
||
}
|
||
};
|
||
return Object.assign(player.emitter, methods);
|
||
}, [
|
||
durationInFrames,
|
||
exitFullscreen,
|
||
loop,
|
||
mediaMuted,
|
||
isMuted,
|
||
mediaVolume,
|
||
player,
|
||
requestFullscreen,
|
||
setMediaMuted,
|
||
setMediaVolume,
|
||
toggle,
|
||
scale
|
||
]);
|
||
const VideoComponent = video ? video.component : null;
|
||
const outerStyle = useMemo12(() => {
|
||
return calculateOuterStyle({
|
||
canvasSize,
|
||
config,
|
||
style: style2,
|
||
overflowVisible,
|
||
layout
|
||
});
|
||
}, [canvasSize, config, layout, overflowVisible, style2]);
|
||
const outer = useMemo12(() => {
|
||
return calculateOuter({ config, layout, scale, overflowVisible });
|
||
}, [config, layout, overflowVisible, scale]);
|
||
const containerStyle3 = useMemo12(() => {
|
||
return calculateContainerStyle({
|
||
config,
|
||
layout,
|
||
scale,
|
||
overflowVisible
|
||
});
|
||
}, [config, layout, overflowVisible, scale]);
|
||
const playerPause = player.pause;
|
||
const playerDispatchError = player.emitter.dispatchError;
|
||
const onError = useCallback11((error) => {
|
||
playerPause();
|
||
playerDispatchError(error);
|
||
}, [playerDispatchError, playerPause]);
|
||
const onFullscreenButtonClick = useCallback11((e) => {
|
||
e.stopPropagation();
|
||
requestFullscreen();
|
||
}, [requestFullscreen]);
|
||
const onExitFullscreenButtonClick = useCallback11((e) => {
|
||
e.stopPropagation();
|
||
exitFullscreen();
|
||
}, [exitFullscreen]);
|
||
const onSingleClick = useCallback11((e) => {
|
||
const rightClick = e instanceof MouseEvent ? e.button === 2 : e.nativeEvent.button;
|
||
if (rightClick) {
|
||
return;
|
||
}
|
||
toggle(e);
|
||
}, [toggle]);
|
||
const onSeekStart = useCallback11(() => {
|
||
setSeeking(true);
|
||
}, []);
|
||
const onSeekEnd = useCallback11(() => {
|
||
setSeeking(false);
|
||
}, []);
|
||
const onDoubleClick = useCallback11(() => {
|
||
if (isFullscreen) {
|
||
exitFullscreen();
|
||
} else {
|
||
requestFullscreen();
|
||
}
|
||
}, [exitFullscreen, isFullscreen, requestFullscreen]);
|
||
const { handlePointerDown, handleDoubleClick } = useClickPreventionOnDoubleClick(onSingleClick, onDoubleClick, doubleClickToFullscreen && allowFullscreen && supportsFullScreen);
|
||
useEffect12(() => {
|
||
if (shouldAutoplay) {
|
||
player.play();
|
||
setShouldAutoPlay(false);
|
||
}
|
||
}, [shouldAutoplay, player]);
|
||
const loadingMarkup = useMemo12(() => {
|
||
return renderLoading ? renderLoading({
|
||
height: outerStyle.height,
|
||
width: outerStyle.width,
|
||
isBuffering: showBufferIndicator
|
||
}) : null;
|
||
}, [outerStyle.height, outerStyle.width, renderLoading, showBufferIndicator]);
|
||
const currentScale = useMemo12(() => {
|
||
return {
|
||
type: "scale",
|
||
scale
|
||
};
|
||
}, [scale]);
|
||
if (!config) {
|
||
return null;
|
||
}
|
||
const poster = renderPoster ? renderPoster({
|
||
height: posterFillMode === "player-size" ? outerStyle.height : config.height,
|
||
width: posterFillMode === "player-size" ? outerStyle.width : config.width,
|
||
isBuffering: showBufferIndicator
|
||
}) : null;
|
||
if (poster === undefined) {
|
||
throw new TypeError("renderPoster() must return a React element, but undefined was returned");
|
||
}
|
||
const shouldShowPoster = poster && [
|
||
showPosterWhenPaused && !player.isPlaying() && !seeking,
|
||
showPosterWhenEnded && player.isLastFrame && !player.isPlaying(),
|
||
showPosterWhenUnplayed && !player.hasPlayed && !player.isPlaying(),
|
||
showPosterWhenBuffering && showBufferIndicator && player.isPlaying(),
|
||
showPosterWhenBufferingAndPaused && showBufferIndicator && !player.isPlaying()
|
||
].some(Boolean);
|
||
const { left, top, width, height, ...outerWithoutScale } = outer;
|
||
const content = /* @__PURE__ */ jsxs9(Fragment3, {
|
||
children: [
|
||
/* @__PURE__ */ jsxs9("div", {
|
||
style: outer,
|
||
onPointerDown: clickToPlay ? handlePointerDown : undefined,
|
||
onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined,
|
||
children: [
|
||
/* @__PURE__ */ jsxs9("div", {
|
||
style: containerStyle3,
|
||
className: playerCssClassname(overrideInternalClassName),
|
||
children: [
|
||
VideoComponent ? /* @__PURE__ */ jsx12(ErrorBoundary, {
|
||
onError,
|
||
errorFallback,
|
||
children: /* @__PURE__ */ jsx12(Internals11.CurrentScaleContext.Provider, {
|
||
value: currentScale,
|
||
children: /* @__PURE__ */ jsx12(VideoComponent, {
|
||
...video?.props ?? {},
|
||
...inputProps ?? {}
|
||
})
|
||
})
|
||
}) : null,
|
||
shouldShowPoster && posterFillMode === "composition-size" ? /* @__PURE__ */ jsx12("div", {
|
||
style: {
|
||
...outerWithoutScale,
|
||
width: config.width,
|
||
height: config.height
|
||
},
|
||
onPointerDown: clickToPlay ? handlePointerDown : undefined,
|
||
onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined,
|
||
children: poster
|
||
}) : null
|
||
]
|
||
}),
|
||
/* @__PURE__ */ jsx12(RenderWarningIfBlacklist, {})
|
||
]
|
||
}),
|
||
shouldShowPoster && posterFillMode === "player-size" ? /* @__PURE__ */ jsx12("div", {
|
||
style: outer,
|
||
onPointerDown: clickToPlay ? handlePointerDown : undefined,
|
||
onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined,
|
||
children: poster
|
||
}) : null,
|
||
controls ? /* @__PURE__ */ jsx12(Controls, {
|
||
fps: config.fps,
|
||
playing: player.playing,
|
||
toggle: player.toggle,
|
||
durationInFrames: config.durationInFrames,
|
||
containerRef: container,
|
||
onFullscreenButtonClick,
|
||
isFullscreen,
|
||
allowFullscreen,
|
||
showVolumeControls,
|
||
onExitFullscreenButtonClick,
|
||
spaceKeyToPlayOrPause,
|
||
onSeekEnd,
|
||
onSeekStart,
|
||
inFrame,
|
||
outFrame,
|
||
initiallyShowControls,
|
||
canvasSize,
|
||
renderFullscreenButton,
|
||
renderPlayPauseButton,
|
||
alwaysShowControls,
|
||
showPlaybackRateControl,
|
||
buffering: showBufferIndicator,
|
||
hideControlsWhenPointerDoesntMove,
|
||
onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined,
|
||
onPointerDown: clickToPlay ? handlePointerDown : undefined,
|
||
renderMuteButton,
|
||
renderVolumeSlider,
|
||
renderCustomControls
|
||
}) : null
|
||
]
|
||
});
|
||
if (noSuspense || IS_NODE && !doesReactVersionSupportSuspense) {
|
||
return /* @__PURE__ */ jsx12("div", {
|
||
ref: container,
|
||
style: outerStyle,
|
||
className: className2,
|
||
children: content
|
||
});
|
||
}
|
||
return /* @__PURE__ */ jsx12("div", {
|
||
ref: container,
|
||
style: outerStyle,
|
||
className: className2,
|
||
children: /* @__PURE__ */ jsx12(Suspense, {
|
||
fallback: loadingMarkup,
|
||
children: content
|
||
})
|
||
});
|
||
};
|
||
var PlayerUI_default = forwardRef(PlayerUI);
|
||
|
||
// src/SharedPlayerContext.tsx
|
||
import { useCallback as useCallback12, useMemo as useMemo13, useState as useState12 } from "react";
|
||
import { Internals as Internals13 } from "remotion";
|
||
|
||
// src/volume-persistance.ts
|
||
import { Internals as Internals12 } from "remotion";
|
||
var DEFAULT_VOLUME_PERSISTANCE_KEY = "remotion.volumePreference";
|
||
var persistVolume = (volume, logLevel, volumePersistenceKey) => {
|
||
if (typeof window === "undefined") {
|
||
return;
|
||
}
|
||
try {
|
||
window.localStorage.setItem(volumePersistenceKey ?? DEFAULT_VOLUME_PERSISTANCE_KEY, String(volume));
|
||
} catch (e) {
|
||
Internals12.Log.error({ logLevel, tag: null }, "Could not persist volume", e);
|
||
}
|
||
};
|
||
var getPreferredVolume = (volumePersistenceKey) => {
|
||
if (typeof window === "undefined") {
|
||
return 1;
|
||
}
|
||
try {
|
||
const val = window.localStorage.getItem(volumePersistenceKey ?? DEFAULT_VOLUME_PERSISTANCE_KEY);
|
||
return val ? Number(val) : 1;
|
||
} catch {
|
||
return 1;
|
||
}
|
||
};
|
||
|
||
// src/SharedPlayerContext.tsx
|
||
import { jsx as jsx13 } from "react/jsx-runtime";
|
||
var PLAYER_COMP_ID = "player-comp";
|
||
var SharedPlayerContexts = ({
|
||
children,
|
||
timelineContext,
|
||
fps,
|
||
compositionHeight,
|
||
compositionWidth,
|
||
durationInFrames,
|
||
component,
|
||
numberOfSharedAudioTags,
|
||
initiallyMuted,
|
||
logLevel,
|
||
audioLatencyHint,
|
||
volumePersistenceKey,
|
||
inputProps,
|
||
audioEnabled
|
||
}) => {
|
||
const compositionManagerContext = useMemo13(() => {
|
||
const context = {
|
||
compositions: [
|
||
{
|
||
component,
|
||
durationInFrames,
|
||
height: compositionHeight,
|
||
width: compositionWidth,
|
||
fps,
|
||
id: PLAYER_COMP_ID,
|
||
nonce: 777,
|
||
folderName: null,
|
||
parentFolderName: null,
|
||
schema: null,
|
||
calculateMetadata: null
|
||
}
|
||
],
|
||
folders: [],
|
||
currentCompositionMetadata: {
|
||
defaultCodec: null,
|
||
defaultOutName: null,
|
||
defaultPixelFormat: null,
|
||
defaultProResProfile: null,
|
||
defaultVideoImageFormat: null,
|
||
durationInFrames,
|
||
fps,
|
||
height: compositionHeight,
|
||
width: compositionWidth,
|
||
props: inputProps
|
||
},
|
||
canvasContent: { type: "composition", compositionId: "player-comp" }
|
||
};
|
||
return context;
|
||
}, [
|
||
component,
|
||
durationInFrames,
|
||
compositionHeight,
|
||
compositionWidth,
|
||
fps,
|
||
inputProps
|
||
]);
|
||
const [mediaMuted, setMediaMuted] = useState12(() => initiallyMuted);
|
||
const [mediaVolume, setMediaVolume] = useState12(() => getPreferredVolume(volumePersistenceKey ?? null));
|
||
const mediaVolumeContextValue = useMemo13(() => {
|
||
return {
|
||
mediaMuted,
|
||
mediaVolume
|
||
};
|
||
}, [mediaMuted, mediaVolume]);
|
||
const setMediaVolumeAndPersist = useCallback12((vol) => {
|
||
setMediaVolume(vol);
|
||
persistVolume(vol, logLevel, volumePersistenceKey ?? null);
|
||
}, [logLevel, volumePersistenceKey]);
|
||
const setMediaVolumeContextValue = useMemo13(() => {
|
||
return {
|
||
setMediaMuted,
|
||
setMediaVolume: setMediaVolumeAndPersist
|
||
};
|
||
}, [setMediaVolumeAndPersist]);
|
||
const logLevelContext = useMemo13(() => {
|
||
return {
|
||
logLevel,
|
||
mountTime: Date.now()
|
||
};
|
||
}, [logLevel]);
|
||
const env = useMemo13(() => {
|
||
return {
|
||
isPlayer: true,
|
||
isRendering: false,
|
||
isStudio: false,
|
||
isClientSideRendering: false,
|
||
isReadOnlyStudio: false
|
||
};
|
||
}, []);
|
||
return /* @__PURE__ */ jsx13(Internals13.RemotionEnvironmentContext.Provider, {
|
||
value: env,
|
||
children: /* @__PURE__ */ jsx13(Internals13.LogLevelContext.Provider, {
|
||
value: logLevelContext,
|
||
children: /* @__PURE__ */ jsx13(Internals13.CanUseRemotionHooksProvider, {
|
||
children: /* @__PURE__ */ jsx13(Internals13.TimelineContext.Provider, {
|
||
value: timelineContext,
|
||
children: /* @__PURE__ */ jsx13(Internals13.CompositionManager.Provider, {
|
||
value: compositionManagerContext,
|
||
children: /* @__PURE__ */ jsx13(Internals13.PrefetchProvider, {
|
||
children: /* @__PURE__ */ jsx13(Internals13.DurationsContextProvider, {
|
||
children: /* @__PURE__ */ jsx13(Internals13.MediaVolumeContext.Provider, {
|
||
value: mediaVolumeContextValue,
|
||
children: /* @__PURE__ */ jsx13(Internals13.SetMediaVolumeContext.Provider, {
|
||
value: setMediaVolumeContextValue,
|
||
children: /* @__PURE__ */ jsx13(Internals13.SharedAudioContextProvider, {
|
||
numberOfAudioTags: numberOfSharedAudioTags,
|
||
audioLatencyHint,
|
||
audioEnabled,
|
||
children: /* @__PURE__ */ jsx13(Internals13.BufferingProvider, {
|
||
children
|
||
})
|
||
})
|
||
})
|
||
})
|
||
})
|
||
})
|
||
})
|
||
})
|
||
})
|
||
})
|
||
});
|
||
};
|
||
|
||
// src/use-remotion-license-acknowledge.ts
|
||
import { Internals as Internals14 } from "remotion";
|
||
var warningShown = false;
|
||
var acknowledgeRemotionLicenseMessage = (acknowledge, logLevel) => {
|
||
if (acknowledge) {
|
||
return;
|
||
}
|
||
if (warningShown) {
|
||
return;
|
||
}
|
||
warningShown = true;
|
||
Internals14.Log.warn({ logLevel, tag: null }, "Note: Some companies are required to obtain a license to use Remotion. See: https://remotion.dev/license\nPass the `acknowledgeRemotionLicense` prop to `<Player />` function to make this message disappear.");
|
||
};
|
||
|
||
// src/utils/validate-in-out-frame.ts
|
||
var validateSingleFrame = (frame, variableName) => {
|
||
if (typeof frame === "undefined" || frame === null) {
|
||
return frame ?? null;
|
||
}
|
||
if (typeof frame !== "number") {
|
||
throw new TypeError(`"${variableName}" must be a number, but is ${JSON.stringify(frame)}`);
|
||
}
|
||
if (Number.isNaN(frame)) {
|
||
throw new TypeError(`"${variableName}" must not be NaN, but is ${JSON.stringify(frame)}`);
|
||
}
|
||
if (!Number.isFinite(frame)) {
|
||
throw new TypeError(`"${variableName}" must be finite, but is ${JSON.stringify(frame)}`);
|
||
}
|
||
if (frame % 1 !== 0) {
|
||
throw new TypeError(`"${variableName}" must be an integer, but is ${JSON.stringify(frame)}`);
|
||
}
|
||
return frame;
|
||
};
|
||
var validateInOutFrames = ({
|
||
inFrame,
|
||
durationInFrames,
|
||
outFrame
|
||
}) => {
|
||
const validatedInFrame = validateSingleFrame(inFrame, "inFrame");
|
||
const validatedOutFrame = validateSingleFrame(outFrame, "outFrame");
|
||
if (validatedInFrame === null && validatedOutFrame === null) {
|
||
return;
|
||
}
|
||
if (validatedInFrame !== null && validatedInFrame > durationInFrames - 1) {
|
||
throw new Error("inFrame must be less than (durationInFrames - 1), but is " + validatedInFrame);
|
||
}
|
||
if (validatedOutFrame !== null && validatedOutFrame > durationInFrames - 1) {
|
||
throw new Error("outFrame must be less than (durationInFrames - 1), but is " + validatedOutFrame);
|
||
}
|
||
if (validatedInFrame !== null && validatedInFrame < 0) {
|
||
throw new Error("inFrame must be greater than 0, but is " + validatedInFrame);
|
||
}
|
||
if (validatedOutFrame !== null && validatedOutFrame <= 0) {
|
||
throw new Error(`outFrame must be greater than 0, but is ${validatedOutFrame}. If you want to render a single frame, use <Thumbnail /> instead.`);
|
||
}
|
||
if (validatedOutFrame !== null && validatedInFrame !== null && validatedOutFrame <= validatedInFrame) {
|
||
throw new Error("outFrame must be greater than inFrame, but is " + validatedOutFrame + " <= " + validatedInFrame);
|
||
}
|
||
};
|
||
|
||
// src/utils/validate-initial-frame.ts
|
||
var validateInitialFrame = ({
|
||
initialFrame,
|
||
durationInFrames
|
||
}) => {
|
||
if (typeof durationInFrames !== "number") {
|
||
throw new Error(`\`durationInFrames\` must be a number, but is ${JSON.stringify(durationInFrames)}`);
|
||
}
|
||
if (typeof initialFrame === "undefined") {
|
||
return;
|
||
}
|
||
if (typeof initialFrame !== "number") {
|
||
throw new Error(`\`initialFrame\` must be a number, but is ${JSON.stringify(initialFrame)}`);
|
||
}
|
||
if (Number.isNaN(initialFrame)) {
|
||
throw new Error(`\`initialFrame\` must be a number, but is NaN`);
|
||
}
|
||
if (!Number.isFinite(initialFrame)) {
|
||
throw new Error(`\`initialFrame\` must be a number, but is Infinity`);
|
||
}
|
||
if (initialFrame % 1 !== 0) {
|
||
throw new Error(`\`initialFrame\` must be an integer, but is ${JSON.stringify(initialFrame)}`);
|
||
}
|
||
if (initialFrame > durationInFrames - 1) {
|
||
throw new Error(`\`initialFrame\` must be less or equal than \`durationInFrames - 1\`, but is ${JSON.stringify(initialFrame)}`);
|
||
}
|
||
};
|
||
|
||
// src/utils/validate-playbackrate.ts
|
||
var validatePlaybackRate = (playbackRate) => {
|
||
if (playbackRate === undefined) {
|
||
return;
|
||
}
|
||
if (playbackRate > 4) {
|
||
throw new Error(`The highest possible playback rate is 4. You passed: ${playbackRate}`);
|
||
}
|
||
if (playbackRate < -4) {
|
||
throw new Error(`The lowest possible playback rate is -4. You passed: ${playbackRate}`);
|
||
}
|
||
if (playbackRate === 0) {
|
||
throw new Error(`A playback rate of 0 is not supported.`);
|
||
}
|
||
};
|
||
|
||
// src/validate.ts
|
||
import { NoReactInternals } from "remotion/no-react";
|
||
var validateFps = NoReactInternals.validateFps;
|
||
var validateDimension = NoReactInternals.validateDimension;
|
||
var validateDurationInFrames = NoReactInternals.validateDurationInFrames;
|
||
var validateDefaultAndInputProps = NoReactInternals.validateDefaultAndInputProps;
|
||
|
||
// src/Player.tsx
|
||
import { jsx as jsx14 } from "react/jsx-runtime";
|
||
var componentOrNullIfLazy = (props) => {
|
||
if ("component" in props) {
|
||
return props.component;
|
||
}
|
||
return null;
|
||
};
|
||
var PlayerFn = ({
|
||
durationInFrames,
|
||
compositionHeight,
|
||
compositionWidth,
|
||
fps,
|
||
inputProps,
|
||
style: style2,
|
||
controls = false,
|
||
loop = false,
|
||
autoPlay = false,
|
||
showVolumeControls = true,
|
||
allowFullscreen = true,
|
||
clickToPlay,
|
||
doubleClickToFullscreen = false,
|
||
spaceKeyToPlayOrPause = true,
|
||
moveToBeginningWhenEnded = true,
|
||
numberOfSharedAudioTags = 5,
|
||
errorFallback = () => "⚠️",
|
||
playbackRate = 1,
|
||
renderLoading,
|
||
className: className2,
|
||
showPosterWhenUnplayed,
|
||
showPosterWhenEnded,
|
||
showPosterWhenPaused,
|
||
showPosterWhenBuffering,
|
||
showPosterWhenBufferingAndPaused,
|
||
initialFrame,
|
||
renderPoster,
|
||
inFrame,
|
||
outFrame,
|
||
initiallyShowControls,
|
||
renderFullscreenButton,
|
||
renderPlayPauseButton,
|
||
renderVolumeSlider,
|
||
renderCustomControls,
|
||
alwaysShowControls = false,
|
||
initiallyMuted = false,
|
||
showPlaybackRateControl = false,
|
||
posterFillMode = "player-size",
|
||
bufferStateDelayInMilliseconds,
|
||
hideControlsWhenPointerDoesntMove = true,
|
||
overflowVisible = false,
|
||
renderMuteButton,
|
||
browserMediaControlsBehavior: passedBrowserMediaControlsBehavior,
|
||
overrideInternalClassName,
|
||
logLevel = "info",
|
||
noSuspense,
|
||
acknowledgeRemotionLicense,
|
||
audioLatencyHint = "interactive",
|
||
volumePersistenceKey,
|
||
...componentProps
|
||
}, ref) => {
|
||
if (typeof window !== "undefined") {
|
||
window.remotion_isPlayer = true;
|
||
}
|
||
if (componentProps.defaultProps !== undefined) {
|
||
throw new Error("The <Player /> component does not accept `defaultProps`, but some were passed. Use `inputProps` instead.");
|
||
}
|
||
const componentForValidation = componentOrNullIfLazy(componentProps);
|
||
if (componentForValidation?.type === Composition) {
|
||
throw new TypeError(`'component' should not be an instance of <Composition/>. Pass the React component directly, and set the duration, fps and dimensions as separate props. See https://www.remotion.dev/docs/player/examples for an example.`);
|
||
}
|
||
if (componentForValidation === Composition) {
|
||
throw new TypeError(`'component' must not be the 'Composition' component. Pass your own React component directly, and set the duration, fps and dimensions as separate props. See https://www.remotion.dev/docs/player/examples for an example.`);
|
||
}
|
||
useState13(() => acknowledgeRemotionLicenseMessage(Boolean(acknowledgeRemotionLicense), logLevel));
|
||
const component = Internals15.useLazyComponent({
|
||
compProps: componentProps,
|
||
componentName: "Player",
|
||
noSuspense: Boolean(noSuspense)
|
||
});
|
||
validateInitialFrame({ initialFrame, durationInFrames });
|
||
const [frame, setFrame] = useState13(() => ({
|
||
[PLAYER_COMP_ID]: initialFrame ?? 0
|
||
}));
|
||
const [playing, setPlaying] = useState13(false);
|
||
const [rootId] = useState13("player-comp");
|
||
const rootRef = useRef11(null);
|
||
const audioAndVideoTags = useRef11([]);
|
||
const imperativePlaying = useRef11(false);
|
||
const [currentPlaybackRate, setCurrentPlaybackRate] = useState13(playbackRate);
|
||
if (typeof compositionHeight !== "number") {
|
||
throw new TypeError(`'compositionHeight' must be a number but got '${typeof compositionHeight}' instead`);
|
||
}
|
||
if (typeof compositionWidth !== "number") {
|
||
throw new TypeError(`'compositionWidth' must be a number but got '${typeof compositionWidth}' instead`);
|
||
}
|
||
validateDimension(compositionHeight, "compositionHeight", "of the <Player /> component");
|
||
validateDimension(compositionWidth, "compositionWidth", "of the <Player /> component");
|
||
validateDurationInFrames(durationInFrames, {
|
||
component: "of the <Player/> component",
|
||
allowFloats: false
|
||
});
|
||
validateFps(fps, "as a prop of the <Player/> component", false);
|
||
validateDefaultAndInputProps(inputProps, "inputProps", null);
|
||
validateInOutFrames({
|
||
durationInFrames,
|
||
inFrame,
|
||
outFrame
|
||
});
|
||
if (typeof controls !== "boolean" && typeof controls !== "undefined") {
|
||
throw new TypeError(`'controls' must be a boolean or undefined but got '${typeof controls}' instead`);
|
||
}
|
||
if (typeof autoPlay !== "boolean" && typeof autoPlay !== "undefined") {
|
||
throw new TypeError(`'autoPlay' must be a boolean or undefined but got '${typeof autoPlay}' instead`);
|
||
}
|
||
if (typeof loop !== "boolean" && typeof loop !== "undefined") {
|
||
throw new TypeError(`'loop' must be a boolean or undefined but got '${typeof loop}' instead`);
|
||
}
|
||
if (typeof doubleClickToFullscreen !== "boolean" && typeof doubleClickToFullscreen !== "undefined") {
|
||
throw new TypeError(`'doubleClickToFullscreen' must be a boolean or undefined but got '${typeof doubleClickToFullscreen}' instead`);
|
||
}
|
||
if (typeof showVolumeControls !== "boolean" && typeof showVolumeControls !== "undefined") {
|
||
throw new TypeError(`'showVolumeControls' must be a boolean or undefined but got '${typeof showVolumeControls}' instead`);
|
||
}
|
||
if (typeof allowFullscreen !== "boolean" && typeof allowFullscreen !== "undefined") {
|
||
throw new TypeError(`'allowFullscreen' must be a boolean or undefined but got '${typeof allowFullscreen}' instead`);
|
||
}
|
||
if (typeof clickToPlay !== "boolean" && typeof clickToPlay !== "undefined") {
|
||
throw new TypeError(`'clickToPlay' must be a boolean or undefined but got '${typeof clickToPlay}' instead`);
|
||
}
|
||
if (typeof spaceKeyToPlayOrPause !== "boolean" && typeof spaceKeyToPlayOrPause !== "undefined") {
|
||
throw new TypeError(`'spaceKeyToPlayOrPause' must be a boolean or undefined but got '${typeof spaceKeyToPlayOrPause}' instead`);
|
||
}
|
||
if (typeof numberOfSharedAudioTags !== "number" || numberOfSharedAudioTags % 1 !== 0 || !Number.isFinite(numberOfSharedAudioTags) || Number.isNaN(numberOfSharedAudioTags) || numberOfSharedAudioTags < 0) {
|
||
throw new TypeError(`'numberOfSharedAudioTags' must be an integer but got '${numberOfSharedAudioTags}' instead`);
|
||
}
|
||
validatePlaybackRate(currentPlaybackRate);
|
||
useEffect13(() => {
|
||
setCurrentPlaybackRate(playbackRate);
|
||
}, [playbackRate]);
|
||
useImperativeHandle2(ref, () => rootRef.current, []);
|
||
useState13(() => {
|
||
Internals15.playbackLogging({
|
||
logLevel,
|
||
message: `[player] Mounting <Player>. User agent = ${typeof navigator === "undefined" ? "server" : navigator.userAgent}`,
|
||
tag: "player",
|
||
mountTime: Date.now()
|
||
});
|
||
});
|
||
const timelineContextValue = useMemo14(() => {
|
||
return {
|
||
frame,
|
||
playing,
|
||
rootId,
|
||
playbackRate: currentPlaybackRate,
|
||
imperativePlaying,
|
||
setPlaybackRate: (rate) => {
|
||
setCurrentPlaybackRate(rate);
|
||
},
|
||
audioAndVideoTags
|
||
};
|
||
}, [frame, currentPlaybackRate, playing, rootId]);
|
||
const setTimelineContextValue = useMemo14(() => {
|
||
return {
|
||
setFrame,
|
||
setPlaying
|
||
};
|
||
}, [setFrame]);
|
||
if (typeof window !== "undefined") {
|
||
useLayoutEffect2(() => {
|
||
Internals15.CSSUtils.injectCSS(Internals15.CSSUtils.makeDefaultPreviewCSS(`.${playerCssClassname(overrideInternalClassName)}`, "#fff"));
|
||
}, [overrideInternalClassName]);
|
||
}
|
||
const actualInputProps = useMemo14(() => inputProps ?? {}, [inputProps]);
|
||
const browserMediaControlsBehavior = useMemo14(() => {
|
||
return passedBrowserMediaControlsBehavior ?? {
|
||
mode: "prevent-media-session"
|
||
};
|
||
}, [passedBrowserMediaControlsBehavior]);
|
||
return /* @__PURE__ */ jsx14(Internals15.IsPlayerContextProvider, {
|
||
children: /* @__PURE__ */ jsx14(SharedPlayerContexts, {
|
||
timelineContext: timelineContextValue,
|
||
component,
|
||
compositionHeight,
|
||
compositionWidth,
|
||
durationInFrames,
|
||
fps,
|
||
numberOfSharedAudioTags,
|
||
initiallyMuted,
|
||
logLevel,
|
||
audioLatencyHint,
|
||
volumePersistenceKey,
|
||
inputProps: actualInputProps,
|
||
audioEnabled: true,
|
||
children: /* @__PURE__ */ jsx14(Internals15.SetTimelineContext.Provider, {
|
||
value: setTimelineContextValue,
|
||
children: /* @__PURE__ */ jsx14(PlayerEmitterProvider, {
|
||
currentPlaybackRate,
|
||
children: /* @__PURE__ */ jsx14(PlayerUI_default, {
|
||
ref: rootRef,
|
||
posterFillMode,
|
||
renderLoading,
|
||
autoPlay: Boolean(autoPlay),
|
||
loop: Boolean(loop),
|
||
controls: Boolean(controls),
|
||
errorFallback,
|
||
style: style2,
|
||
inputProps: actualInputProps,
|
||
allowFullscreen: Boolean(allowFullscreen),
|
||
moveToBeginningWhenEnded: Boolean(moveToBeginningWhenEnded),
|
||
clickToPlay: typeof clickToPlay === "boolean" ? clickToPlay : Boolean(controls),
|
||
showVolumeControls: Boolean(showVolumeControls),
|
||
doubleClickToFullscreen: Boolean(doubleClickToFullscreen),
|
||
spaceKeyToPlayOrPause: Boolean(spaceKeyToPlayOrPause),
|
||
playbackRate: currentPlaybackRate,
|
||
className: className2 ?? undefined,
|
||
showPosterWhenUnplayed: Boolean(showPosterWhenUnplayed),
|
||
showPosterWhenEnded: Boolean(showPosterWhenEnded),
|
||
showPosterWhenPaused: Boolean(showPosterWhenPaused),
|
||
showPosterWhenBuffering: Boolean(showPosterWhenBuffering),
|
||
showPosterWhenBufferingAndPaused: Boolean(showPosterWhenBufferingAndPaused),
|
||
renderPoster,
|
||
inFrame: inFrame ?? null,
|
||
outFrame: outFrame ?? null,
|
||
initiallyShowControls: initiallyShowControls ?? true,
|
||
renderFullscreen: renderFullscreenButton ?? null,
|
||
renderPlayPauseButton: renderPlayPauseButton ?? null,
|
||
renderMuteButton: renderMuteButton ?? null,
|
||
renderVolumeSlider: renderVolumeSlider ?? null,
|
||
renderCustomControls: renderCustomControls ?? null,
|
||
alwaysShowControls,
|
||
showPlaybackRateControl,
|
||
bufferStateDelayInMilliseconds: bufferStateDelayInMilliseconds ?? 300,
|
||
hideControlsWhenPointerDoesntMove,
|
||
overflowVisible,
|
||
browserMediaControlsBehavior,
|
||
overrideInternalClassName: overrideInternalClassName ?? undefined,
|
||
noSuspense: Boolean(noSuspense)
|
||
})
|
||
})
|
||
})
|
||
})
|
||
});
|
||
};
|
||
var forward = forwardRef2;
|
||
var Player = forward(PlayerFn);
|
||
// src/Thumbnail.tsx
|
||
import {
|
||
forwardRef as forwardRef4,
|
||
useImperativeHandle as useImperativeHandle4,
|
||
useLayoutEffect as useLayoutEffect3,
|
||
useMemo as useMemo17,
|
||
useRef as useRef13,
|
||
useState as useState14
|
||
} from "react";
|
||
import { Internals as Internals17, random as random2 } from "remotion";
|
||
|
||
// src/ThumbnailUI.tsx
|
||
import React13, {
|
||
forwardRef as forwardRef3,
|
||
Suspense as Suspense2,
|
||
useCallback as useCallback13,
|
||
useImperativeHandle as useImperativeHandle3,
|
||
useMemo as useMemo16,
|
||
useRef as useRef12
|
||
} from "react";
|
||
import { Internals as Internals16 } from "remotion";
|
||
|
||
// src/use-thumbnail.ts
|
||
import { useContext as useContext7, useMemo as useMemo15 } from "react";
|
||
var useThumbnail = () => {
|
||
const emitter = useContext7(ThumbnailEmitterContext);
|
||
if (!emitter) {
|
||
throw new TypeError("Expected Player event emitter context");
|
||
}
|
||
const returnValue = useMemo15(() => {
|
||
return {
|
||
emitter
|
||
};
|
||
}, [emitter]);
|
||
return returnValue;
|
||
};
|
||
|
||
// src/ThumbnailUI.tsx
|
||
import { jsx as jsx15 } from "react/jsx-runtime";
|
||
var reactVersion2 = React13.version.split(".")[0];
|
||
if (reactVersion2 === "0") {
|
||
throw new Error(`Version ${reactVersion2} of "react" is not supported by Remotion`);
|
||
}
|
||
var doesReactVersionSupportSuspense2 = parseInt(reactVersion2, 10) >= 18;
|
||
var ThumbnailUI = ({
|
||
style: style2,
|
||
inputProps,
|
||
errorFallback,
|
||
renderLoading,
|
||
className: className2,
|
||
overflowVisible,
|
||
noSuspense,
|
||
overrideInternalClassName
|
||
}, ref) => {
|
||
const config = Internals16.useUnsafeVideoConfig();
|
||
const video = Internals16.useVideo();
|
||
const container = useRef12(null);
|
||
const canvasSize = useElementSize(container, {
|
||
triggerOnWindowResize: false,
|
||
shouldApplyCssTransforms: false
|
||
});
|
||
const layout = useMemo16(() => {
|
||
if (!config || !canvasSize) {
|
||
return null;
|
||
}
|
||
return calculateCanvasTransformation({
|
||
canvasSize,
|
||
compositionHeight: config.height,
|
||
compositionWidth: config.width,
|
||
previewSize: "auto"
|
||
});
|
||
}, [canvasSize, config]);
|
||
const scale = layout?.scale ?? 1;
|
||
const thumbnail = useThumbnail();
|
||
useBufferStateEmitter(thumbnail.emitter);
|
||
useImperativeHandle3(ref, () => {
|
||
const methods = {
|
||
getContainerNode: () => container.current,
|
||
getScale: () => scale
|
||
};
|
||
return Object.assign(thumbnail.emitter, methods);
|
||
}, [scale, thumbnail.emitter]);
|
||
const VideoComponent = video ? video.component : null;
|
||
const outerStyle = useMemo16(() => {
|
||
return calculateOuterStyle({
|
||
config,
|
||
style: style2,
|
||
canvasSize,
|
||
overflowVisible,
|
||
layout
|
||
});
|
||
}, [canvasSize, config, layout, overflowVisible, style2]);
|
||
const outer = useMemo16(() => {
|
||
return calculateOuter({ config, layout, scale, overflowVisible });
|
||
}, [config, layout, overflowVisible, scale]);
|
||
const containerStyle3 = useMemo16(() => {
|
||
return calculateContainerStyle({
|
||
config,
|
||
layout,
|
||
scale,
|
||
overflowVisible
|
||
});
|
||
}, [config, layout, overflowVisible, scale]);
|
||
const onError = useCallback13((error) => {
|
||
thumbnail.emitter.dispatchError(error);
|
||
}, [thumbnail.emitter]);
|
||
const loadingMarkup = useMemo16(() => {
|
||
return renderLoading ? renderLoading({
|
||
height: outerStyle.height,
|
||
width: outerStyle.width,
|
||
isBuffering: false
|
||
}) : null;
|
||
}, [outerStyle.height, outerStyle.width, renderLoading]);
|
||
const currentScaleContext = useMemo16(() => {
|
||
return {
|
||
type: "scale",
|
||
scale
|
||
};
|
||
}, [scale]);
|
||
if (!config) {
|
||
return null;
|
||
}
|
||
const content = /* @__PURE__ */ jsx15("div", {
|
||
style: outer,
|
||
children: /* @__PURE__ */ jsx15("div", {
|
||
style: containerStyle3,
|
||
className: playerCssClassname(overrideInternalClassName),
|
||
children: VideoComponent ? /* @__PURE__ */ jsx15(ErrorBoundary, {
|
||
onError,
|
||
errorFallback,
|
||
children: /* @__PURE__ */ jsx15(Internals16.CurrentScaleContext.Provider, {
|
||
value: currentScaleContext,
|
||
children: /* @__PURE__ */ jsx15(VideoComponent, {
|
||
...video?.props ?? {},
|
||
...inputProps ?? {}
|
||
})
|
||
})
|
||
}) : null
|
||
})
|
||
});
|
||
if (noSuspense || IS_NODE && !doesReactVersionSupportSuspense2) {
|
||
return /* @__PURE__ */ jsx15("div", {
|
||
ref: container,
|
||
style: outerStyle,
|
||
className: className2,
|
||
children: content
|
||
});
|
||
}
|
||
return /* @__PURE__ */ jsx15("div", {
|
||
ref: container,
|
||
style: outerStyle,
|
||
className: className2,
|
||
children: /* @__PURE__ */ jsx15(Suspense2, {
|
||
fallback: loadingMarkup,
|
||
children: content
|
||
})
|
||
});
|
||
};
|
||
var ThumbnailUI_default = forwardRef3(ThumbnailUI);
|
||
|
||
// src/Thumbnail.tsx
|
||
import { jsx as jsx16 } from "react/jsx-runtime";
|
||
var ThumbnailFn = ({
|
||
frameToDisplay,
|
||
style: style2,
|
||
inputProps,
|
||
compositionHeight,
|
||
compositionWidth,
|
||
durationInFrames,
|
||
fps,
|
||
className: className2,
|
||
errorFallback = () => "⚠️",
|
||
renderLoading,
|
||
overflowVisible = false,
|
||
overrideInternalClassName,
|
||
logLevel = "info",
|
||
noSuspense,
|
||
...componentProps
|
||
}, ref) => {
|
||
if (typeof window !== "undefined") {
|
||
useLayoutEffect3(() => {
|
||
window.remotion_isPlayer = true;
|
||
}, []);
|
||
}
|
||
const [thumbnailId] = useState14(() => String(random2(null)));
|
||
const rootRef = useRef13(null);
|
||
const timelineState = useMemo17(() => {
|
||
const value = {
|
||
playing: false,
|
||
frame: {
|
||
[PLAYER_COMP_ID]: frameToDisplay
|
||
},
|
||
rootId: thumbnailId,
|
||
imperativePlaying: {
|
||
current: false
|
||
},
|
||
playbackRate: 1,
|
||
setPlaybackRate: () => {
|
||
throw new Error("thumbnail");
|
||
},
|
||
audioAndVideoTags: { current: [] }
|
||
};
|
||
return value;
|
||
}, [frameToDisplay, thumbnailId]);
|
||
useImperativeHandle4(ref, () => rootRef.current, []);
|
||
const Component = Internals17.useLazyComponent({
|
||
compProps: componentProps,
|
||
componentName: "Thumbnail",
|
||
noSuspense: Boolean(noSuspense)
|
||
});
|
||
const [emitter] = useState14(() => new ThumbnailEmitter);
|
||
const passedInputProps = useMemo17(() => {
|
||
return inputProps ?? {};
|
||
}, [inputProps]);
|
||
return /* @__PURE__ */ jsx16(Internals17.IsPlayerContextProvider, {
|
||
children: /* @__PURE__ */ jsx16(SharedPlayerContexts, {
|
||
timelineContext: timelineState,
|
||
component: Component,
|
||
compositionHeight,
|
||
compositionWidth,
|
||
durationInFrames,
|
||
fps,
|
||
numberOfSharedAudioTags: 0,
|
||
initiallyMuted: true,
|
||
logLevel,
|
||
audioLatencyHint: "playback",
|
||
inputProps: passedInputProps,
|
||
audioEnabled: false,
|
||
children: /* @__PURE__ */ jsx16(ThumbnailEmitterContext.Provider, {
|
||
value: emitter,
|
||
children: /* @__PURE__ */ jsx16(ThumbnailUI_default, {
|
||
ref: rootRef,
|
||
className: className2,
|
||
errorFallback,
|
||
inputProps: passedInputProps,
|
||
renderLoading,
|
||
style: style2,
|
||
overflowVisible,
|
||
overrideInternalClassName,
|
||
noSuspense: Boolean(noSuspense)
|
||
})
|
||
})
|
||
})
|
||
});
|
||
};
|
||
var forward2 = forwardRef4;
|
||
var Thumbnail = forward2(ThumbnailFn);
|
||
|
||
// src/index.ts
|
||
var PlayerInternals = {
|
||
PlayerEventEmitterContext,
|
||
PlayerEmitter,
|
||
usePlayer,
|
||
usePlayback,
|
||
useElementSize,
|
||
calculateCanvasTransformation,
|
||
useHoverState,
|
||
updateAllElementsSizes,
|
||
PlayerEmitterProvider,
|
||
BufferingIndicator,
|
||
useFrameImperative
|
||
};
|
||
export {
|
||
Thumbnail,
|
||
PlayerInternals,
|
||
Player
|
||
};
|