@remotion/player 4.0.231 → 4.0.233

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -54,6 +54,7 @@ export type PlayerProps<Schema extends AnyZodObject, Props extends Record<string
54
54
  readonly hideControlsWhenPointerDoesntMove?: boolean | number;
55
55
  readonly overflowVisible?: boolean;
56
56
  readonly browserMediaControlsBehavior?: BrowserMediaControlsBehavior;
57
+ readonly overrideInternalClassName?: string;
57
58
  } & CompProps<Props> & PropsIfHasProps<Schema, Props>;
58
59
  export type PlayerPropsWithoutZod<Props extends Record<string, unknown>> = PlayerProps<AnyZodObject, Props>;
59
60
  export declare const componentOrNullIfLazy: <Props>(props: CompProps<Props>) => ComponentType<Props> | null;
@@ -22,7 +22,7 @@ const componentOrNullIfLazy = (props) => {
22
22
  return null;
23
23
  };
24
24
  exports.componentOrNullIfLazy = componentOrNullIfLazy;
25
- const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps, inputProps, style, controls = false, loop = false, autoPlay = false, showVolumeControls = true, allowFullscreen = true, clickToPlay, doubleClickToFullscreen = false, spaceKeyToPlayOrPause = true, moveToBeginningWhenEnded = true, numberOfSharedAudioTags = 5, errorFallback = () => '⚠️', playbackRate = 1, renderLoading, className, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, showPosterWhenBuffering, initialFrame, renderPoster, inFrame, outFrame, initiallyShowControls, renderFullscreenButton, renderPlayPauseButton, renderVolumeSlider, alwaysShowControls = false, initiallyMuted = false, showPlaybackRateControl = false, posterFillMode = 'player-size', bufferStateDelayInMilliseconds, hideControlsWhenPointerDoesntMove = true, overflowVisible = false, renderMuteButton, browserMediaControlsBehavior: passedBrowserMediaControlsBehavior, ...componentProps }, ref) => {
25
+ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps, inputProps, style, controls = false, loop = false, autoPlay = false, showVolumeControls = true, allowFullscreen = true, clickToPlay, doubleClickToFullscreen = false, spaceKeyToPlayOrPause = true, moveToBeginningWhenEnded = true, numberOfSharedAudioTags = 5, errorFallback = () => '⚠️', playbackRate = 1, renderLoading, className, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, showPosterWhenBuffering, initialFrame, renderPoster, inFrame, outFrame, initiallyShowControls, renderFullscreenButton, renderPlayPauseButton, renderVolumeSlider, alwaysShowControls = false, initiallyMuted = false, showPlaybackRateControl = false, posterFillMode = 'player-size', bufferStateDelayInMilliseconds, hideControlsWhenPointerDoesntMove = true, overflowVisible = false, renderMuteButton, browserMediaControlsBehavior: passedBrowserMediaControlsBehavior, overrideInternalClassName, ...componentProps }, ref) => {
26
26
  if (typeof window !== 'undefined') {
27
27
  // eslint-disable-next-line react-hooks/rules-of-hooks
28
28
  (0, react_1.useLayoutEffect)(() => {
@@ -134,8 +134,8 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
134
134
  // eslint-disable-next-line react-hooks/rules-of-hooks
135
135
  (0, react_1.useLayoutEffect)(() => {
136
136
  // Inject CSS only on client, and also only after the Player has hydrated
137
- remotion_1.Internals.CSSUtils.injectCSS(remotion_1.Internals.CSSUtils.makeDefaultPreviewCSS(`.${player_css_classname_js_1.PLAYER_CSS_CLASSNAME}`, '#fff'));
138
- }, []);
137
+ remotion_1.Internals.CSSUtils.injectCSS(remotion_1.Internals.CSSUtils.makeDefaultPreviewCSS(`.${(0, player_css_classname_js_1.playerCssClassname)(overrideInternalClassName)}`, '#fff'));
138
+ }, [overrideInternalClassName]);
139
139
  }
140
140
  const actualInputProps = (0, react_1.useMemo)(() => inputProps !== null && inputProps !== void 0 ? inputProps : {}, [inputProps]);
141
141
  const browserMediaControlsBehavior = (0, react_1.useMemo)(() => {
@@ -145,7 +145,7 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
145
145
  }, [passedBrowserMediaControlsBehavior]);
146
146
  return ((0, jsx_runtime_1.jsx)(remotion_1.Internals.IsPlayerContextProvider, { children: (0, jsx_runtime_1.jsx)(SharedPlayerContext_js_1.SharedPlayerContexts, { timelineContext: timelineContextValue, component: component, compositionHeight: compositionHeight, compositionWidth: compositionWidth, durationInFrames: durationInFrames, fps: fps, numberOfSharedAudioTags: numberOfSharedAudioTags, initiallyMuted: initiallyMuted, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.Timeline.SetTimelineContext.Provider, { value: setTimelineContextValue, children: (0, jsx_runtime_1.jsx)(EmitterProvider_js_1.PlayerEmitterProvider, { currentPlaybackRate: currentPlaybackRate, children: (0, jsx_runtime_1.jsx)(PlayerUI_js_1.default, { ref: rootRef, posterFillMode: posterFillMode, renderLoading: renderLoading, autoPlay: Boolean(autoPlay), loop: Boolean(loop), controls: Boolean(controls), errorFallback: errorFallback, style: style, inputProps: actualInputProps, allowFullscreen: Boolean(allowFullscreen), moveToBeginningWhenEnded: Boolean(moveToBeginningWhenEnded), clickToPlay: typeof clickToPlay === 'boolean'
147
147
  ? clickToPlay
148
- : Boolean(controls), showVolumeControls: Boolean(showVolumeControls), doubleClickToFullscreen: Boolean(doubleClickToFullscreen), spaceKeyToPlayOrPause: Boolean(spaceKeyToPlayOrPause), playbackRate: currentPlaybackRate, className: className !== null && className !== void 0 ? className : undefined, showPosterWhenUnplayed: Boolean(showPosterWhenUnplayed), showPosterWhenEnded: Boolean(showPosterWhenEnded), showPosterWhenPaused: Boolean(showPosterWhenPaused), showPosterWhenBuffering: Boolean(showPosterWhenBuffering), renderPoster: renderPoster, inFrame: inFrame !== null && inFrame !== void 0 ? inFrame : null, outFrame: outFrame !== null && outFrame !== void 0 ? outFrame : null, initiallyShowControls: initiallyShowControls !== null && initiallyShowControls !== void 0 ? initiallyShowControls : true, renderFullscreen: renderFullscreenButton !== null && renderFullscreenButton !== void 0 ? renderFullscreenButton : null, renderPlayPauseButton: renderPlayPauseButton !== null && renderPlayPauseButton !== void 0 ? renderPlayPauseButton : null, renderMuteButton: renderMuteButton !== null && renderMuteButton !== void 0 ? renderMuteButton : null, renderVolumeSlider: renderVolumeSlider !== null && renderVolumeSlider !== void 0 ? renderVolumeSlider : null, alwaysShowControls: alwaysShowControls, showPlaybackRateControl: showPlaybackRateControl, bufferStateDelayInMilliseconds: bufferStateDelayInMilliseconds !== null && bufferStateDelayInMilliseconds !== void 0 ? bufferStateDelayInMilliseconds : 300, hideControlsWhenPointerDoesntMove: hideControlsWhenPointerDoesntMove, overflowVisible: overflowVisible, browserMediaControlsBehavior: browserMediaControlsBehavior }) }) }) }) }));
148
+ : Boolean(controls), showVolumeControls: Boolean(showVolumeControls), doubleClickToFullscreen: Boolean(doubleClickToFullscreen), spaceKeyToPlayOrPause: Boolean(spaceKeyToPlayOrPause), playbackRate: currentPlaybackRate, className: className !== null && className !== void 0 ? className : undefined, showPosterWhenUnplayed: Boolean(showPosterWhenUnplayed), showPosterWhenEnded: Boolean(showPosterWhenEnded), showPosterWhenPaused: Boolean(showPosterWhenPaused), showPosterWhenBuffering: Boolean(showPosterWhenBuffering), renderPoster: renderPoster, inFrame: inFrame !== null && inFrame !== void 0 ? inFrame : null, outFrame: outFrame !== null && outFrame !== void 0 ? outFrame : null, initiallyShowControls: initiallyShowControls !== null && initiallyShowControls !== void 0 ? initiallyShowControls : true, renderFullscreen: renderFullscreenButton !== null && renderFullscreenButton !== void 0 ? renderFullscreenButton : null, renderPlayPauseButton: renderPlayPauseButton !== null && renderPlayPauseButton !== void 0 ? renderPlayPauseButton : null, renderMuteButton: renderMuteButton !== null && renderMuteButton !== void 0 ? renderMuteButton : null, renderVolumeSlider: renderVolumeSlider !== null && renderVolumeSlider !== void 0 ? renderVolumeSlider : null, alwaysShowControls: alwaysShowControls, showPlaybackRateControl: showPlaybackRateControl, bufferStateDelayInMilliseconds: bufferStateDelayInMilliseconds !== null && bufferStateDelayInMilliseconds !== void 0 ? bufferStateDelayInMilliseconds : 300, hideControlsWhenPointerDoesntMove: hideControlsWhenPointerDoesntMove, overflowVisible: overflowVisible, browserMediaControlsBehavior: browserMediaControlsBehavior, overrideInternalClassName: overrideInternalClassName !== null && overrideInternalClassName !== void 0 ? overrideInternalClassName : undefined }) }) }) }) }));
149
149
  };
