@mux/mux-react-native-player 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/MuxReactNativePlayer.podspec +37 -0
  2. package/README.md +134 -0
  3. package/android/build.gradle +33 -0
  4. package/android/src/main/AndroidManifest.xml +1 -0
  5. package/android/src/main/java/com/mux/reactnativeplayer/MuxReactNativePlayerModule.kt +135 -0
  6. package/android/src/main/java/com/mux/reactnativeplayer/MuxVideoRecords.kt +174 -0
  7. package/android/src/main/java/com/mux/reactnativeplayer/MuxVideoView.kt +452 -0
  8. package/android/src/main/res/layout/mux_video_player_view.xml +6 -0
  9. package/assets/MuxRobot_02.gif +0 -0
  10. package/assets/MuxRobot_02@2x.gif +0 -0
  11. package/assets/MuxRobot_03.gif +0 -0
  12. package/assets/MuxRobot_03@2x.gif +0 -0
  13. package/assets/MuxRobot_04.gif +0 -0
  14. package/assets/MuxRobot_04@2x.gif +0 -0
  15. package/assets/MuxRobot_05.gif +0 -0
  16. package/assets/MuxRobot_05@2x.gif +0 -0
  17. package/build/MuxVideoControls.d.ts +21 -0
  18. package/build/MuxVideoControls.d.ts.map +1 -0
  19. package/build/MuxVideoControls.js +1032 -0
  20. package/build/MuxVideoPlayer.d.ts +59 -0
  21. package/build/MuxVideoPlayer.d.ts.map +1 -0
  22. package/build/MuxVideoPlayer.js +265 -0
  23. package/build/MuxVideoView.d.ts +39 -0
  24. package/build/MuxVideoView.d.ts.map +1 -0
  25. package/build/MuxVideoView.js +254 -0
  26. package/build/NativeMuxVideoView.d.ts +5 -0
  27. package/build/NativeMuxVideoView.d.ts.map +1 -0
  28. package/build/NativeMuxVideoView.js +4 -0
  29. package/build/index.d.ts +6 -0
  30. package/build/index.d.ts.map +1 -0
  31. package/build/index.js +3 -0
  32. package/build/normalizeSource.d.ts +7 -0
  33. package/build/normalizeSource.d.ts.map +1 -0
  34. package/build/normalizeSource.js +76 -0
  35. package/build/screenOrientation.d.ts +3 -0
  36. package/build/screenOrientation.d.ts.map +1 -0
  37. package/build/screenOrientation.js +38 -0
  38. package/build/types.d.ts +170 -0
  39. package/build/types.d.ts.map +1 -0
  40. package/build/types.js +1 -0
  41. package/expo-module.config.json +13 -0
  42. package/ios/MuxReactNativePlayerModule.swift +139 -0
  43. package/ios/MuxVideoRecords.swift +212 -0
  44. package/ios/MuxVideoView.swift +502 -0
  45. package/package.json +69 -0
  46. package/plugin/index.d.ts +11 -0
  47. package/plugin/index.js +1 -0
  48. package/plugin/withMuxReactNativePlayer.js +203 -0
  49. package/src/MuxVideoControls.tsx +1772 -0
  50. package/src/MuxVideoPlayer.ts +338 -0
  51. package/src/MuxVideoView.tsx +412 -0
  52. package/src/NativeMuxVideoView.ts +15 -0
  53. package/src/index.ts +32 -0
  54. package/src/normalizeSource.ts +101 -0
  55. package/src/screenOrientation.ts +46 -0
  56. package/src/types.ts +228 -0
