@remotion/player 3.2.12-crf.7 → 3.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Player.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { ComponentType, MutableRefObject } from 'react';
2
2
  import React from 'react';
3
3
  import type { CompProps } from 'remotion';
4
4
  import type { PlayerRef } from './player-methods';
5
- import type { RenderLoading } from './PlayerUI';
5
+ import type { RenderLoading, RenderPoster } from './PlayerUI';
6
6
  declare type PropsIfHasProps<Props> = {} extends Props ? {
7
7
  inputProps?: Props;
8
8
  } : {
@@ -31,9 +31,14 @@ export declare type PlayerProps<T> = {
31
31
  renderLoading?: RenderLoading;
32
32
  moveToBeginningWhenEnded?: boolean;
33
33
  className?: string;
34
+ initialFrame?: number;
35
+ renderPoster?: RenderPoster;
36
+ showPosterWhenPaused?: boolean;
37
+ showPosterWhenEnded?: boolean;
38
+ showPosterWhenUnplayed?: boolean;
34
39
  } & PropsIfHasProps<T> & CompProps<T>;
35
40
  export declare const componentOrNullIfLazy: <T>(props: CompProps<T>) => ComponentType<T> | null;
36
- export declare const PlayerFn: <T>({ durationInFrames, compositionHeight, compositionWidth, fps, inputProps, style, controls, loop, autoPlay, showVolumeControls, allowFullscreen, clickToPlay, doubleClickToFullscreen, spaceKeyToPlayOrPause, moveToBeginningWhenEnded, numberOfSharedAudioTags, errorFallback, playbackRate, renderLoading, className, ...componentProps }: PlayerProps<T>, ref: MutableRefObject<PlayerRef>) => JSX.Element;
41
+ export declare const PlayerFn: <T>({ durationInFrames, compositionHeight, compositionWidth, fps, inputProps, style, controls, loop, autoPlay, showVolumeControls, allowFullscreen, clickToPlay, doubleClickToFullscreen, spaceKeyToPlayOrPause, moveToBeginningWhenEnded, numberOfSharedAudioTags, errorFallback, playbackRate, renderLoading, className, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, initialFrame, renderPoster, ...componentProps }: PlayerProps<T>, ref: MutableRefObject<PlayerRef>) => JSX.Element;
37
42
  declare module 'react' {
38
43
  function forwardRef<T, P = {}>(render: (props: P, ref: React.MutableRefObject<T>) => React.ReactElement | null): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
39
44
  }
package/dist/Player.js CHANGED
@@ -11,6 +11,7 @@ const emitter_context_1 = require("./emitter-context");
11
11
  const event_emitter_1 = require("./event-emitter");
12
12
  const player_css_classname_1 = require("./player-css-classname");
13
13
  const PlayerUI_1 = __importDefault(require("./PlayerUI"));
14
+ const validate_initial_frame_1 = require("./utils/validate-initial-frame");
14
15
  const validate_playbackrate_1 = require("./utils/validate-playbackrate");
15
16
  const volume_persistance_1 = require("./volume-persistance");
16
17
  const componentOrNullIfLazy = (props) => {
@@ -20,7 +21,7 @@ const componentOrNullIfLazy = (props) => {
20
21
  return null;
21
22
  };
22
23
  exports.componentOrNullIfLazy = componentOrNullIfLazy;
23
- 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, ...componentProps }, ref) => {
24
+ 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, initialFrame, renderPoster, ...componentProps }, ref) => {
24
25
  if (typeof window !== 'undefined') {
25
26
  // eslint-disable-next-line react-hooks/rules-of-hooks
26
27
  (0, react_1.useLayoutEffect)(() => {
@@ -40,7 +41,8 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
40
41
  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.`);
41
42
  }
42
43
  const component = remotion_1.Internals.useLazyComponent(componentProps);
43
- const [frame, setFrame] = (0, react_1.useState)(0);
44
+ (0, validate_initial_frame_1.validateInitialFrame)({ initialFrame, durationInFrames });
45
+ const [frame, setFrame] = (0, react_1.useState)(() => initialFrame !== null && initialFrame !== void 0 ? initialFrame : 0);
44
46
  const [playing, setPlaying] = (0, react_1.useState)(false);
45
47
  const [rootId] = (0, react_1.useState)('player-comp');
46
48
  const [emitter] = (0, react_1.useState)(() => new event_emitter_1.PlayerEmitter());
@@ -180,13 +182,16 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
180
182
  const passedInputProps = (0, react_1.useMemo)(() => {
181
183
  return inputProps !== null && inputProps !== void 0 ? inputProps : {};
182
184
  }, [inputProps]);
183
- (0, react_1.useLayoutEffect)(() => {
184
- // Inject CSS only on client, and also only after the Player has hydrated
185
- remotion_1.Internals.CSSUtils.injectCSS(remotion_1.Internals.CSSUtils.makeDefaultCSS(`.${player_css_classname_1.PLAYER_CSS_CLASSNAME}`, '#fff'));
186
- }, []);
185
+ if (typeof window !== 'undefined') {
186
+ // eslint-disable-next-line react-hooks/rules-of-hooks
187
+ (0, react_1.useLayoutEffect)(() => {
188
+ // Inject CSS only on client, and also only after the Player has hydrated
189
+ remotion_1.Internals.CSSUtils.injectCSS(remotion_1.Internals.CSSUtils.makeDefaultCSS(`.${player_css_classname_1.PLAYER_CSS_CLASSNAME}`, '#fff'));
190
+ }, []);
191
+ }
187
192
  return ((0, jsx_runtime_1.jsx)(remotion_1.Internals.CanUseRemotionHooksProvider, { children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.Timeline.TimelineContext.Provider, { value: timelineContextValue, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.Timeline.SetTimelineContext.Provider, { value: setTimelineContextValue, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.CompositionManager.Provider, { value: compositionManagerContext, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.MediaVolumeContext.Provider, { value: mediaVolumeContextValue, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.SetMediaVolumeContext.Provider, { value: setMediaVolumeContextValue, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.SharedAudioContextProvider, { numberOfAudioTags: numberOfSharedAudioTags, children: (0, jsx_runtime_1.jsx)(emitter_context_1.PlayerEventEmitterContext.Provider, { value: emitter, children: (0, jsx_runtime_1.jsx)(PlayerUI_1.default, { ref: rootRef, renderLoading: renderLoading, autoPlay: Boolean(autoPlay), loop: Boolean(loop), controls: Boolean(controls), errorFallback: errorFallback, style: style, inputProps: passedInputProps, allowFullscreen: Boolean(allowFullscreen), moveToBeginningWhenEnded: Boolean(moveToBeginningWhenEnded), clickToPlay: typeof clickToPlay === 'boolean'
188
193
  ? clickToPlay
189
- : Boolean(controls), showVolumeControls: Boolean(showVolumeControls), setMediaVolume: setMediaVolumeAndPersist, mediaVolume: mediaVolume, mediaMuted: mediaMuted, doubleClickToFullscreen: Boolean(doubleClickToFullscreen), setMediaMuted: setMediaMuted, spaceKeyToPlayOrPause: Boolean(spaceKeyToPlayOrPause), playbackRate: playbackRate, className: className !== null && className !== void 0 ? className : undefined }) }) }) }) }) }) }) }) }));
194
+ : Boolean(controls), showVolumeControls: Boolean(showVolumeControls), setMediaVolume: setMediaVolumeAndPersist, mediaVolume: mediaVolume, mediaMuted: mediaMuted, doubleClickToFullscreen: Boolean(doubleClickToFullscreen), setMediaMuted: setMediaMuted, spaceKeyToPlayOrPause: Boolean(spaceKeyToPlayOrPause), playbackRate: playbackRate, className: className !== null && className !== void 0 ? className : undefined, showPosterWhenUnplayed: Boolean(showPosterWhenUnplayed), showPosterWhenEnded: Boolean(showPosterWhenEnded), showPosterWhenPaused: Boolean(showPosterWhenPaused), renderPoster: renderPoster }) }) }) }) }) }) }) }) }));
190
195
  };
191
196
  exports.PlayerFn = PlayerFn;
192
197
  exports.Player = (0, react_1.forwardRef)(exports.PlayerFn);
@@ -22,4 +22,6 @@ export declare const Controls: React.FC<{
22
22
  allowFullscreen: boolean;
23
23
  onExitFullscreenButtonClick: MouseEventHandler<HTMLButtonElement>;
24
24
  spaceKeyToPlayOrPause: boolean;
25
+ onSeekEnd: () => void;
26
+ onSeekStart: () => void;
25
27
  }>;
@@ -62,7 +62,7 @@ const timeLabel = {
62
62
  fontFamily: 'sans-serif',
63
63
  fontSize: 14,
64
64
  };
65
- const Controls = ({ durationInFrames, hovered, isFullscreen, fps, player, showVolumeControls, onFullscreenButtonClick, allowFullscreen, onExitFullscreenButtonClick, spaceKeyToPlayOrPause, }) => {
65
+ const Controls = ({ durationInFrames, hovered, isFullscreen, fps, player, showVolumeControls, onFullscreenButtonClick, allowFullscreen, onExitFullscreenButtonClick, spaceKeyToPlayOrPause, onSeekEnd, onSeekStart, }) => {
66
66
  const playButtonRef = (0, react_1.useRef)(null);
67
67
  const frame = remotion_1.Internals.Timeline.useTimelinePosition();
68
68
  const [supportsFullscreen, setSupportsFullscreen] = (0, react_1.useState)(false);
@@ -90,6 +90,6 @@ const Controls = ({ durationInFrames, hovered, isFullscreen, fps, player, showVo
90
90
  }, []);
91
91
  return ((0, jsx_runtime_1.jsxs)("div", { style: containerCss, children: [(0, jsx_runtime_1.jsxs)("div", { style: controlsRow, children: [(0, jsx_runtime_1.jsxs)("div", { style: leftPartStyle, children: [(0, jsx_runtime_1.jsx)("button", { ref: playButtonRef, type: "button", style: buttonStyle, onClick: player.playing ? player.pause : player.play, "aria-label": player.playing ? 'Pause video' : 'Play video', title: player.playing ? 'Pause video' : 'Play video', children: player.playing ? (0, jsx_runtime_1.jsx)(icons_1.PauseIcon, {}) : (0, jsx_runtime_1.jsx)(icons_1.PlayIcon, {}) }), 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_1.MediaVolumeSlider, {})] })) : null, (0, jsx_runtime_1.jsx)("div", { style: xSpacer }), (0, jsx_runtime_1.jsxs)("div", { style: timeLabel, children: [(0, format_time_1.formatTime)(frame / fps), " / ", (0, format_time_1.formatTime)(durationInFrames / fps)] }), (0, jsx_runtime_1.jsx)("div", { style: xSpacer })] }), (0, jsx_runtime_1.jsx)("div", { style: flex1 }), (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: buttonStyle, onClick: isFullscreen
92
92
  ? onExitFullscreenButtonClick
93
- : onFullscreenButtonClick, children: (0, jsx_runtime_1.jsx)(icons_1.FullscreenIcon, { minimized: !isFullscreen }) })) : null })] }), (0, jsx_runtime_1.jsx)("div", { style: ySpacer }), (0, jsx_runtime_1.jsx)(PlayerSeekBar_1.PlayerSeekBar, { durationInFrames: durationInFrames })] }));
93
+ : onFullscreenButtonClick, children: (0, jsx_runtime_1.jsx)(icons_1.FullscreenIcon, { minimized: !isFullscreen }) })) : null })] }), (0, jsx_runtime_1.jsx)("div", { style: ySpacer }), (0, jsx_runtime_1.jsx)(PlayerSeekBar_1.PlayerSeekBar, { onSeekEnd: onSeekEnd, onSeekStart: onSeekStart, durationInFrames: durationInFrames })] }));
94
94
  };
95
95
  exports.Controls = Controls;
@@ -1,4 +1,6 @@
1
1
  /// <reference types="react" />
2
2
  export declare const PlayerSeekBar: React.FC<{
3
3
  durationInFrames: number;
4
+ onSeekStart: () => void;
5
+ onSeekEnd: () => void;
4
6
  }>;
@@ -41,7 +41,7 @@ const findBodyInWhichDivIsLocated = (div) => {
41
41
  }
42
42
  return current;
43
43
  };
44
- const PlayerSeekBar = ({ durationInFrames }) => {
44
+ const PlayerSeekBar = ({ durationInFrames, onSeekEnd, onSeekStart }) => {
45
45
  const containerRef = (0, react_1.useRef)(null);
46
46
  const barHovered = (0, use_hover_state_1.useHoverState)(containerRef);
47
47
  const size = (0, use_element_size_1.useElementSize)(containerRef, {
@@ -64,7 +64,8 @@ const PlayerSeekBar = ({ durationInFrames }) => {
64
64
  dragging: true,
65
65
  wasPlaying: playing,
66
66
  });
67
- }, [size, durationInFrames, pause, seek, playing]);
67
+ onSeekStart();
68
+ }, [size, durationInFrames, pause, seek, playing, onSeekStart]);
68
69
  const onPointerMove = (0, react_1.useCallback)((e) => {
69
70
  var _a;
70
71
  if (!size) {
@@ -89,7 +90,8 @@ const PlayerSeekBar = ({ durationInFrames }) => {
89
90
  else {
90
91
  pause();
91
92
  }
92
- }, [dragging, pause, play]);
93
+ onSeekEnd();
94
+ }, [dragging, onSeekEnd, pause, play]);
93
95
  (0, react_1.useEffect)(() => {
94
96
  if (!dragging.dragging) {
95
97
  return;
@@ -7,6 +7,7 @@ export declare type RenderLoading = (canvas: {
7
7
  height: number;
8
8
  width: number;
9
9
  }) => React.ReactChild;
10
+ export declare type RenderPoster = RenderLoading;
10
11
  declare const _default: (props: {
11
12
  controls: boolean;
12
13
  loop: boolean;
@@ -24,8 +25,12 @@ declare const _default: (props: {
24
25
  mediaVolume: number;
25
26
  errorFallback: ErrorFallback;
26
27
  playbackRate: number;
27
- renderLoading?: RenderLoading | undefined;
28
+ renderLoading: RenderLoading | undefined;
29
+ renderPoster: RenderLoading | undefined;
28
30
  className: string | undefined;
29
31
  moveToBeginningWhenEnded: boolean;
32
+ showPosterWhenPaused: boolean;
33
+ showPosterWhenEnded: boolean;
34
+ showPosterWhenUnplayed: boolean;
30
35
  } & React.RefAttributes<PlayerRef | null>) => React.ReactElement<any, string | React.JSXElementConstructor<any>> | null;
31
36
  export default _default;
package/dist/PlayerUI.js CHANGED
@@ -42,7 +42,7 @@ if (reactVersion === '0') {
42
42
  throw new Error(`Version ${reactVersion} of "react" is not supported by Remotion`);
43
43
  }
44
44
  const doesReactVersionSupportSuspense = parseInt(reactVersion, 10) >= 18;
45
- const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps, clickToPlay, showVolumeControls, mediaVolume, mediaMuted, doubleClickToFullscreen, setMediaMuted, setMediaVolume, spaceKeyToPlayOrPause, errorFallback, playbackRate, renderLoading, className, moveToBeginningWhenEnded, }, ref) => {
45
+ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps, clickToPlay, showVolumeControls, mediaVolume, mediaMuted, doubleClickToFullscreen, setMediaMuted, setMediaVolume, spaceKeyToPlayOrPause, errorFallback, playbackRate, renderLoading, renderPoster, className, moveToBeginningWhenEnded, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, }, ref) => {
46
46
  var _a, _b, _c;
47
47
  const config = remotion_1.Internals.useUnsafeVideoConfig();
48
48
  const video = remotion_1.Internals.useVideo();
@@ -55,6 +55,7 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
55
55
  const [hasPausedToResume, setHasPausedToResume] = (0, react_1.useState)(false);
56
56
  const [shouldAutoplay, setShouldAutoPlay] = (0, react_1.useState)(autoPlay);
57
57
  const [isFullscreen, setIsFullscreen] = (0, react_1.useState)(() => false);
58
+ const [seeking, setSeeking] = (0, react_1.useState)(false);
58
59
  (0, use_playback_1.usePlayback)({
59
60
  loop,
60
61
  playbackRate,
@@ -292,6 +293,12 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
292
293
  const onSingleClick = (0, react_1.useCallback)((e) => {
293
294
  toggle(e);
294
295
  }, [toggle]);
296
+ const onSeekStart = (0, react_1.useCallback)(() => {
297
+ setSeeking(true);
298
+ }, []);
299
+ const onSeekEnd = (0, react_1.useCallback)(() => {
300
+ setSeeking(false);
301
+ }, []);
295
302
  const onDoubleClick = (0, react_1.useCallback)(() => {
296
303
  if (isFullscreen) {
297
304
  exitFullscreen();
@@ -310,7 +317,22 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
310
317
  if (!config) {
311
318
  return null;
312
319
  }
313
- const content = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: outer, onClick: clickToPlay ? handleClick : undefined, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, children: (0, jsx_runtime_1.jsx)("div", { style: containerStyle, className: player_css_classname_1.PLAYER_CSS_CLASSNAME, children: VideoComponent ? ((0, jsx_runtime_1.jsx)(error_boundary_1.ErrorBoundary, { onError: onError, errorFallback: errorFallback, children: (0, jsx_runtime_1.jsx)(VideoComponent, { ...((_b = video === null || video === void 0 ? void 0 : video.defaultProps) !== null && _b !== void 0 ? _b : {}), ...((_c = inputProps) !== null && _c !== void 0 ? _c : {}) }) })) : null }) }), controls ? ((0, jsx_runtime_1.jsx)(PlayerControls_1.Controls, { fps: config.fps, durationInFrames: config.durationInFrames, hovered: hovered, player: player, onFullscreenButtonClick: onFullscreenButtonClick, isFullscreen: isFullscreen, allowFullscreen: allowFullscreen, showVolumeControls: showVolumeControls, onExitFullscreenButtonClick: onExitFullscreenButtonClick, spaceKeyToPlayOrPause: spaceKeyToPlayOrPause })) : null] }));
320
+ const poster = renderPoster
321
+ ? renderPoster({
322
+ height: outerStyle.height,
323
+ width: outerStyle.width,
324
+ })
325
+ : null;
326
+ if (poster === undefined) {
327
+ throw new TypeError('renderPoster() must return a React element, but undefined was returned');
328
+ }
329
+ const shouldShowPoster = poster &&
330
+ [
331
+ showPosterWhenPaused && !player.isPlaying() && !seeking,
332
+ showPosterWhenEnded && player.isLastFrame && !player.isPlaying(),
333
+ showPosterWhenUnplayed && !player.hasPlayed && !player.isPlaying(),
334
+ ].some(Boolean);
335
+ const content = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: outer, onClick: clickToPlay ? handleClick : undefined, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, children: (0, jsx_runtime_1.jsx)("div", { style: containerStyle, className: player_css_classname_1.PLAYER_CSS_CLASSNAME, children: VideoComponent ? ((0, jsx_runtime_1.jsx)(error_boundary_1.ErrorBoundary, { onError: onError, errorFallback: errorFallback, children: (0, jsx_runtime_1.jsx)(VideoComponent, { ...((_b = video === null || video === void 0 ? void 0 : video.defaultProps) !== null && _b !== void 0 ? _b : {}), ...((_c = inputProps) !== null && _c !== void 0 ? _c : {}) }) })) : null }) }), shouldShowPoster ? ((0, jsx_runtime_1.jsx)("div", { style: outer, onClick: clickToPlay ? handleClick : undefined, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, children: poster })) : null, controls ? ((0, jsx_runtime_1.jsx)(PlayerControls_1.Controls, { fps: config.fps, durationInFrames: config.durationInFrames, hovered: hovered, player: player, onFullscreenButtonClick: onFullscreenButtonClick, isFullscreen: isFullscreen, allowFullscreen: allowFullscreen, showVolumeControls: showVolumeControls, onExitFullscreenButtonClick: onExitFullscreenButtonClick, spaceKeyToPlayOrPause: spaceKeyToPlayOrPause, onSeekEnd: onSeekEnd, onSeekStart: onSeekStart })) : null] }));
314
336
  if (is_node_1.IS_NODE && !doesReactVersionSupportSuspense) {
315
337
  return ((0, jsx_runtime_1.jsx)("div", { ref: container, style: outerStyle, className: className, children: content }));
316
338
  }
@@ -15,5 +15,5 @@ export declare class ErrorBoundary extends React.Component<{
15
15
  hasError: Error;
16
16
  };
17
17
  componentDidCatch(error: Error): void;
18
- render(): string | number | boolean | JSX.Element | React.ReactFragment | null | undefined;
18
+ render(): string | number | boolean | React.ReactFragment | JSX.Element | null | undefined;
19
19
  }
package/dist/index.d.ts CHANGED
@@ -1,9 +1,8 @@
1
- /// <reference types="react" />
2
1
  import type { CallbackListener, EventTypes } from './event-emitter';
3
2
  import { PlayerEmitter } from './event-emitter';
4
3
  export { ErrorFallback, Player, PlayerProps } from './Player';
5
4
  export { PlayerMethods, PlayerRef } from './player-methods';
6
- export type { RenderLoading } from './PlayerUI';
5
+ export type { RenderLoading, RenderPoster } from './PlayerUI';
7
6
  export { PreviewSize } from './utils/preview-size';
8
7
  export { Size } from './utils/use-element-size';
9
8
  export type { CallbackListener, EventTypes };
@@ -23,6 +22,7 @@ export declare const PlayerInternals: {
23
22
  seek: (newFrame: number) => void;
24
23
  getCurrentFrame: () => number;
25
24
  isPlaying: () => boolean;
25
+ hasPlayed: boolean;
26
26
  };
27
27
  usePlayback: ({ loop, playbackRate, moveToBeginningWhenEnded, inFrame, outFrame, }: {
28
28
  loop: boolean;
@@ -51,7 +51,7 @@ test('No durationInFrames should give errors', () => {
51
51
  durationInFrames: undefined, component: test_utils_1.HelloWorld, controls: true, showVolumeControls: true }));
52
52
  }
53
53
  catch (e) {
54
- expect(e.message).toMatch(/The "durationInFrames" prop of the <Player\/> component must be a number, but you passed a value of type undefined/);
54
+ expect(e.message).toMatch(/durationInFrames` must be a number, but is undefined/);
55
55
  }
56
56
  });