150
150
  const forward = react_1.forwardRef;
151
151
  /**
@@ -2,7 +2,6 @@ import type { MouseEventHandler, ReactNode, SyntheticEvent } from 'react';
2
2
  import React from 'react';
3
3
  import type { RenderMuteButton } from './MediaVolumeSlider.js';
4
4
  import type { RenderVolumeSlider } from './render-volume-slider.js';
5
- import type { usePlayer } from './use-player.js';
6
5
  import type { Size } from './utils/use-element-size.js';
7
6
  export type RenderPlayPauseButton = (props: {
8
7
  playing: boolean;
@@ -15,7 +14,6 @@ export declare const Controls: React.FC<{
15
14
  readonly fps: number;
16
15
  readonly durationInFrames: number;
17
16
  readonly showVolumeControls: boolean;
18
- readonly player: ReturnType<typeof usePlayer>;
19
17
  readonly onFullscreenButtonClick: MouseEventHandler<HTMLButtonElement>;
20
18
  readonly isFullscreen: boolean;
21
19
  readonly allowFullscreen: boolean;
@@ -38,4 +36,6 @@ export declare const Controls: React.FC<{
38
36
  readonly onDoubleClick: MouseEventHandler<HTMLDivElement> | undefined;
39
37
  readonly renderMuteButton: RenderMuteButton | null;
40
38
  readonly renderVolumeSlider: RenderVolumeSlider | null;
39
+ readonly playing: boolean;
40
+ readonly toggle: (e?: SyntheticEvent | PointerEvent) => void;
41
41
  }>;
@@ -3,12 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Controls = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
- const remotion_1 = require("remotion");
7
6
  const DefaultPlayPauseButton_js_1 = require("./DefaultPlayPauseButton.js");
8
7
  const MediaVolumeSlider_js_1 = require("./MediaVolumeSlider.js");
9
8
  const PlaybackrateControl_js_1 = require("./PlaybackrateControl.js");
10
9
  const PlayerSeekBar_js_1 = require("./PlayerSeekBar.js");
11
- const format_time_js_1 = require("./format-time.js");
10
+ const PlayerTimeLabel_js_1 = require("./PlayerTimeLabel.js");
12
11
  const icons_js_1 = require("./icons.js");
13
12
  const use_hover_state_js_1 = require("./use-hover-state.js");
14
13
  const use_video_controls_resize_js_1 = require("./use-video-controls-resize.js");
@@ -66,10 +65,9 @@ const flex1 = {
66
65
  flex: 1,
67
66
  };
68
67
  const fullscreen = {};
69
- const Controls = ({ durationInFrames, isFullscreen, fps, player, showVolumeControls, onFullscreenButtonClick, allowFullscreen, onExitFullscreenButtonClick, spaceKeyToPlayOrPause, onSeekEnd, onSeekStart, inFrame, outFrame, initiallyShowControls, canvasSize, renderPlayPauseButton, renderFullscreenButton, alwaysShowControls, showPlaybackRateControl, containerRef, buffering, hideControlsWhenPointerDoesntMove, onPointerDown, onDoubleClick, renderMuteButton, renderVolumeSlider, }) => {
68
+ const 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, }) => {
70
69
  var _a, _b;
71
70
  const playButtonRef = (0, react_1.useRef)(null);
72
- const frame = remotion_1.Internals.Timeline.useTimelinePosition();
73
71
  const [supportsFullscreen, setSupportsFullscreen] = (0, react_1.useState)(false);
74
72
  const hovered = (0, use_hover_state_js_1.useHoverState)(containerRef, hideControlsWhenPointerDoesntMove);
75
73
  const { maxTimeLabelWidth, displayVerticalVolumeSlider } = (0, use_video_controls_resize_js_1.useVideoControlsResize)({
@@ -99,12 +97,12 @@ const Controls = ({ durationInFrames, isFullscreen, fps, player, showVolumeContr
99
97
  });
100
98
  const containerCss = (0, react_1.useMemo)(() => {
101
99
  // Hide if playing and mouse outside
102
- const shouldShow = hovered || !player.playing || shouldShowInitially || alwaysShowControls;
100
+ const shouldShow = hovered || !playing || shouldShowInitially || alwaysShowControls;
103
101
  return {
104
102
  ...containerStyle,
105
103
  opacity: Number(shouldShow),
106
104
  };
107
- }, [hovered, shouldShowInitially, player.playing, alwaysShowControls]);
105
+ }, [hovered, shouldShowInitially, playing, alwaysShowControls]);
108
106
  (0, react_1.useEffect)(() => {
109
107
  if (playButtonRef.current && spaceKeyToPlayOrPause) {
110
108
  // This switches focus to play button when player.playing flag changes
@@ -112,7 +110,7 @@ const Controls = ({ durationInFrames, isFullscreen, fps, player, showVolumeContr
112
110
  preventScroll: true,
113
111
  });
114
112
  }
115
- }, [player.playing, spaceKeyToPlayOrPause]);
113
+ }, [playing, spaceKeyToPlayOrPause]);
116
114
  (0, react_1.useEffect)(() => {
117
115
  var _a;
118
116
  // Must be handled client-side to avoid SSR hydration mismatch
@@ -133,16 +131,6 @@ const Controls = ({ durationInFrames, isFullscreen, fps, player, showVolumeContr
133
131
  clearInterval(timeout);
134
132
  };
135
133
  }, [shouldShowInitially]);
136
- const timeLabel = (0, react_1.useMemo)(() => {
137
- return {
138
- color: 'white',
139
- fontFamily: 'sans-serif',
140
- fontSize: 14,
141
- maxWidth: maxTimeLabelWidth === null ? undefined : maxTimeLabelWidth,
142
- overflow: 'hidden',
143
- textOverflow: 'ellipsis',
144
- };
145
- }, [maxTimeLabelWidth]);
146
134
  const playbackRates = (0, react_1.useMemo)(() => {
147
135
  if (showPlaybackRateControl === true) {
148
136
  return [0.5, 0.8, 1, 1.2, 1.5, 1.8, 2, 2.5, 3];
@@ -174,10 +162,10 @@ const Controls = ({ durationInFrames, isFullscreen, fps, player, showVolumeContr
174
162
  onDoubleClick === null || onDoubleClick === void 0 ? void 0 : onDoubleClick(e);
175
163
  }
176
164
  }, [onDoubleClick]);
177
- return ((0, jsx_runtime_1.jsxs)("div", { ref: ref, style: containerCss, onPointerDown: onPointerDownIfContainer, onDoubleClick: onDoubleClickIfContainer, children: [(0, jsx_runtime_1.jsxs)("div", { ref: flexRef, style: controlsRow, children: [(0, jsx_runtime_1.jsxs)("div", { style: leftPartStyle, children: [(0, jsx_runtime_1.jsx)("button", { ref: playButtonRef, type: "button", style: PlaybackrateControl_js_1.playerButtonStyle, onClick: player.playing ? player.pause : player.play, "aria-label": player.playing ? 'Pause video' : 'Play video', title: player.playing ? 'Pause video' : 'Play video', children: renderPlayPauseButton === null ? ((0, jsx_runtime_1.jsx)(DefaultPlayPauseButton_js_1.DefaultPlayPauseButton, { buffering: buffering, playing: player.playing })) : (((_b = renderPlayPauseButton({
178
- playing: player.playing,
165
+ return ((0, jsx_runtime_1.jsxs)("div", { ref: ref, style: containerCss, onPointerDown: onPointerDownIfContainer, onDoubleClick: onDoubleClickIfContainer, children: [(0, jsx_runtime_1.jsxs)("div", { ref: flexRef, style: controlsRow, children: [(0, jsx_runtime_1.jsxs)("div", { style: leftPartStyle, children: [(0, jsx_runtime_1.jsx)("button", { ref: playButtonRef, type: "button", style: PlaybackrateControl_js_1.playerButtonStyle, onClick: toggle, "aria-label": playing ? 'Pause video' : 'Play video', title: playing ? 'Pause video' : 'Play video', children: renderPlayPauseButton === null ? ((0, jsx_runtime_1.jsx)(DefaultPlayPauseButton_js_1.DefaultPlayPauseButton, { buffering: buffering, playing: playing })) : (((_b = renderPlayPauseButton({
166
+ playing,
179
167
  isBuffering: buffering,
180
- })) !== null && _b !== void 0 ? _b : ((0, jsx_runtime_1.jsx)(DefaultPlayPauseButton_js_1.DefaultPlayPauseButton, { buffering: buffering, playing: player.playing })))) }), showVolumeControls ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: xSpacer }), (0, jsx_runtime_1.jsx)(MediaVolumeSlider_js_1.MediaVolumeSlider, { renderMuteButton: renderMuteButton, renderVolumeSlider: renderVolumeSlider, displayVerticalVolumeSlider: displayVerticalVolumeSlider })] })) : null, (0, jsx_runtime_1.jsx)("div", { style: xSpacer }), (0, jsx_runtime_1.jsxs)("div", { style: timeLabel, children: [(0, format_time_js_1.formatTime)(frame / fps), " / ", (0, format_time_js_1.formatTime)(durationInFrames / fps)] }), (0, jsx_runtime_1.jsx)("div", { style: xSpacer })] }), (0, jsx_runtime_1.jsx)("div", { style: flex1 }), playbackRates && canvasSize && ((0, jsx_runtime_1.jsx)(PlaybackrateControl_js_1.PlaybackrateControl, { canvasSize: canvasSize, playbackRates: playbackRates })), playbackRates && supportsFullscreen && allowFullscreen ? ((0, jsx_runtime_1.jsx)("div", { style: xSpacer })) : null, (0, jsx_runtime_1.jsx)("div", { style: fullscreen, children: supportsFullscreen && allowFullscreen ? ((0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": isFullscreen ? 'Exit fullscreen' : 'Enter Fullscreen', title: isFullscreen ? 'Exit fullscreen' : 'Enter Fullscreen', style: PlaybackrateControl_js_1.playerButtonStyle, onClick: isFullscreen
168
+ })) !== null && _b !== void 0 ? _b : ((0, jsx_runtime_1.jsx)(DefaultPlayPauseButton_js_1.DefaultPlayPauseButton, { buffering: buffering, playing: playing })))) }), showVolumeControls ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: xSpacer }), (0, jsx_runtime_1.jsx)(MediaVolumeSlider_js_1.MediaVolumeSlider, { renderMuteButton: renderMuteButton, renderVolumeSlider: renderVolumeSlider, displayVerticalVolumeSlider: displayVerticalVolumeSlider })] })) : null, (0, jsx_runtime_1.jsx)("div", { style: xSpacer }), (0, jsx_runtime_1.jsx)(PlayerTimeLabel_js_1.PlayerTimeLabel, { durationInFrames: durationInFrames, fps: fps, maxTimeLabelWidth: maxTimeLabelWidth }), (0, jsx_runtime_1.jsx)("div", { style: xSpacer })] }), (0, jsx_runtime_1.jsx)("div", { style: flex1 }), playbackRates && canvasSize && ((0, jsx_runtime_1.jsx)(PlaybackrateControl_js_1.PlaybackrateControl, { canvasSize: canvasSize, playbackRates: playbackRates })), playbackRates && supportsFullscreen && allowFullscreen ? ((0, jsx_runtime_1.jsx)("div", { style: xSpacer })) : null, (0, jsx_runtime_1.jsx)("div", { style: fullscreen, children: supportsFullscreen && allowFullscreen ? ((0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": isFullscreen ? 'Exit fullscreen' : 'Enter Fullscreen', title: isFullscreen ? 'Exit fullscreen' : 'Enter Fullscreen', style: PlaybackrateControl_js_1.playerButtonStyle, onClick: isFullscreen
181
169
  ? onExitFullscreenButtonClick
182
170
  : onFullscreenButtonClick, children: renderFullscreenButton === null ? ((0, jsx_runtime_1.jsx)(icons_js_1.FullscreenIcon, { isFullscreen: isFullscreen })) : (renderFullscreenButton({ isFullscreen })) })) : null })] }), (0, jsx_runtime_1.jsx)("div", { style: ySpacer }), (0, jsx_runtime_1.jsx)(PlayerSeekBar_js_1.PlayerSeekBar, { onSeekEnd: onSeekEnd, onSeekStart: onSeekStart, durationInFrames: durationInFrames, inFrame: inFrame, outFrame: outFrame })] }));
183
171
  };
@@ -119,18 +119,18 @@ const PlayerSeekBar = ({ durationInFrames, onSeekEnd, onSeekStart, inFrame, outF
119
119
  backgroundColor: 'white',
120
120
  left: Math.max(0, (frame / Math.max(1, durationInFrames - 1)) * width - KNOB_SIZE / 2),
121
121
  boxShadow: '0 0 2px black',
122
- opacity: Number(barHovered),
122
+ opacity: Number(barHovered || dragging.dragging),
123
123
  };
124
- }, [barHovered, durationInFrames, frame, width]);
124
+ }, [barHovered, dragging.dragging, durationInFrames, frame, width]);
125
125
  const fillStyle = (0, react_1.useMemo)(() => {
126
126
  return {
127
127
  height: BAR_HEIGHT,
128
128
  backgroundColor: 'rgba(255, 255, 255, 1)',
129
- width: ((frame - (inFrame !== null && inFrame !== void 0 ? inFrame : 0)) / (durationInFrames - 1)) * 100 + '%',
130
- marginLeft: ((inFrame !== null && inFrame !== void 0 ? inFrame : 0) / (durationInFrames - 1)) * 100 + '%',
129
+ width: ((frame - (inFrame !== null && inFrame !== void 0 ? inFrame : 0)) / (durationInFrames - 1)) * width,
130
+ marginLeft: ((inFrame !== null && inFrame !== void 0 ? inFrame : 0) / (durationInFrames - 1)) * width,
131
131
  borderRadius: BAR_HEIGHT / 2,
132
132
  };
133
- }, [durationInFrames, frame, inFrame]);
133
+ }, [durationInFrames, frame, inFrame, width]);
134
134
  const active = (0, react_1.useMemo)(() => {
135
135
  return {
136
136
  height: BAR_HEIGHT,
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ export declare const PlayerTimeLabel: React.FC<{
3
+ readonly maxTimeLabelWidth: number | null;
4
+ readonly durationInFrames: number;
5
+ readonly fps: number;
6
+ }>;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PlayerTimeLabel = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const remotion_1 = require("remotion");
7
+ const format_time_1 = require("./format-time");
8
+ const PlayerTimeLabel = ({ durationInFrames, maxTimeLabelWidth, fps }) => {
9
+ const frame = remotion_1.Internals.Timeline.useTimelinePosition();
10
+ const timeLabel = (0, react_1.useMemo)(() => {
11
+ return {
12
+ color: 'white',
13
+ fontFamily: 'sans-serif',
14
+ fontSize: 14,
15
+ maxWidth: maxTimeLabelWidth === null ? undefined : maxTimeLabelWidth,
16
+ overflow: 'hidden',
17
+ textOverflow: 'ellipsis',
18
+ };
19
+ }, [maxTimeLabelWidth]);
20
+ return ((0, jsx_runtime_1.jsxs)("div", { style: timeLabel, children: [(0, format_time_1.formatTime)(frame / fps), " / ", (0, format_time_1.formatTime)(durationInFrames / fps)] }));
21
+ };
22
+ exports.PlayerTimeLabel = PlayerTimeLabel;
@@ -49,5 +49,6 @@ declare const _default: React.ForwardRefExoticComponent<{
49
49
  readonly hideControlsWhenPointerDoesntMove: boolean | number;
50
50
  readonly overflowVisible: boolean;
51
51
  readonly browserMediaControlsBehavior: BrowserMediaControlsBehavior;
52
+ readonly overrideInternalClassName: string | undefined;
52
53
  } & React.RefAttributes<PlayerRef>>;
53
54
  export default _default;
@@ -40,7 +40,7 @@ if (reactVersion === '0') {
40
40
  throw new Error(`Version ${reactVersion} of "react" is not supported by Remotion`);
41
41
  }
42
42
  const doesReactVersionSupportSuspense = parseInt(reactVersion, 10) >= 18;
43
- const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps, clickToPlay, showVolumeControls, doubleClickToFullscreen, spaceKeyToPlayOrPause, errorFallback, playbackRate, renderLoading, renderPoster, className, moveToBeginningWhenEnded, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, showPosterWhenBuffering, inFrame, outFrame, initiallyShowControls, renderFullscreen: renderFullscreenButton, renderPlayPauseButton, renderMuteButton, renderVolumeSlider, alwaysShowControls, showPlaybackRateControl, posterFillMode, bufferStateDelayInMilliseconds, hideControlsWhenPointerDoesntMove, overflowVisible, browserMediaControlsBehavior, }, ref) => {
43
+ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps, clickToPlay, showVolumeControls, doubleClickToFullscreen, spaceKeyToPlayOrPause, errorFallback, playbackRate, renderLoading, renderPoster, className, moveToBeginningWhenEnded, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, showPosterWhenBuffering, inFrame, outFrame, initiallyShowControls, renderFullscreen: renderFullscreenButton, renderPlayPauseButton, renderMuteButton, renderVolumeSlider, alwaysShowControls, showPlaybackRateControl, posterFillMode, bufferStateDelayInMilliseconds, hideControlsWhenPointerDoesntMove, overflowVisible, browserMediaControlsBehavior, overrideInternalClassName, }, ref) => {
44
44
  var _a, _b, _c;
45
45
  const config = remotion_1.Internals.useUnsafeVideoConfig();
46
46
  const video = remotion_1.Internals.useVideo();
@@ -62,13 +62,14 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
62
62
  document.webkitFullscreenEnabled);
63
63
  }, []);
64
64
  const player = (0, use_player_js_1.usePlayer)();
65
+ const playerToggle = player.toggle;
65
66
  (0, use_playback_js_1.usePlayback)({
66
67
  loop,
67
68
  playbackRate,
68
69
  moveToBeginningWhenEnded,
69
70
  inFrame,
70
71
  outFrame,
71
- frameRef: player.remotionInternal_currentFrameRef,
72
+ getCurrentFrame: player.getCurrentFrame,
72
73
  browserMediaControlsBehavior,
73
74
  });
74
75
  (0, react_1.useEffect)(() => {
@@ -96,13 +97,8 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
96
97
  };
97
98
  }, []);
98
99
  const toggle = (0, react_1.useCallback)((e) => {
99
- if (player.isPlaying()) {
100
- player.pause();
101
- }
102
- else {
103
- player.play(e);
104
- }
105
- }, [player]);
100
+ playerToggle(e);
101
+ }, [playerToggle]);
106
102
  const requestFullscreen = (0, react_1.useCallback)(() => {
107
103
  if (!allowFullscreen) {
108
104
  throw new Error('allowFullscreen is false');
@@ -338,11 +334,13 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
338
334
  overflowVisible,
339
335
  });
340
336
  }, [canvasSize, config, layout, overflowVisible, scale]);
337
+ const playerPause = player.pause;
338
+ const playerDispatchError = player.emitter.dispatchError;
341
339
  const onError = (0, react_1.useCallback)((error) => {
342
- player.pause();
340
+ playerPause();
343
341
  // Pay attention to `this context`
344
- player.emitter.dispatchError(error);
345
- }, [player]);
342
+ playerDispatchError(error);
343
+ }, [playerDispatchError, playerPause]);
346
344
  const onFullscreenButtonClick = (0, react_1.useCallback)((e) => {
347
345
  e.stopPropagation();
348
346
  requestFullscreen();
@@ -419,11 +417,11 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
419
417
  showPosterWhenBuffering && showBufferIndicator && player.isPlaying(),
420
418
  ].some(Boolean);
421
419
  const { left, top, width, height, ...outerWithoutScale } = outer;
422
- const content = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: outer, onPointerDown: clickToPlay ? handlePointerDown : undefined, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, children: (0, jsx_runtime_1.jsxs)("div", { style: containerStyle, className: player_css_classname_js_1.PLAYER_CSS_CLASSNAME, children: [VideoComponent ? ((0, jsx_runtime_1.jsx)(error_boundary_js_1.ErrorBoundary, { onError: onError, errorFallback: errorFallback, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.CurrentScaleContext.Provider, { value: currentScale, children: (0, jsx_runtime_1.jsx)(VideoComponent, { ...((_c = video === null || video === void 0 ? void 0 : video.props) !== null && _c !== void 0 ? _c : {}), ...(inputProps !== null && inputProps !== void 0 ? inputProps : {}) }) }) })) : null, shouldShowPoster && posterFillMode === 'composition-size' ? ((0, jsx_runtime_1.jsx)("div", { style: {
420
+ const content = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: outer, onPointerDown: clickToPlay ? handlePointerDown : undefined, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, children: (0, jsx_runtime_1.jsxs)("div", { style: containerStyle, className: (0, player_css_classname_js_1.playerCssClassname)(overrideInternalClassName), children: [VideoComponent ? ((0, jsx_runtime_1.jsx)(error_boundary_js_1.ErrorBoundary, { onError: onError, errorFallback: errorFallback, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.CurrentScaleContext.Provider, { value: currentScale, children: (0, jsx_runtime_1.jsx)(VideoComponent, { ...((_c = video === null || video === void 0 ? void 0 : video.props) !== null && _c !== void 0 ? _c : {}), ...(inputProps !== null && inputProps !== void 0 ? inputProps : {}) }) }) })) : null, shouldShowPoster && posterFillMode === 'composition-size' ? ((0, jsx_runtime_1.jsx)("div", { style: {
423
421
  ...outerWithoutScale,
424
422
  width: config.width,
425
423
  height: config.height,
426
- }, onPointerDown: clickToPlay ? handlePointerDown : undefined, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, children: poster })) : null] }) }), shouldShowPoster && posterFillMode === 'player-size' ? ((0, jsx_runtime_1.jsx)("div", { style: outer, onPointerDown: clickToPlay ? handlePointerDown : undefined, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, children: poster })) : null, controls ? ((0, jsx_runtime_1.jsx)(PlayerControls_js_1.Controls, { fps: config.fps, durationInFrames: config.durationInFrames, player: player, containerRef: container, onFullscreenButtonClick: onFullscreenButtonClick, isFullscreen: isFullscreen, allowFullscreen: allowFullscreen, showVolumeControls: showVolumeControls, onExitFullscreenButtonClick: onExitFullscreenButtonClick, spaceKeyToPlayOrPause: spaceKeyToPlayOrPause, onSeekEnd: onSeekEnd, onSeekStart: onSeekStart, inFrame: inFrame, outFrame: outFrame, initiallyShowControls: initiallyShowControls, canvasSize: canvasSize, renderFullscreenButton: renderFullscreenButton, renderPlayPauseButton: renderPlayPauseButton, alwaysShowControls: alwaysShowControls, showPlaybackRateControl: showPlaybackRateControl, buffering: showBufferIndicator, hideControlsWhenPointerDoesntMove: hideControlsWhenPointerDoesntMove, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, onPointerDown: clickToPlay
424
+ }, onPointerDown: clickToPlay ? handlePointerDown : undefined, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, children: poster })) : null] }) }), shouldShowPoster && posterFillMode === 'player-size' ? ((0, jsx_runtime_1.jsx)("div", { style: outer, onPointerDown: clickToPlay ? handlePointerDown : undefined, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, children: poster })) : null, controls ? ((0, jsx_runtime_1.jsx)(PlayerControls_js_1.Controls, { fps: config.fps, playing: player.playing, toggle: player.toggle, durationInFrames: config.durationInFrames, containerRef: container, onFullscreenButtonClick: onFullscreenButtonClick, isFullscreen: isFullscreen, allowFullscreen: allowFullscreen, showVolumeControls: showVolumeControls, onExitFullscreenButtonClick: onExitFullscreenButtonClick, spaceKeyToPlayOrPause: spaceKeyToPlayOrPause, onSeekEnd: onSeekEnd, onSeekStart: onSeekStart, inFrame: inFrame, outFrame: outFrame, initiallyShowControls: initiallyShowControls, canvasSize: canvasSize, renderFullscreenButton: renderFullscreenButton, renderPlayPauseButton: renderPlayPauseButton, alwaysShowControls: alwaysShowControls, showPlaybackRateControl: showPlaybackRateControl, buffering: showBufferIndicator, hideControlsWhenPointerDoesntMove: hideControlsWhenPointerDoesntMove, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, onPointerDown: clickToPlay
427
425
  ? handlePointerDown
428
426
  : undefined, renderMuteButton: renderMuteButton, renderVolumeSlider: renderVolumeSlider })) : null] }));
429
427
  if (is_node_js_1.IS_NODE && !doesReactVersionSupportSuspense) {
@@ -15,6 +15,7 @@ export type ThumbnailProps<Schema extends AnyZodObject, Props extends Record<str
15
15
  readonly errorFallback?: ErrorFallback;
16
16
  readonly renderLoading?: RenderLoading;
17
17
  readonly className?: string;
18
+ readonly overrideInternalClassName?: string;
18
19
  };
19
20
  export type ThumbnailPropsWithoutZod<Props extends Record<string, unknown>> = ThumbnailProps<AnyZodObject, Props>;
20
21
  /**
@@ -11,7 +11,7 @@ const emitter_context_js_1 = require("./emitter-context.js");
11
11
  const event_emitter_js_1 = require("./event-emitter.js");
12
12
  const SharedPlayerContext_js_1 = require("./SharedPlayerContext.js");
13
13
  const ThumbnailUI_js_1 = __importDefault(require("./ThumbnailUI.js"));
14
- const ThumbnailFn = ({ frameToDisplay, style, inputProps, compositionHeight, compositionWidth, durationInFrames, fps, className, errorFallback = () => '⚠️', renderLoading, overflowVisible = false, ...componentProps }, ref) => {
14
+ const ThumbnailFn = ({ frameToDisplay, style, inputProps, compositionHeight, compositionWidth, durationInFrames, fps, className, errorFallback = () => '⚠️', renderLoading, overflowVisible = false, overrideInternalClassName, ...componentProps }, ref) => {
15
15
  if (typeof window !== 'undefined') {
16
16
  // eslint-disable-next-line react-hooks/rules-of-hooks
17
17
  (0, react_1.useLayoutEffect)(() => {
@@ -44,7 +44,7 @@ const ThumbnailFn = ({ frameToDisplay, style, inputProps, compositionHeight, com
44
44
  const passedInputProps = (0, react_1.useMemo)(() => {
45
45
  return inputProps !== null && inputProps !== void 0 ? inputProps : {};
46
46
  }, [inputProps]);
47
- return ((0, jsx_runtime_1.jsx)(remotion_1.Internals.IsPlayerContextProvider, { children: (0, jsx_runtime_1.jsx)(SharedPlayerContext_js_1.SharedPlayerContexts, { timelineContext: timelineState, component: Component, compositionHeight: compositionHeight, compositionWidth: compositionWidth, durationInFrames: durationInFrames, fps: fps, numberOfSharedAudioTags: 0, initiallyMuted: true, children: (0, jsx_runtime_1.jsx)(emitter_context_js_1.ThumbnailEmitterContext.Provider, { value: emitter, children: (0, jsx_runtime_1.jsx)(ThumbnailUI_js_1.default, { ref: rootRef, className: className, errorFallback: errorFallback, inputProps: passedInputProps, renderLoading: renderLoading, style: style, overflowVisible: overflowVisible }) }) }) }));
47
+ return ((0, jsx_runtime_1.jsx)(remotion_1.Internals.IsPlayerContextProvider, { children: (0, jsx_runtime_1.jsx)(SharedPlayerContext_js_1.SharedPlayerContexts, { timelineContext: timelineState, component: Component, compositionHeight: compositionHeight, compositionWidth: compositionWidth, durationInFrames: durationInFrames, fps: fps, numberOfSharedAudioTags: 0, initiallyMuted: true, children: (0, jsx_runtime_1.jsx)(emitter_context_js_1.ThumbnailEmitterContext.Provider, { value: emitter, children: (0, jsx_runtime_1.jsx)(ThumbnailUI_js_1.default, { ref: rootRef, className: className, errorFallback: errorFallback, inputProps: passedInputProps, renderLoading: renderLoading, style: style, overflowVisible: overflowVisible, overrideInternalClassName: overrideInternalClassName }) }) }) }));
48
48
  };
49
49
  const forward = react_1.forwardRef;
50
50
  /**
@@ -8,5 +8,6 @@ declare const _default: React.ForwardRefExoticComponent<{
8
8
  readonly renderLoading: RenderLoading | undefined;
9
9
  readonly className: string | undefined;
10
10
  readonly overflowVisible: boolean;
11
+ readonly overrideInternalClassName: string | undefined;
11
12
  } & React.RefAttributes<ThumbnailMethods>>;
12
13
  export default _default;
@@ -38,7 +38,7 @@ if (reactVersion === '0') {
38
38
  throw new Error(`Version ${reactVersion} of "react" is not supported by Remotion`);
39
39
  }
40
40
  const doesReactVersionSupportSuspense = parseInt(reactVersion, 10) >= 18;
41
- const ThumbnailUI = ({ style, inputProps, errorFallback, renderLoading, className, overflowVisible }, ref) => {
41
+ const ThumbnailUI = ({ style, inputProps, errorFallback, renderLoading, className, overflowVisible, overrideInternalClassName, }, ref) => {
42
42
  var _a, _b;
43
43
  const config = remotion_1.Internals.useUnsafeVideoConfig();
44
44
  const video = remotion_1.Internals.useVideo();
@@ -112,7 +112,7 @@ const ThumbnailUI = ({ style, inputProps, errorFallback, renderLoading, classNam
112
112
  if (!config) {
113
113
  return null;
114
114
  }
115
- const content = ((0, jsx_runtime_1.jsx)("div", { style: outer, children: (0, jsx_runtime_1.jsx)("div", { style: containerStyle, className: player_css_classname_js_1.PLAYER_CSS_CLASSNAME, children: VideoComponent ? ((0, jsx_runtime_1.jsx)(error_boundary_js_1.ErrorBoundary, { onError: onError, errorFallback: errorFallback, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.CurrentScaleContext.Provider, { value: currentScaleContext, children: (0, jsx_runtime_1.jsx)(VideoComponent, { ...((_b = video === null || video === void 0 ? void 0 : video.props) !== null && _b !== void 0 ? _b : {}), ...(inputProps !== null && inputProps !== void 0 ? inputProps : {}) }) }) })) : null }) }));
115
+ const content = ((0, jsx_runtime_1.jsx)("div", { style: outer, children: (0, jsx_runtime_1.jsx)("div", { style: containerStyle, className: (0, player_css_classname_js_1.playerCssClassname)(overrideInternalClassName), children: VideoComponent ? ((0, jsx_runtime_1.jsx)(error_boundary_js_1.ErrorBoundary, { onError: onError, errorFallback: errorFallback, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.CurrentScaleContext.Provider, { value: currentScaleContext, children: (0, jsx_runtime_1.jsx)(VideoComponent, { ...((_b = video === null || video === void 0 ? void 0 : video.props) !== null && _b !== void 0 ? _b : {}), ...(inputProps !== null && inputProps !== void 0 ? inputProps : {}) }) }) })) : null }) }));
116
116
  if (is_node_js_1.IS_NODE && !doesReactVersionSupportSuspense) {
117
117
  return ((0, jsx_runtime_1.jsx)("div", { ref: container, style: outerStyle, className: className, children: content }));
118
118
  }
@@ -30,16 +30,16 @@ export declare const PlayerInternals: {
30
30
  isPlaying: () => boolean;
31
31
  hasPlayed: boolean;
32
32
  isBuffering: () => boolean;
33
- remotionInternal_currentFrameRef: React.MutableRefObject<number>;
33
+ toggle: (e?: import("react").SyntheticEvent | PointerEvent) => void;
34
34
  };
35
- usePlayback: ({ loop, playbackRate, moveToBeginningWhenEnded, inFrame, outFrame, frameRef, browserMediaControlsBehavior, }: {
35
+ usePlayback: ({ loop, playbackRate, moveToBeginningWhenEnded, inFrame, outFrame, browserMediaControlsBehavior, getCurrentFrame, }: {
36
36
  loop: boolean;
37
37
  playbackRate: number;
38
38
  moveToBeginningWhenEnded: boolean;
39
39
  inFrame: number | null;
40
40
  outFrame: number | null;
41
- frameRef: React.MutableRefObject<number>;
42
41
  browserMediaControlsBehavior: import("./browser-mediasession.js").BrowserMediaControlsBehavior;
42
+ getCurrentFrame: import("./use-frame-imperative.js").GetCurrentFrame;
43
43
  }) => void;
44
44
  useElementSize: (ref: React.RefObject<HTMLElement>, options: {
45
45
  triggerOnWindowResize: boolean;
@@ -66,4 +66,5 @@ export declare const PlayerInternals: {
66
66
  BufferingIndicator: import("react").FC<{
67
67
  readonly type: "player" | "studio";
68
68
  }>;
69
+ useFrameImperative: () => import("./use-frame-imperative.js").GetCurrentFrame;
69
70
  };
package/dist/cjs/index.js CHANGED
@@ -7,6 +7,7 @@ const calculate_scale_js_1 = require("./calculate-scale.js");
7
7
  const emitter_context_js_1 = require("./emitter-context.js");
8
8
  const EmitterProvider_js_1 = require("./EmitterProvider.js");
9
9
  const event_emitter_js_1 = require("./event-emitter.js");
10
+ const use_frame_imperative_js_1 = require("./use-frame-imperative.js");
10
11
  const use_hover_state_js_1 = require("./use-hover-state.js");
11
12
  const use_playback_js_1 = require("./use-playback.js");
12
13
  const use_player_js_1 = require("./use-player.js");
@@ -26,4 +27,5 @@ exports.PlayerInternals = {
26
27
  updateAllElementsSizes: use_element_size_js_1.updateAllElementsSizes,
27
28
  PlayerEmitterProvider: EmitterProvider_js_1.PlayerEmitterProvider,
28
29
  BufferingIndicator: BufferingIndicator_js_1.BufferingIndicator,
30
+ useFrameImperative: use_frame_imperative_js_1.useFrameImperative,
29
31
  };
@@ -1 +1 @@
1
- export declare const PLAYER_CSS_CLASSNAME = "__remotion-player";
1
+ export declare const playerCssClassname: (override?: string) => string;
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PLAYER_CSS_CLASSNAME = void 0;
4
- exports.PLAYER_CSS_CLASSNAME = '__remotion-player';
3
+ exports.playerCssClassname = void 0;
4
+ const playerCssClassname = (override) => {
5
+ return override !== null && override !== void 0 ? override : '__remotion-player';
6
+ };
7
+ exports.playerCssClassname = playerCssClassname;
@@ -0,0 +1,2 @@
1
+ export type GetCurrentFrame = () => number;
2
+ export declare const useFrameImperative: () => GetCurrentFrame;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useFrameImperative = void 0;
4
+ const react_1 = require("react");
5
+ const remotion_1 = require("remotion");
6
+ const useFrameImperative = () => {
7
+ const frame = remotion_1.Internals.Timeline.useTimelinePosition();
8
+ const frameRef = (0, react_1.useRef)(frame);
9
+ frameRef.current = frame;
10
+ const getCurrentFrame = (0, react_1.useCallback)(() => {
11
+ return frameRef.current;
12
+ }, []);
13
+ return getCurrentFrame;
14
+ };
15
+ exports.useFrameImperative = useFrameImperative;
@@ -1,10 +1,11 @@
1
1
  import type { BrowserMediaControlsBehavior } from './browser-mediasession.js';
2
- export declare const usePlayback: ({ loop, playbackRate, moveToBeginningWhenEnded, inFrame, outFrame, frameRef, browserMediaControlsBehavior, }: {
2
+ import type { GetCurrentFrame } from './use-frame-imperative.js';
3
+ export declare const usePlayback: ({ loop, playbackRate, moveToBeginningWhenEnded, inFrame, outFrame, browserMediaControlsBehavior, getCurrentFrame, }: {
3
4
  loop: boolean;
4
5
  playbackRate: number;
5
6
  moveToBeginningWhenEnded: boolean;
6
7
  inFrame: number | null;
7
8
  outFrame: number | null;
8
- frameRef: React.MutableRefObject<number>;
9
9
  browserMediaControlsBehavior: BrowserMediaControlsBehavior;
10
+ getCurrentFrame: GetCurrentFrame;
10
11
  }) => void;
@@ -8,7 +8,7 @@ const browser_mediasession_js_1 = require("./browser-mediasession.js");
8
8
  const calculate_next_frame_js_1 = require("./calculate-next-frame.js");
9
9
  const is_backgrounded_js_1 = require("./is-backgrounded.js");
10
10
  const use_player_js_1 = require("./use-player.js");
11
- const usePlayback = ({ loop, playbackRate, moveToBeginningWhenEnded, inFrame, outFrame, frameRef, browserMediaControlsBehavior, }) => {
11
+ const usePlayback = ({ loop, playbackRate, moveToBeginningWhenEnded, inFrame, outFrame, browserMediaControlsBehavior, getCurrentFrame, }) => {
12
12
  const config = remotion_1.Internals.useUnsafeVideoConfig();
13
13
  const frame = remotion_1.Internals.Timeline.useTimelinePosition();
14
14
  const { playing, pause, emitter } = (0, use_player_js_1.usePlayer)();
@@ -70,7 +70,7 @@ const usePlayback = ({ loop, playbackRate, moveToBeginningWhenEnded, inFrame, ou
70
70
  const time = performance.now() - startedTime;
71
71
  const actualLastFrame = outFrame !== null && outFrame !== void 0 ? outFrame : config.durationInFrames - 1;
72
72
  const actualFirstFrame = inFrame !== null && inFrame !== void 0 ? inFrame : 0;
73
- const currentFrame = frameRef.current;
73
+ const currentFrame = getCurrentFrame();
74
74
  const { nextFrame, framesToAdvance, hasEnded } = (0, calculate_next_frame_js_1.calculateNextFrame)({
75
75
  time,
76
76
  currentFrame,
@@ -82,7 +82,7 @@ const usePlayback = ({ loop, playbackRate, moveToBeginningWhenEnded, inFrame, ou
82
82
  shouldLoop: loop,
83
83
  });
84
84
  framesAdvanced += framesToAdvance;
85
- if (nextFrame !== frameRef.current &&
85
+ if (nextFrame !== getCurrentFrame() &&
86
86
  (!hasEnded || moveToBeginningWhenEnded)) {
87
87
  setFrame((c) => ({ ...c, [config.id]: nextFrame }));
88
88
  }
@@ -147,20 +147,20 @@ const usePlayback = ({ loop, playbackRate, moveToBeginningWhenEnded, inFrame, ou
147
147
  outFrame,
148
148
  moveToBeginningWhenEnded,
149
149
  isBackgroundedRef,
150
- frameRef,
150
+ getCurrentFrame,
151
151
  buffering,
152
152
  context,
153
153
  ]);
154
154
  (0, react_1.useEffect)(() => {
155
155
  const interval = setInterval(() => {
156
- if (lastTimeUpdateEvent.current === frameRef.current) {
156
+ if (lastTimeUpdateEvent.current === getCurrentFrame()) {
157
157
  return;
158
158
  }
159
- emitter.dispatchTimeUpdate({ frame: frameRef.current });
160
- lastTimeUpdateEvent.current = frameRef.current;
159
+ emitter.dispatchTimeUpdate({ frame: getCurrentFrame() });
160
+ lastTimeUpdateEvent.current = getCurrentFrame();
161
161
  }, 250);
162
162
  return () => clearInterval(interval);
163
- }, [emitter, frameRef]);
163
+ }, [emitter, getCurrentFrame]);
164
164
  (0, react_1.useEffect)(() => {
165
165
  emitter.dispatchFrameUpdate({ frame });
166
166
  }, [emitter, frame]);
@@ -15,10 +15,7 @@ type UsePlayerMethods = {
15
15
  isPlaying: () => boolean;
16
16
  hasPlayed: boolean;
17
17
  isBuffering: () => boolean;
18
- /**
19
- * @deprecated Remotion internal API
20
- */
21
- remotionInternal_currentFrameRef: React.MutableRefObject<number>;
18
+ toggle: (e?: SyntheticEvent | PointerEvent) => void;
22
19
  };