@@ -0,0 +1,59 @@
1
+ import type { MuxNativeViewRef, MuxPlayerStatus, MuxPlaybackStatus, MuxSourceErrorEvent, MuxSourceLoadEvent, MuxStatusChangeEvent, MuxTimeUpdateEvent, MuxVideoSource, NormalizedMuxVideoSource } from './types';
2
+ type Listener = () => void;
3
+ export type MuxVideoPlayerSnapshot = {
4
+ source?: NormalizedMuxVideoSource;
5
+ shouldPlay: boolean;
6
+ muted: boolean;
7
+ volume: number;
8
+ loop: boolean;
9
+ playbackRate: number;
10
+ status: MuxPlayerStatus;
11
+ };
12
+ export declare class MuxVideoPlayer {
13
+ private source?;
14
+ private nativeRef?;
15
+ private listeners;
16
+ private pendingCommands;
17
+ private statusState;
18
+ private snapshot;
19
+ private shouldPlay;
20
+ private resumeAt;
21
+ muted: boolean;
22
+ volume: number;
23
+ loop: boolean;
24
+ playbackRate: number;
25
+ constructor(source?: MuxVideoSource);
26
+ get status(): MuxPlaybackStatus;
27
+ get currentTime(): number;
28
+ get duration(): number;
29
+ get bufferedPosition(): number;
30
+ get error(): string | undefined;
31
+ play(): Promise<void>;
32
+ pause(): Promise<void>;
33
+ replay(): Promise<void>;
34
+ seekBy(seconds: number): Promise<void>;
35
+ seekTo(seconds: number): Promise<void>;
36
+ setMuted(muted: boolean): Promise<void>;
37
+ setVolume(volume: number): Promise<void>;
38
+ setLoop(loop: boolean): Promise<void>;
39
+ setPlaybackRate(rate: number): Promise<void>;
40
+ setCaptionTrack(trackId: string | null): Promise<void>;
41
+ preload(): Promise<void>;
42
+ replace(source: MuxVideoSource): void;
43
+ release(): Promise<void>;
44
+ _attachNativeRef(ref: MuxNativeViewRef | null): void;
45
+ _subscribe: (listener: Listener) => (() => void);
46
+ _getSnapshot: () => MuxVideoPlayerSnapshot;
47
+ _handleStatusChange(event: MuxStatusChangeEvent): void;
48
+ _handleTimeUpdate(event: MuxTimeUpdateEvent): void;
49
+ _handleSourceLoad(event: MuxSourceLoadEvent): void;
50
+ _markResumePoint(): void;
51
+ _handleSourceError(event: MuxSourceErrorEvent): void;
52
+ private runNativeCommand;
53
+ private emitChange;
54
+ private createSnapshot;
55
+ private updateSnapshot;
56
+ }
57
+ export declare function createMuxVideoPlayer(source?: MuxVideoSource): MuxVideoPlayer;
58
+ export {};
59
+ //# sourceMappingURL=MuxVideoPlayer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MuxVideoPlayer.d.ts","sourceRoot":"","sources":["../src/MuxVideoPlayer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,265 @@
1
+ import { normalizeMuxVideoSource } from './normalizeSource';
2
+ const idleStatus = {
3
+ status: 'idle',
4
+ currentTime: 0,
5
+ duration: 0,
6
+ bufferedPosition: 0,
7
+ muted: false,
8
+ volume: 1,
9
+ loop: false,
10
+ playbackRate: 1,
11
+ captionTracks: [],
12
+ selectedCaptionTrackId: null,
13
+ };
14
+ export class MuxVideoPlayer {
15
+ source;
16
+ nativeRef;
17
+ listeners = new Set();
18
+ pendingCommands = [];
19
+ statusState = { ...idleStatus };
20
+ snapshot = this.createSnapshot();
21
+ shouldPlay = false;
22
+ resumeAt = null;
23
+ muted = false;
24
+ volume = 1;
25
+ loop = false;
26
+ playbackRate = 1;
27
+ constructor(source) {
28
+ if (source != null) {
29
+ this.source = normalizeMuxVideoSource(source);
30
+ this.statusState = { ...this.statusState, status: 'loading' };
31
+ this.updateSnapshot();
32
+ }
33
+ }
34
+ get status() {
35
+ return this.statusState.status;
36
+ }
37
+ get currentTime() {
38
+ return this.statusState.currentTime;
39
+ }
40
+ get duration() {
41
+ return this.statusState.duration;
42
+ }
43
+ get bufferedPosition() {
44
+ return this.statusState.bufferedPosition;
45
+ }
46
+ get error() {
47
+ return this.statusState.error;
48
+ }
49
+ play() {
50
+ this.shouldPlay = true;
51
+ this.updateSnapshot();
52
+ this.emitChange();
53
+ return this.runNativeCommand('play');
54
+ }
55
+ pause() {
56
+ this.shouldPlay = false;
57
+ this.updateSnapshot();
58
+ this.emitChange();
59
+ return this.runNativeCommand('pause');
60
+ }
61
+ replay() {
62
+ this.shouldPlay = true;
63
+ this.updateSnapshot();
64
+ this.emitChange();
65
+ return this.runNativeCommand('replay');
66
+ }
67
+ seekBy(seconds) {
68
+ return this.runNativeCommand('seekBy', seconds);
69
+ }
70
+ seekTo(seconds) {
71
+ return this.runNativeCommand('seekTo', seconds);
72
+ }
73
+ setMuted(muted) {
74
+ this.muted = muted;
75
+ this.updateSnapshot();
76
+ this.emitChange();
77
+ return this.runNativeCommand('setMuted', muted);
78
+ }
79
+ setVolume(volume) {
80
+ this.volume = clamp(volume, 0, 1);
81
+ this.updateSnapshot();
82
+ this.emitChange();
83
+ return this.runNativeCommand('setVolume', this.volume);
84
+ }
85
+ setLoop(loop) {
86
+ this.loop = loop;
87
+ this.updateSnapshot();
88
+ this.emitChange();
89
+ return this.runNativeCommand('setLoop', loop);
90
+ }
91
+ setPlaybackRate(rate) {
92
+ this.playbackRate = clamp(rate, 0.25, 4);
93
+ this.updateSnapshot();
94
+ this.emitChange();
95
+ return this.runNativeCommand('setPlaybackRate', this.playbackRate);
96
+ }
97
+ setCaptionTrack(trackId) {
98
+ this.statusState = {
99
+ ...this.statusState,
100
+ selectedCaptionTrackId: trackId,
101
+ };
102
+ this.updateSnapshot();
103
+ this.emitChange();
104
+ return this.runNativeCommand('setCaptionTrack', trackId);
105
+ }
106
+ preload() {
107
+ return this.runNativeCommand('preload');
108
+ }
109
+ replace(source) {
110
+ this.source = normalizeMuxVideoSource(source);
111
+ this.resumeAt = null;
112
+ this.statusState = { ...idleStatus, muted: this.muted, volume: this.volume, loop: this.loop, playbackRate: this.playbackRate, status: 'loading' };
113
+ this.updateSnapshot();
114
+ this.emitChange();
115
+ }
116
+ release() {
117
+ this.shouldPlay = false;
118
+ this.source = undefined;
119
+ this.resumeAt = null;
120
+ this.statusState = { ...idleStatus };
121
+ this.updateSnapshot();
122
+ this.emitChange();
123
+ return this.runNativeCommand('release');
124
+ }
125
+ _attachNativeRef(ref) {
126
+ this.nativeRef = ref;
127
+ if (!ref || this.pendingCommands.length === 0) {
128
+ return;
129
+ }
130
+ const pending = this.pendingCommands.splice(0);
131
+ for (const command of pending) {
132
+ command().catch(() => {
133
+ // The caller already received a resolved promise while the command was queued.
134
+ });
135
+ }
136
+ }
137
+ _subscribe = (listener) => {
138
+ this.listeners.add(listener);
139
+ return () => {
140
+ this.listeners.delete(listener);
141
+ };
142
+ };
143
+ _getSnapshot = () => this.snapshot;
144
+ _handleStatusChange(event) {
145
+ this.statusState = {
146
+ ...event,
147
+ captionTracks: event.captionTracks ?? this.statusState.captionTracks ?? [],
148
+ selectedCaptionTrackId: normalizeCaptionTrackId(event.selectedCaptionTrackId, this.statusState.selectedCaptionTrackId ?? null),
149
+ };
150
+ if (this.statusState.status === 'playing') {
151
+ this.shouldPlay = true;
152
+ }
153
+ else if (this.statusState.status === 'ended' || this.statusState.status === 'idle' || this.statusState.status === 'error') {
154
+ this.shouldPlay = false;
155
+ }
156
+ this.muted = this.statusState.muted;
157
+ this.volume = this.statusState.volume;
158
+ this.loop = this.statusState.loop;
159
+ this.playbackRate = this.statusState.playbackRate;
160
+ this.updateSnapshot();
161
+ this.emitChange();
162
+ }
163
+ _handleTimeUpdate(event) {
164
+ this.statusState = {
165
+ ...this.statusState,
166
+ currentTime: event.currentTime,
167
+ duration: event.duration,
168
+ bufferedPosition: event.bufferedPosition,
169
+ };
170
+ this.updateSnapshot();
171
+ this.emitChange();
172
+ }
173
+ _handleSourceLoad(event) {
174
+ const resumeAt = this.resumeAt;
175
+ this.resumeAt = null;
176
+ this.statusState = {
177
+ ...this.statusState,
178
+ status: this.statusState.status === 'loading' ? 'ready' : this.statusState.status,
179
+ duration: event.duration,
180
+ captionTracks: event.captionTracks ?? this.statusState.captionTracks ?? [],
181
+ selectedCaptionTrackId: normalizeCaptionTrackId(event.selectedCaptionTrackId, this.statusState.selectedCaptionTrackId ?? null),
182
+ error: undefined,
183
+ };
184
+ this.updateSnapshot();
185
+ this.emitChange();
186
+ if (resumeAt !== null && resumeAt > 0.5 && resumeAt < event.duration - 0.5) {
187
+ this.runNativeCommand('seekTo', resumeAt).catch(() => {
188
+ // Resume seek is best-effort; user can scrub if it fails.
189
+ });
190
+ }
191
+ if (this.shouldPlay) {
192
+ this.runNativeCommand('play').catch(() => {
193
+ // Source load means the command is retryable through a later user action.
194
+ });
195
+ }
196
+ }
197
+ _markResumePoint() {
198
+ if (this.statusState.currentTime > 0) {
199
+ this.resumeAt = this.statusState.currentTime;
200
+ }
201
+ }
202
+ _handleSourceError(event) {
203
+ this.statusState = {
204
+ ...this.statusState,
205
+ status: 'error',
206
+ error: event.message,
207
+ };
208
+ this.updateSnapshot();
209
+ this.emitChange();
210
+ }
211
+ runNativeCommand(command, value) {
212
+ const call = () => {
213
+ const ref = this.nativeRef;
214
+ if (!ref) {
215
+ return Promise.resolve();
216
+ }
217
+ if (typeof ref[command] !== 'function') {
218
+ return Promise.resolve();
219
+ }
220
+ if (value === undefined) {
221
+ return ref[command].call(ref);
222
+ }
223
+ return ref[command].call(ref, value);
224
+ };
225
+ if (!this.nativeRef) {
226
+ this.pendingCommands.push(call);
227
+ return Promise.resolve();
228
+ }
229
+ return call();
230
+ }
231
+ emitChange() {
232
+ for (const listener of this.listeners) {
233
+ listener();
234
+ }
235
+ }
236
+ createSnapshot() {
237
+ return {
238
+ source: this.source,
239
+ shouldPlay: this.shouldPlay,
240
+ muted: this.muted,
241
+ volume: this.volume,
242
+ loop: this.loop,
243
+ playbackRate: this.playbackRate,
244
+ status: this.statusState,
245
+ };
246
+ }
247
+ updateSnapshot() {
248
+ this.snapshot = this.createSnapshot();
249
+ }
250
+ }
251
+ export function createMuxVideoPlayer(source) {
252
+ return new MuxVideoPlayer(source);
253
+ }
254
+ function clamp(value, min, max) {
255
+ if (!Number.isFinite(value)) {
256
+ return min;
257
+ }
258
+ return Math.min(max, Math.max(min, value));
259
+ }
260
+ function normalizeCaptionTrackId(trackId, fallback) {
261
+ if (trackId === undefined) {
262
+ return fallback;
263
+ }
264
+ return trackId === '' ? null : trackId;
265
+ }
@@ -0,0 +1,39 @@
1
+ import * as React from 'react';
2
+ import { MuxVideoPlayer } from './MuxVideoPlayer';
3
+ import type { MuxVideoSource } from './types';
4
+ export type MuxVideoViewRef = {
5
+ play: () => Promise<void>;
6
+ pause: () => Promise<void>;
7
+ replay: () => Promise<void>;
8
+ seekBy: (seconds: number) => Promise<void>;
9
+ seekTo: (seconds: number) => Promise<void>;
10
+ setMuted: (muted: boolean) => Promise<void>;
11
+ setVolume: (volume: number) => Promise<void>;
12
+ setLoop: (loop: boolean) => Promise<void>;
13
+ setPlaybackRate: (rate: number) => Promise<void>;
14
+ setCaptionTrack: (trackId: string | null) => Promise<void>;
15
+ preload: () => Promise<void>;
16
+ release: () => Promise<void>;
17
+ enterFullscreen: () => void;
18
+ exitFullscreen: () => void;
19
+ };
20
+ export declare const MuxVideoView: React.ForwardRefExoticComponent<import("react-native").ViewProps & {
21
+ player: import("./MuxVideoPlayer").MuxVideoPlayer;
22
+ controls?: import("./types").MuxVideoControls;
23
+ controlsTheme?: import("./types").MuxVideoControlsTheme;
24
+ robots?: import("./types").MuxVideoRobotsConfig;
25
+ nativeControls?: boolean;
26
+ contentFit?: import("./types").MuxContentFit;
27
+ allowsFullscreen?: boolean;
28
+ allowsPictureInPicture?: boolean;
29
+ timeUpdateEventInterval?: number;
30
+ startupBufferDuration?: number;
31
+ onStatusChange?: (event: import("./types").MuxStatusChangeEvent) => void;
32
+ onPlayingChange?: (event: import("./types").MuxPlayingChangeEvent) => void;
33
+ onTimeUpdate?: (event: import("./types").MuxTimeUpdateEvent) => void;
34
+ onSourceLoad?: (event: import("./types").MuxSourceLoadEvent) => void;
35
+ onSourceError?: (event: import("./types").MuxSourceErrorEvent) => void;
36
+ onFullscreenChange?: (isFullscreen: boolean) => void;
37
+ } & React.RefAttributes<MuxVideoViewRef>>;
38
+ export declare function useMuxVideoPlayer(source?: MuxVideoSource, setup?: (player: MuxVideoPlayer) => void): MuxVideoPlayer;
39
+ //# sourceMappingURL=MuxVideoView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MuxVideoView.d.ts","sourceRoot":"","sources":["../src/MuxVideoView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAOlD,OAAO,KAAK,EAIV,cAAc,EAGf,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;yCA+TxB,CAAC;AAIF,wBAAgB,iBAAiB,CAC/B,MAAM,CAAC,EAAE,cAAc,EACvB,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,GACvC,cAAc,CA8BhB"}
@@ -0,0 +1,254 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import * as React from 'react';
3
+ import { Dimensions, Modal, Platform, StyleSheet, View } from 'react-native';
4
+ const IOS_LANDSCAPE_SAFE_INSET = 50;
5
+ import { MuxVideoPlayer } from './MuxVideoPlayer';
6
+ import { MuxVideoControls } from './MuxVideoControls';
7
+ import NativeMuxVideoView from './NativeMuxVideoView';
8
+ import { lockOrientationLandscape, unlockOrientation, } from './screenOrientation';
9
+ export const MuxVideoView = React.forwardRef(({ player, controls, controlsTheme, robots, nativeControls = true, contentFit = 'contain', allowsFullscreen = true, allowsPictureInPicture = false, timeUpdateEventInterval = 0.5, startupBufferDuration, onStatusChange, onPlayingChange, onTimeUpdate, onSourceLoad, onSourceError, onFullscreenChange, ...viewProps }, ref) => {
10
+ const nativeRef = React.useRef(null);
11
+ const [fullscreen, setFullscreen] = React.useState(false);
12
+ const [fullscreenReady, setFullscreenReady] = React.useState(false);
13
+ const enteredViaRotationRef = React.useRef(false);
14
+ const suppressRotationEntryRef = React.useRef(false);
15
+ const pendingExitRef = React.useRef(false);
16
+ const [generatedSummary, setGeneratedSummary] = React.useState();
17
+ const [generatedChapters, setGeneratedChapters] = React.useState();
18
+ const [generatedKeyMoments, setGeneratedKeyMoments] = React.useState();
19
+ const snapshot = React.useSyncExternalStore(player._subscribe, player._getSnapshot, player._getSnapshot);
20
+ const enterFullscreen = React.useCallback(() => {
21
+ player._markResumePoint();
22
+ enteredViaRotationRef.current = false;
23
+ suppressRotationEntryRef.current = false;
24
+ pendingExitRef.current = false;
25
+ const { width, height } = Dimensions.get('window');
26
+ setFullscreenReady(width > height);
27
+ setFullscreen(true);
28
+ }, [player]);
29
+ const beginExit = React.useCallback(() => {
30
+ player._markResumePoint();
31
+ enteredViaRotationRef.current = false;
32
+ suppressRotationEntryRef.current = true;
33
+ setFullscreenReady(false);
34
+ const { width, height } = Dimensions.get('window');
35
+ if (width <= height) {
36
+ pendingExitRef.current = false;
37
+ setFullscreen(false);
38
+ return;
39
+ }
40
+ pendingExitRef.current = true;
41
+ void unlockOrientation();
42
+ setTimeout(() => {
43
+ if (pendingExitRef.current) {
44
+ pendingExitRef.current = false;
45
+ setFullscreen(false);
46
+ }
47
+ }, 600);
48
+ }, [player]);
49
+ const exitFullscreen = React.useCallback(() => {
50
+ beginExit();
51
+ }, [beginExit]);
52
+ const toggleFullscreen = React.useCallback(() => {
53
+ if (fullscreen) {
54
+ beginExit();
55
+ return;
56
+ }
57
+ player._markResumePoint();
58
+ enteredViaRotationRef.current = false;
59
+ suppressRotationEntryRef.current = false;
60
+ pendingExitRef.current = false;
61
+ const { width, height } = Dimensions.get('window');
62
+ setFullscreenReady(width > height);
63
+ setFullscreen(true);
64
+ }, [beginExit, fullscreen, player]);
65
+ React.useEffect(() => {
66
+ if (!allowsFullscreen) {
67
+ return;
68
+ }
69
+ const subscription = Dimensions.addEventListener('change', ({ window }) => {
70
+ const isLandscape = window.width > window.height;
71
+ if (!isLandscape) {
72
+ suppressRotationEntryRef.current = false;
73
+ if (pendingExitRef.current) {
74
+ pendingExitRef.current = false;
75
+ setFullscreenReady(false);
76
+ setFullscreen(false);
77
+ return;
78
+ }
79
+ }
80
+ else if (pendingExitRef.current) {
81
+ pendingExitRef.current = false;
82
+ }
83
+ setFullscreenReady(isLandscape);
84
+ setFullscreen(prev => {
85
+ if (isLandscape && !prev) {
86
+ if (suppressRotationEntryRef.current) {
87
+ return prev;
88
+ }
89
+ enteredViaRotationRef.current = true;
90
+ player._markResumePoint();
91
+ return true;
92
+ }
93
+ if (!isLandscape && prev && enteredViaRotationRef.current) {
94
+ enteredViaRotationRef.current = false;
95
+ player._markResumePoint();
96
+ return false;
97
+ }
98
+ return prev;
99
+ });
100
+ });
101
+ return () => subscription.remove();
102
+ }, [allowsFullscreen, player]);
103
+ React.useImperativeHandle(ref, () => ({
104
+ play: () => player.play(),
105
+ pause: () => player.pause(),
106
+ replay: () => player.replay(),
107
+ seekBy: seconds => player.seekBy(seconds),
108
+ seekTo: seconds => player.seekTo(seconds),
109
+ setMuted: muted => player.setMuted(muted),
110
+ setVolume: volume => player.setVolume(volume),
111
+ setLoop: loop => player.setLoop(loop),
112
+ setPlaybackRate: rate => player.setPlaybackRate(rate),
113
+ setCaptionTrack: trackId => player.setCaptionTrack(trackId),
114
+ preload: () => player.preload(),
115
+ release: () => player.release(),
116
+ enterFullscreen,
117
+ exitFullscreen,
118
+ }), [player, enterFullscreen, exitFullscreen]);
119
+ React.useEffect(() => {
120
+ player._attachNativeRef(nativeRef.current);
121
+ return () => {
122
+ player._attachNativeRef(null);
123
+ };
124
+ }, [player, fullscreen]);
125
+ React.useEffect(() => {
126
+ onFullscreenChange?.(fullscreen);
127
+ if (!fullscreen) {
128
+ return;
129
+ }
130
+ const entryViaRotation = enteredViaRotationRef.current;
131
+ if (!entryViaRotation) {
132
+ void lockOrientationLandscape();
133
+ }
134
+ return () => {
135
+ if (!entryViaRotation) {
136
+ void unlockOrientation();
137
+ }
138
+ };
139
+ }, [fullscreen, onFullscreenChange]);
140
+ const controlsMode = controls ?? (nativeControls ? 'native' : 'none');
141
+ const showCustomControls = controlsMode === 'custom';
142
+ const showNativeControls = controlsMode === 'native';
143
+ const controlsRobots = React.useMemo(() => robots && robots.assetId == null && snapshot.source?.assetId
144
+ ? { ...robots, assetId: snapshot.source.assetId }
145
+ : robots, [robots, snapshot.source?.assetId]);
146
+ const robotsAssetId = controlsRobots?.assetId;
147
+ React.useEffect(() => {
148
+ setGeneratedSummary(undefined);
149
+ setGeneratedChapters(undefined);
150
+ setGeneratedKeyMoments(undefined);
151
+ }, [robotsAssetId]);
152
+ const sharedNativeProps = {
153
+ startupBufferDuration,
154
+ source: snapshot.source,
155
+ playWhenReady: snapshot.shouldPlay,
156
+ muted: snapshot.muted,
157
+ volume: snapshot.volume,
158
+ loop: snapshot.loop,
159
+ playbackRate: snapshot.playbackRate,
160
+ contentFit,
161
+ allowsFullscreen,
162
+ allowsPictureInPicture,
163
+ timeUpdateEventInterval,
164
+ onStatusChange: (event) => {
165
+ player._handleStatusChange(event.nativeEvent);
166
+ onStatusChange?.(event.nativeEvent);
167
+ },
168
+ onPlayingChange: (event) => {
169
+ onPlayingChange?.(event.nativeEvent);
170
+ },
171
+ onTimeUpdate: (event) => {
172
+ player._handleTimeUpdate(event.nativeEvent);
173
+ onTimeUpdate?.(event.nativeEvent);
174
+ },
175
+ onSourceLoad: (event) => {
176
+ player._handleSourceLoad(event.nativeEvent);
177
+ onSourceLoad?.(event.nativeEvent);
178
+ },
179
+ onSourceError: (event) => {
180
+ player._handleSourceError(event.nativeEvent);
181
+ onSourceError?.(event.nativeEvent);
182
+ },
183
+ };
184
+ if (showNativeControls) {
185
+ return (_jsx(NativeMuxVideoView, { ...viewProps, ...sharedNativeProps, ref: nativeRef, nativeControls: true }));
186
+ }
187
+ const renderManagedBody = (containerStyle, controlsInset = { left: 0, right: 0 }) => (_jsxs(View, { style: containerStyle, children: [_jsx(NativeMuxVideoView, { ...sharedNativeProps, ref: nativeRef, nativeControls: false, style: StyleSheet.absoluteFill }), showCustomControls ? (_jsx(View, { pointerEvents: "box-none", style: {
188
+ position: 'absolute',
189
+ top: 0,
190
+ bottom: 0,
191
+ left: controlsInset.left,
192
+ right: controlsInset.right,
193
+ }, children: _jsx(MuxVideoControls, { player: player, status: snapshot.status, shouldPlay: snapshot.shouldPlay, theme: controlsTheme, robots: controlsRobots, allowsFullscreen: allowsFullscreen, isFullscreen: fullscreen, onToggleFullscreen: allowsFullscreen ? toggleFullscreen : undefined, generatedSummary: generatedSummary, generatedChapters: generatedChapters, generatedKeyMoments: generatedKeyMoments, onGeneratedSummaryChange: setGeneratedSummary, onGeneratedChaptersChange: setGeneratedChapters, onGeneratedKeyMomentsChange: setGeneratedKeyMoments }) })) : null] }));
194
+ const inlineContainerStyle = [
195
+ styles.customControlsContainer,
196
+ viewProps.style,
197
+ ];
198
+ if (!fullscreen) {
199
+ return renderManagedBody(inlineContainerStyle);
200
+ }
201
+ const fullscreenControlsInset = Platform.OS === 'ios' && fullscreenReady
202
+ ? { left: IOS_LANDSCAPE_SAFE_INSET, right: IOS_LANDSCAPE_SAFE_INSET }
203
+ : { left: 0, right: 0 };
204
+ return (_jsxs(_Fragment, { children: [_jsx(View, { ...viewProps, style: [inlineContainerStyle, styles.placeholderInline], pointerEvents: "none" }), _jsx(Modal, { animationType: "fade", visible: true, supportedOrientations: [
205
+ 'portrait',
206
+ 'landscape',
207
+ 'landscape-left',
208
+ 'landscape-right',
209
+ ], statusBarTranslucent: true, presentationStyle: "overFullScreen", transparent: true, onRequestClose: exitFullscreen, children: _jsxs(View, { style: styles.fullscreenContainer, children: [renderManagedBody(StyleSheet.absoluteFill, fullscreenControlsInset), !fullscreenReady ? (_jsx(View, { pointerEvents: "none", style: [StyleSheet.absoluteFill, styles.fullscreenCover] })) : null] }) })] }));
210
+ });
211
+ MuxVideoView.displayName = 'MuxVideoView';
212
+ export function useMuxVideoPlayer(source, setup) {
213
+ const playerRef = React.useRef(null);
214
+ const sourceKey = source == null ? undefined : JSON.stringify(source);
215
+ if (playerRef.current == null) {
216
+ playerRef.current = new MuxVideoPlayer(source);
217
+ }
218
+ React.useEffect(() => {
219
+ if (source != null) {
220
+ playerRef.current?.replace(source);
221
+ }
222
+ }, [sourceKey]);
223
+ React.useEffect(() => {
224
+ if (playerRef.current && setup) {
225
+ setup(playerRef.current);
226
+ }
227
+ }, [setup]);
228
+ React.useEffect(() => {
229
+ const player = playerRef.current;
230
+ return () => {
231
+ player?.release().catch(() => {
232
+ // React cleanup may run after the native view has already detached.
233
+ });
234
+ };
235
+ }, []);
236
+ return playerRef.current;
237
+ }
238
+ const styles = StyleSheet.create({
239
+ customControlsContainer: {
240
+ backgroundColor: 'transparent',
241
+ overflow: 'hidden',
242
+ position: 'relative',
243
+ },
244
+ placeholderInline: {
245
+ backgroundColor: 'transparent',
246
+ },
247
+ fullscreenContainer: {
248
+ backgroundColor: '#000',
249
+ flex: 1,
250
+ },
251
+ fullscreenCover: {
252
+ backgroundColor: '#000',
253
+ },
254
+ });
@@ -0,0 +1,5 @@
1
+ import * as React from 'react';
2
+ import type { MuxNativeViewRef, NativeMuxVideoViewProps } from './types';
3
+ declare const NativeMuxVideoView: React.ComponentType<NativeMuxVideoViewProps & React.RefAttributes<MuxNativeViewRef>>;
4
+ export default NativeMuxVideoView;
5
+ //# sourceMappingURL=NativeMuxVideoView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NativeMuxVideoView.d.ts","sourceRoot":"","sources":["../src/NativeMuxVideoView.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAIzE,QAAA,MAAM,kBAAkB,EAAuD,KAAK,CAAC,aAAa,CAChG,uBAAuB,GAAG,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAChE,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { requireNativeViewManager, requireOptionalNativeModule, } from 'expo-modules-core';
2
+ requireOptionalNativeModule('MuxReactNativePlayer');
3
+ const NativeMuxVideoView = requireNativeViewManager('MuxReactNativePlayer');
4
+ export default NativeMuxVideoView;
@@ -0,0 +1,6 @@
1
+ export { createMuxVideoPlayer, MuxVideoPlayer } from './MuxVideoPlayer';
2
+ export { MuxVideoView, useMuxVideoPlayer } from './MuxVideoView';
3
+ export { muxResolutionSupport, normalizeMuxVideoSource } from './normalizeSource';
4
+ export type { MuxContentFit, MuxVideoControls, MuxVideoControlsTheme, MuxCustomData, MuxMaxResolution, MuxMinResolution, MuxNativeViewRef, MuxPlaybackStatus, MuxPlayerStatus, MuxPlayingChangeEvent, MuxRenditionOrder, MuxSourceErrorEvent, MuxSourceLoadEvent, MuxStatusChangeEvent, MuxTimeUpdateEvent, MuxVideoChapter, MuxVideoCaptionTrack, MuxVideoClipping, MuxVideoMetadata, MuxVideoKeyMoment, MuxVideoRobotsConfig, MuxVideoRobotsContext, MuxVideoSource, MuxVideoSourceObject, MuxVideoSummary, MuxVideoViewProps, } from './types';
5
+ export type { MuxVideoViewRef } from './MuxVideoView';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAClF,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EACrB,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,iBAAiB,GAClB,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
package/build/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { createMuxVideoPlayer, MuxVideoPlayer } from './MuxVideoPlayer';
2
+ export { MuxVideoView, useMuxVideoPlayer } from './MuxVideoView';
3
+ export { muxResolutionSupport, normalizeMuxVideoSource } from './normalizeSource';
@@ -0,0 +1,7 @@
1
+ import type { MuxMaxResolution, MuxMinResolution, MuxVideoSource, NormalizedMuxVideoSource } from './types';
2
+ export declare function normalizeMuxVideoSource(source: MuxVideoSource): NormalizedMuxVideoSource;
3
+ export declare const muxResolutionSupport: {
4
+ readonly min: MuxMinResolution[];
5
+ readonly max: MuxMaxResolution[];
6
+ };
7
+ //# sourceMappingURL=normalizeSource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizeSource.d.ts","sourceRoot":"","sources":["../src/normalizeSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,wBAAwB,EACzB,MAAM,SAAS,CAAC;AAMjB,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,cAAc,GAAG,wBAAwB,CA0BxF;AA2DD,eAAO,MAAM,oBAAoB;;;CAGvB,CAAC"}