57
57
  test('Invalid playbackRate should give error', () => {
@@ -13,6 +13,7 @@ declare type UsePlayerMethods = {
13
13
  seek: (newFrame: number) => void;
14
14
  getCurrentFrame: () => number;
15
15
  isPlaying: () => boolean;
16
+ hasPlayed: boolean;
16
17
  };
17
18
  export declare const usePlayer: () => UsePlayerMethods;
18
19
  export {};
@@ -7,8 +7,9 @@ const emitter_context_1 = require("./emitter-context");
7
7
  const usePlayer = () => {
8
8
  var _a;
9
9
  const [playing, setPlaying, imperativePlaying] = remotion_1.Internals.Timeline.usePlayingState();
10
+ const [hasPlayed, setHasPlayed] = (0, react_1.useState)(false);
10
11
  const frame = remotion_1.Internals.Timeline.useTimelinePosition();
11
- const playStart = (0, react_1.useRef)(0);
12
+ const playStart = (0, react_1.useRef)(frame);
12
13
  const setFrame = remotion_1.Internals.Timeline.useTimelineSetFrame();
13
14
  const setTimelinePosition = remotion_1.Internals.Timeline.useTimelineSetFrame();
14
15
  const audioContext = (0, react_1.useContext)(remotion_1.Internals.SharedAudioContext);
@@ -32,6 +33,7 @@ const usePlayer = () => {
32
33
  if (imperativePlaying.current) {
33
34
  return;
34
35
  }
36
+ setHasPlayed(true);
35
37
  if (isLastFrame) {
36
38
  seek(0);
37
39
  }
@@ -109,6 +111,7 @@ const usePlayer = () => {
109
111
  getCurrentFrame: () => frameRef.current,
110
112
  isPlaying: () => imperativePlaying.current,
111
113
  pauseAndReturnToPlayStart,
114
+ hasPlayed,
112
115
  };
113
116
  }, [
114
117
  frameBack,
@@ -122,6 +125,7 @@ const usePlayer = () => {
122
125
  isFirstFrame,
123
126
  pauseAndReturnToPlayStart,
124
127
  imperativePlaying,
128
+ hasPlayed,
125
129
  ]);
126
130
  return returnValue;
127
131
  };
@@ -0,0 +1,4 @@
1
+ export declare const validateInitialFrame: ({ initialFrame, durationInFrames, }: {
2
+ initialFrame: unknown;
3
+ durationInFrames: unknown;
4
+ }) => void;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateInitialFrame = void 0;
4
+ const validateInitialFrame = ({ initialFrame, durationInFrames, }) => {
5
+ if (typeof durationInFrames !== 'number') {
6
+ throw new Error(`\`durationInFrames\` must be a number, but is ${JSON.stringify(durationInFrames)}`);
7
+ }
8
+ if (typeof initialFrame === 'undefined') {
9
+ return;
10
+ }
11
+ if (typeof initialFrame !== 'number') {
12
+ throw new Error(`\`initialFrame\` must be a number, but is ${JSON.stringify(initialFrame)}`);
13
+ }
14
+ if (Number.isNaN(initialFrame)) {
15
+ throw new Error(`\`initialFrame\` must be a number, but is NaN`);
16
+ }
17
+ if (!Number.isFinite(initialFrame)) {
18
+ throw new Error(`\`initialFrame\` must be a number, but is Infinity`);
19
+ }
20
+ if (initialFrame % 1 !== 0) {
21
+ throw new Error(`\`initialFrame\` must be an integer, but is ${JSON.stringify(initialFrame)}`);
22
+ }
23
+ if (initialFrame > durationInFrames - 1) {
24
+ throw new Error(`\`initialFrame\` must be less or equal than \`durationInFrames - 1\`, but is ${JSON.stringify(initialFrame)}`);
25
+ }
26
+ };
27
+ exports.validateInitialFrame = validateInitialFrame;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/player",
3
- "version": "3.2.12-crf.7+93db68411",
3
+ "version": "3.2.14",
4
4
  "description": "Remotion Player",
5
5
  "main": "dist/index.js",
6
6
  "sideEffects": false,
@@ -28,7 +28,7 @@
28
28
  ],
29
29
  "license": "SEE LICENSE IN LICENSE.md",
30
30
  "dependencies": {
31
- "remotion": "3.2.12-crf.7+93db68411"
31
+ "remotion": "3.2.14"
32
32
  },
33
33
  "peerDependencies": {
34
34
  "react": ">=16.8.0",
@@ -63,5 +63,5 @@
63
63
  "publishConfig": {
64
64
  "access": "public"
65
65
  },
66
- "gitHead": "93db68411377be8a8345f52ad3ea4d281424decc"
66
+ "gitHead": "544ef952c9df79b015496d68202776787f15cd93"
67
67
  }