23
20
  export declare const usePlayer: () => UsePlayerMethods;
24
21
  export {};
@@ -4,6 +4,7 @@ exports.usePlayer = void 0;
4
4
  const react_1 = require("react");
5
5
  const remotion_1 = require("remotion");
6
6
  const emitter_context_js_1 = require("./emitter-context.js");
7
+ const use_frame_imperative_js_1 = require("./use-frame-imperative.js");
7
8
  const usePlayer = () => {
8
9
  var _a;
9
10
  const [playing, setPlaying, imperativePlaying] = remotion_1.Internals.Timeline.usePlayingState();
@@ -123,6 +124,15 @@ const usePlayer = () => {
123
124
  };
124
125
  });
125
126
  }, [videoId, imperativePlaying, lastFrame, setFrame]);
127
+ const getCurrentFrame = (0, use_frame_imperative_js_1.useFrameImperative)();
128
+ const toggle = (0, react_1.useCallback)((e) => {
129
+ if (imperativePlaying.current) {
130
+ pause();
131
+ }
132
+ else {
133
+ play(e);
134
+ }
135
+ }, [imperativePlaying, pause, play]);
126
136
  const returnValue = (0, react_1.useMemo)(() => {
127
137
  return {
128
138
  frameBack,
@@ -134,27 +144,30 @@ const usePlayer = () => {
134
144
  pause,
135
145
  seek,
136
146
  isFirstFrame,
137
- getCurrentFrame: () => frameRef.current,
147
+ getCurrentFrame,
138
148
  isPlaying: () => imperativePlaying.current,
139
149
  isBuffering: () => buffering.current,
140
150
  pauseAndReturnToPlayStart,
141
151
  hasPlayed,
142
152
  remotionInternal_currentFrameRef: frameRef,
153
+ toggle,
143
154
  };
144
155
  }, [
156
+ buffering,
157
+ emitter,
145
158
  frameBack,
146
159
  frameForward,
160
+ getCurrentFrame,
161
+ hasPlayed,
162
+ imperativePlaying,
163
+ isFirstFrame,
147
164
  isLastFrame,
148
- emitter,
149
- playing,
150
- play,
151
165
  pause,
152
- seek,
153
- isFirstFrame,
154
166
  pauseAndReturnToPlayStart,
155
- hasPlayed,
156
- imperativePlaying,
157
- buffering,
167
+ play,
168
+ playing,
169
+ seek,
170
+ toggle,
158
171
  ]);
159
172
  return returnValue;
160
173
  };