@karnstack/kino 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.
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/dist/control-bar-DWzMIb23.js +1513 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +2 -0
- package/dist/mux.d.ts +38 -0
- package/dist/mux.js +439 -0
- package/dist/styles.css +969 -0
- package/dist/types-D0bLitH2.d.ts +90 -0
- package/package.json +84 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { a as Provider, c as TextTrackInfo, i as PlayerActions, n as MediaError, o as QualityLevel, r as MediaState, s as SourceOptions, t as Capabilities } from "./types-D0bLitH2.js";
|
|
2
|
+
import { ReactNode, RefObject } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/util/format-time.d.ts
|
|
5
|
+
declare function formatTime(seconds: number): string;
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/core/store.d.ts
|
|
8
|
+
declare const PlayerContext: import("react").Context<Provider | null>;
|
|
9
|
+
declare function useMediaSelector<T>(selector: (s: MediaState) => T, isEqual?: (a: T, b: T) => boolean): T;
|
|
10
|
+
declare function usePlayer(): {
|
|
11
|
+
state: MediaState;
|
|
12
|
+
actions: PlayerActions;
|
|
13
|
+
};
|
|
14
|
+
declare function usePlayerActions(): PlayerActions;
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/ui/player.d.ts
|
|
17
|
+
declare function useWrapperRef(): RefObject<HTMLDivElement | null> | null;
|
|
18
|
+
declare function useIsCompact(): boolean;
|
|
19
|
+
type PlayerProps = {
|
|
20
|
+
provider: Provider;
|
|
21
|
+
accentColor?: string;
|
|
22
|
+
theme?: Record<string, string>;
|
|
23
|
+
className?: string;
|
|
24
|
+
children?: ReactNode;
|
|
25
|
+
};
|
|
26
|
+
declare function Player({
|
|
27
|
+
provider,
|
|
28
|
+
accentColor,
|
|
29
|
+
theme,
|
|
30
|
+
className,
|
|
31
|
+
children
|
|
32
|
+
}: PlayerProps): import("react").JSX.Element;
|
|
33
|
+
declare namespace Player {
|
|
34
|
+
var Overlay: ({
|
|
35
|
+
children
|
|
36
|
+
}: {
|
|
37
|
+
children: ReactNode;
|
|
38
|
+
}) => import("react").JSX.Element;
|
|
39
|
+
}
|
|
40
|
+
declare function useControlsVisible(): boolean;
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/ui/scrubber.d.ts
|
|
43
|
+
declare function Scrubber(): import("react").JSX.Element;
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/ui/idle-overlay.d.ts
|
|
46
|
+
declare function IdleOverlay(): import("react").JSX.Element | null;
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/ui/captions.d.ts
|
|
49
|
+
declare function Captions(): import("react").JSX.Element | null;
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/ui/buttons.d.ts
|
|
52
|
+
declare function PlayPauseButton(): import("react").JSX.Element;
|
|
53
|
+
declare function SkipBackButton(): import("react").JSX.Element;
|
|
54
|
+
declare function SkipForwardButton(): import("react").JSX.Element;
|
|
55
|
+
declare function VolumeControl(): import("react").JSX.Element;
|
|
56
|
+
declare function PipButton(): import("react").JSX.Element | null;
|
|
57
|
+
declare function FullscreenButton(): import("react").JSX.Element | null;
|
|
58
|
+
//#endregion
|
|
59
|
+
//#region src/ui/control-bar.d.ts
|
|
60
|
+
declare function ControlBar(): import("react").JSX.Element;
|
|
61
|
+
//#endregion
|
|
62
|
+
export { type Capabilities, Captions, ControlBar, FullscreenButton, IdleOverlay, type MediaError, type MediaState, PipButton, PlayPauseButton, Player, type PlayerActions, PlayerContext, type Provider, type QualityLevel, Scrubber, SkipBackButton, SkipForwardButton, type SourceOptions, type TextTrackInfo, VolumeControl, formatTime, useControlsVisible, useIsCompact, useMediaSelector, usePlayer, usePlayerActions, useWrapperRef };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { _ as usePlayer, a as SkipBackButton, c as Captions, d as Player, f as useControlsVisible, g as useMediaSelector, h as PlayerContext, i as PlayPauseButton, l as IdleOverlay, m as useWrapperRef, n as FullscreenButton, o as SkipForwardButton, p as useIsCompact, r as PipButton, s as VolumeControl, t as ControlBar, u as Scrubber, v as usePlayerActions, y as formatTime } from "./control-bar-DWzMIb23.js";
|
|
2
|
+
export { Captions, ControlBar, FullscreenButton, IdleOverlay, PipButton, PlayPauseButton, Player, PlayerContext, Scrubber, SkipBackButton, SkipForwardButton, VolumeControl, formatTime, useControlsVisible, useIsCompact, useMediaSelector, usePlayer, usePlayerActions, useWrapperRef };
|
package/dist/mux.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { a as Provider } from "./types-D0bLitH2.js";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
//#region src/mux/provider.d.ts
|
|
4
|
+
type MuxProviderOptions = {
|
|
5
|
+
playbackId: string;
|
|
6
|
+
tokens?: {
|
|
7
|
+
playback?: string;
|
|
8
|
+
thumbnail?: string;
|
|
9
|
+
storyboard?: string;
|
|
10
|
+
};
|
|
11
|
+
metadata?: {
|
|
12
|
+
videoId?: string;
|
|
13
|
+
videoTitle?: string;
|
|
14
|
+
viewerUserId?: string;
|
|
15
|
+
};
|
|
16
|
+
envKey?: string;
|
|
17
|
+
poster?: string;
|
|
18
|
+
autoPlay?: boolean;
|
|
19
|
+
defaultRate?: number;
|
|
20
|
+
};
|
|
21
|
+
declare function createMuxProvider(opts: MuxProviderOptions): Provider;
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/mux/mux-player.d.ts
|
|
24
|
+
type MuxPlayerProps = MuxProviderOptions & {
|
|
25
|
+
accentColor?: string;
|
|
26
|
+
theme?: Record<string, string>;
|
|
27
|
+
className?: string;
|
|
28
|
+
children?: ReactNode;
|
|
29
|
+
};
|
|
30
|
+
declare function MuxPlayer({
|
|
31
|
+
accentColor,
|
|
32
|
+
theme,
|
|
33
|
+
className,
|
|
34
|
+
children,
|
|
35
|
+
...opts
|
|
36
|
+
}: MuxPlayerProps): import("react").JSX.Element;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { MuxPlayer, type MuxPlayerProps, type MuxProviderOptions, createMuxProvider };
|
package/dist/mux.js
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import { c as Captions, d as Player, l as IdleOverlay, t as ControlBar } from "./control-bar-DWzMIb23.js";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import "@mux/mux-video";
|
|
5
|
+
//#region src/core/fake-provider.ts
|
|
6
|
+
const DEFAULT_CAPS = {
|
|
7
|
+
canSetQuality: true,
|
|
8
|
+
hasStoryboard: false,
|
|
9
|
+
canPiP: true,
|
|
10
|
+
canFullscreen: true,
|
|
11
|
+
canSetRate: true,
|
|
12
|
+
hasTextTracks: false
|
|
13
|
+
};
|
|
14
|
+
function defaultState() {
|
|
15
|
+
return {
|
|
16
|
+
paused: true,
|
|
17
|
+
currentTime: 0,
|
|
18
|
+
duration: 0,
|
|
19
|
+
buffered: [],
|
|
20
|
+
rate: 1,
|
|
21
|
+
volume: 1,
|
|
22
|
+
muted: false,
|
|
23
|
+
readyState: 0,
|
|
24
|
+
seeking: false,
|
|
25
|
+
ended: false,
|
|
26
|
+
error: null,
|
|
27
|
+
qualities: [],
|
|
28
|
+
activeQualityId: "auto",
|
|
29
|
+
videoHeight: 0,
|
|
30
|
+
textTracks: [],
|
|
31
|
+
activeTextTrackId: null,
|
|
32
|
+
activeCueText: "",
|
|
33
|
+
fullscreen: false,
|
|
34
|
+
pip: false,
|
|
35
|
+
storyboard: null,
|
|
36
|
+
capabilities: { ...DEFAULT_CAPS }
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/mux/urls.ts
|
|
41
|
+
const IMAGE_HOST = "https://image.mux.com";
|
|
42
|
+
function buildImageUrl(playbackId, kind, token, ext = kind === "storyboard" ? "vtt" : "webp") {
|
|
43
|
+
const base = `${IMAGE_HOST}/${playbackId}/${kind}.${ext}`;
|
|
44
|
+
return token ? `${base}?token=${token}` : base;
|
|
45
|
+
}
|
|
46
|
+
function detectIOS(ua) {
|
|
47
|
+
return /iPhone|iPad|iPod/.test(ua);
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/util/captions.ts
|
|
51
|
+
function joinCues(cues) {
|
|
52
|
+
if (!cues || cues.length === 0) return "";
|
|
53
|
+
const parts = [];
|
|
54
|
+
for (let i = 0; i < cues.length; i++) {
|
|
55
|
+
const c = cues[i];
|
|
56
|
+
if (c && typeof c.text === "string") parts.push(c.text);
|
|
57
|
+
}
|
|
58
|
+
return parts.join("\n").replace(/<[^>]+>/g, "").replace(/[^\S\n]+/g, " ").replace(/ *\n */g, "\n").replace(/\n{2,}/g, "\n").trim();
|
|
59
|
+
}
|
|
60
|
+
function activeCueText(tracks, now) {
|
|
61
|
+
if (!tracks) return "";
|
|
62
|
+
for (let i = 0; i < tracks.length; i++) {
|
|
63
|
+
const t = tracks[i];
|
|
64
|
+
if (!t) continue;
|
|
65
|
+
if (t.kind !== "subtitles" && t.kind !== "captions") continue;
|
|
66
|
+
if (t.mode === "disabled") continue;
|
|
67
|
+
const active = joinCues(t.activeCues);
|
|
68
|
+
if (active) return active;
|
|
69
|
+
const all = t.cues;
|
|
70
|
+
if (all && all.length) {
|
|
71
|
+
const hits = [];
|
|
72
|
+
for (let j = 0; j < all.length; j++) {
|
|
73
|
+
const c = all[j];
|
|
74
|
+
if (c && c.startTime <= now && now < c.endTime) hits.push(c);
|
|
75
|
+
}
|
|
76
|
+
const text = joinCues(hits);
|
|
77
|
+
if (text) return text;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/mux/provider.ts
|
|
84
|
+
function createMuxProvider(opts) {
|
|
85
|
+
const ios = typeof navigator !== "undefined" && detectIOS(navigator.userAgent);
|
|
86
|
+
let el = null;
|
|
87
|
+
let renditions = null;
|
|
88
|
+
let renditionsBound = false;
|
|
89
|
+
let state = {
|
|
90
|
+
...defaultState(),
|
|
91
|
+
rate: opts.defaultRate ?? 1,
|
|
92
|
+
storyboard: { vttUrl: buildImageUrl(opts.playbackId, "storyboard", opts.tokens?.storyboard) },
|
|
93
|
+
capabilities: {
|
|
94
|
+
canSetQuality: !ios,
|
|
95
|
+
hasStoryboard: true,
|
|
96
|
+
canPiP: typeof document !== "undefined" && "pictureInPictureEnabled" in document,
|
|
97
|
+
canFullscreen: true,
|
|
98
|
+
canSetRate: true,
|
|
99
|
+
hasTextTracks: true
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
103
|
+
const emit = () => listeners.forEach((l) => l());
|
|
104
|
+
const patch = (p) => {
|
|
105
|
+
state = {
|
|
106
|
+
...state,
|
|
107
|
+
...p
|
|
108
|
+
};
|
|
109
|
+
emit();
|
|
110
|
+
};
|
|
111
|
+
const onFullscreenChange = () => patch({ fullscreen: document.fullscreenElement != null });
|
|
112
|
+
const onWebkitBeginFullscreen = () => patch({ fullscreen: true });
|
|
113
|
+
const onWebkitEndFullscreen = () => patch({ fullscreen: false });
|
|
114
|
+
const onEnterPip = () => patch({ pip: true });
|
|
115
|
+
const onLeavePip = () => patch({ pip: false });
|
|
116
|
+
let activeTrack = null;
|
|
117
|
+
const readCueText = () => ios ? "" : activeCueText(el?.textTracks, el?.currentTime ?? 0);
|
|
118
|
+
const modeForActiveTrack = (t) => ios ? "showing" : t.cues && t.cues.length > 0 ? "hidden" : "showing";
|
|
119
|
+
const onCueChange = () => {
|
|
120
|
+
applyTextTrackModes();
|
|
121
|
+
patch({ activeCueText: readCueText() });
|
|
122
|
+
};
|
|
123
|
+
const bindActiveTrack = (track) => {
|
|
124
|
+
if (activeTrack) activeTrack.removeEventListener("cuechange", onCueChange);
|
|
125
|
+
activeTrack = track;
|
|
126
|
+
if (activeTrack) activeTrack.addEventListener("cuechange", onCueChange);
|
|
127
|
+
patch({ activeCueText: readCueText() });
|
|
128
|
+
};
|
|
129
|
+
const applyTextTrackModes = () => {
|
|
130
|
+
const tt = el?.textTracks;
|
|
131
|
+
if (!tt) return;
|
|
132
|
+
const id = state.activeTextTrackId;
|
|
133
|
+
let next = null;
|
|
134
|
+
for (let i = 0; i < tt.length; i++) {
|
|
135
|
+
const t = tt[i];
|
|
136
|
+
if (!t) continue;
|
|
137
|
+
if (t.kind !== "subtitles" && t.kind !== "captions") continue;
|
|
138
|
+
if (id != null && (t.id || String(i)) === id) {
|
|
139
|
+
const mode = modeForActiveTrack(t);
|
|
140
|
+
if (t.mode !== mode) t.mode = mode;
|
|
141
|
+
next = t;
|
|
142
|
+
} else if (t.mode !== "disabled") t.mode = "disabled";
|
|
143
|
+
}
|
|
144
|
+
if (next !== activeTrack) bindActiveTrack(next);
|
|
145
|
+
};
|
|
146
|
+
const onTextTracksChanged = () => {
|
|
147
|
+
applyTextTrackModes();
|
|
148
|
+
patch({
|
|
149
|
+
textTracks: readTextTracks(),
|
|
150
|
+
activeCueText: readCueText()
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
const readQualities = () => {
|
|
154
|
+
const r = el?.videoRenditions;
|
|
155
|
+
if (!r) return [];
|
|
156
|
+
const out = [];
|
|
157
|
+
for (let i = 0; i < r.length; i++) {
|
|
158
|
+
const item = r[i];
|
|
159
|
+
if (!item) continue;
|
|
160
|
+
out.push({
|
|
161
|
+
id: item.id,
|
|
162
|
+
height: item.height,
|
|
163
|
+
bitrate: item.bitrate,
|
|
164
|
+
selected: item.selected
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
};
|
|
169
|
+
const readTextTracks = () => {
|
|
170
|
+
const tt = el?.textTracks;
|
|
171
|
+
if (!tt) return [];
|
|
172
|
+
const out = [];
|
|
173
|
+
for (let i = 0; i < tt.length; i++) {
|
|
174
|
+
const t = tt[i];
|
|
175
|
+
if (!t) continue;
|
|
176
|
+
if (t.kind !== "subtitles" && t.kind !== "captions") continue;
|
|
177
|
+
out.push({
|
|
178
|
+
id: t.id || String(i),
|
|
179
|
+
kind: t.kind,
|
|
180
|
+
label: t.label,
|
|
181
|
+
lang: t.language,
|
|
182
|
+
mode: t.mode
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return out;
|
|
186
|
+
};
|
|
187
|
+
const bindRenditions = () => {
|
|
188
|
+
if (renditionsBound || !el?.videoRenditions) return;
|
|
189
|
+
renditions = el.videoRenditions;
|
|
190
|
+
renditions.addEventListener("change", syncFromEl);
|
|
191
|
+
renditionsBound = true;
|
|
192
|
+
};
|
|
193
|
+
const syncFromEl = () => {
|
|
194
|
+
if (!el) return;
|
|
195
|
+
bindRenditions();
|
|
196
|
+
applyTextTrackModes();
|
|
197
|
+
const ranges = [];
|
|
198
|
+
for (let i = 0; i < el.buffered.length; i++) ranges.push([el.buffered.start(i), el.buffered.end(i)]);
|
|
199
|
+
patch({
|
|
200
|
+
paused: el.paused,
|
|
201
|
+
currentTime: el.currentTime,
|
|
202
|
+
duration: el.duration || 0,
|
|
203
|
+
buffered: ranges,
|
|
204
|
+
rate: el.playbackRate,
|
|
205
|
+
volume: el.volume,
|
|
206
|
+
muted: el.muted,
|
|
207
|
+
readyState: el.readyState,
|
|
208
|
+
seeking: el.seeking,
|
|
209
|
+
ended: el.ended,
|
|
210
|
+
error: el.error ? {
|
|
211
|
+
code: el.error.code,
|
|
212
|
+
message: el.error.message
|
|
213
|
+
} : null,
|
|
214
|
+
qualities: readQualities(),
|
|
215
|
+
textTracks: readTextTracks(),
|
|
216
|
+
videoHeight: el.videoHeight || 0,
|
|
217
|
+
activeCueText: readCueText()
|
|
218
|
+
});
|
|
219
|
+
if (typeof navigator !== "undefined" && "mediaSession" in navigator) navigator.mediaSession.playbackState = el.paused ? "paused" : "playing";
|
|
220
|
+
};
|
|
221
|
+
const MEDIA_SESSION_ACTIONS = [
|
|
222
|
+
"play",
|
|
223
|
+
"pause",
|
|
224
|
+
"seekbackward",
|
|
225
|
+
"seekforward",
|
|
226
|
+
"seekto"
|
|
227
|
+
];
|
|
228
|
+
const setupMediaSession = () => {
|
|
229
|
+
if (typeof navigator === "undefined" || !("mediaSession" in navigator)) return;
|
|
230
|
+
const ms = navigator.mediaSession;
|
|
231
|
+
const set = (a, h) => {
|
|
232
|
+
try {
|
|
233
|
+
ms.setActionHandler(a, h);
|
|
234
|
+
} catch {}
|
|
235
|
+
};
|
|
236
|
+
set("play", () => actions.play());
|
|
237
|
+
set("pause", () => actions.pause());
|
|
238
|
+
set("seekbackward", (d) => actions.seek(Math.max(0, state.currentTime - (d.seekOffset || 10))));
|
|
239
|
+
set("seekforward", (d) => actions.seek(state.currentTime + (d.seekOffset || 10)));
|
|
240
|
+
set("seekto", (d) => {
|
|
241
|
+
if (typeof d.seekTime === "number") actions.seek(d.seekTime);
|
|
242
|
+
});
|
|
243
|
+
if (typeof MediaMetadata !== "undefined") try {
|
|
244
|
+
ms.metadata = new MediaMetadata({ title: opts.metadata?.videoTitle ?? "Video" });
|
|
245
|
+
} catch {}
|
|
246
|
+
};
|
|
247
|
+
const teardownMediaSession = () => {
|
|
248
|
+
if (typeof navigator === "undefined" || !("mediaSession" in navigator)) return;
|
|
249
|
+
for (const a of MEDIA_SESSION_ACTIONS) try {
|
|
250
|
+
navigator.mediaSession.setActionHandler(a, null);
|
|
251
|
+
} catch {}
|
|
252
|
+
};
|
|
253
|
+
const MEDIA_EVENTS = [
|
|
254
|
+
"play",
|
|
255
|
+
"pause",
|
|
256
|
+
"timeupdate",
|
|
257
|
+
"durationchange",
|
|
258
|
+
"progress",
|
|
259
|
+
"volumechange",
|
|
260
|
+
"ratechange",
|
|
261
|
+
"seeking",
|
|
262
|
+
"seeked",
|
|
263
|
+
"ended",
|
|
264
|
+
"loadedmetadata",
|
|
265
|
+
"canplay",
|
|
266
|
+
"waiting",
|
|
267
|
+
"error"
|
|
268
|
+
];
|
|
269
|
+
const actions = {
|
|
270
|
+
play: () => {
|
|
271
|
+
el?.play?.();
|
|
272
|
+
},
|
|
273
|
+
pause: () => el?.pause(),
|
|
274
|
+
seek: (t) => {
|
|
275
|
+
if (el) el.currentTime = t;
|
|
276
|
+
},
|
|
277
|
+
setRate: (r) => {
|
|
278
|
+
if (el) el.playbackRate = r;
|
|
279
|
+
},
|
|
280
|
+
setVolume: (v) => {
|
|
281
|
+
if (el) el.volume = Math.min(1, Math.max(0, v));
|
|
282
|
+
},
|
|
283
|
+
setMuted: (m) => {
|
|
284
|
+
if (el) el.muted = m;
|
|
285
|
+
},
|
|
286
|
+
setQuality: (id) => {
|
|
287
|
+
const r = el?.videoRenditions;
|
|
288
|
+
if (!r) return;
|
|
289
|
+
if (id === "auto") {
|
|
290
|
+
r.selectedIndex = -1;
|
|
291
|
+
patch({ activeQualityId: "auto" });
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
for (let i = 0; i < r.length; i++) {
|
|
295
|
+
const item = r[i];
|
|
296
|
+
if (item && item.id === id) {
|
|
297
|
+
item.selected = true;
|
|
298
|
+
r.selectedIndex = i;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
patch({ activeQualityId: id });
|
|
302
|
+
},
|
|
303
|
+
setTextTrack: (id) => {
|
|
304
|
+
if (!el?.textTracks) return;
|
|
305
|
+
patch({ activeTextTrackId: id });
|
|
306
|
+
applyTextTrackModes();
|
|
307
|
+
},
|
|
308
|
+
enterFullscreen: (wrapper) => {
|
|
309
|
+
if (wrapper.requestFullscreen) wrapper.requestFullscreen();
|
|
310
|
+
else el?.webkitEnterFullscreen?.();
|
|
311
|
+
},
|
|
312
|
+
exitFullscreen: () => {
|
|
313
|
+
if (document.fullscreenElement) document.exitFullscreen?.();
|
|
314
|
+
else el?.webkitExitFullscreen?.();
|
|
315
|
+
},
|
|
316
|
+
enterPiP: () => {
|
|
317
|
+
el?.requestPictureInPicture?.();
|
|
318
|
+
},
|
|
319
|
+
exitPiP: () => {
|
|
320
|
+
document.exitPictureInPicture?.();
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
return {
|
|
324
|
+
mount(container) {
|
|
325
|
+
el = document.createElement("mux-video");
|
|
326
|
+
el.playbackId = opts.tokens?.playback ? `${opts.playbackId}?token=${opts.tokens.playback}` : opts.playbackId;
|
|
327
|
+
el.setAttribute("crossorigin", "");
|
|
328
|
+
el.setAttribute("playsinline", "");
|
|
329
|
+
el.playsInline = true;
|
|
330
|
+
el.poster = opts.poster ?? buildImageUrl(opts.playbackId, "thumbnail", opts.tokens?.thumbnail);
|
|
331
|
+
if (opts.autoPlay) el.autoplay = true;
|
|
332
|
+
el.playbackRate = state.rate;
|
|
333
|
+
if (opts.envKey) el.envKey = opts.envKey;
|
|
334
|
+
if (opts.metadata) el.metadata = {
|
|
335
|
+
video_id: opts.metadata.videoId,
|
|
336
|
+
video_title: opts.metadata.videoTitle,
|
|
337
|
+
viewer_user_id: opts.metadata.viewerUserId
|
|
338
|
+
};
|
|
339
|
+
for (const ev of MEDIA_EVENTS) el.addEventListener(ev, syncFromEl);
|
|
340
|
+
document.addEventListener("fullscreenchange", onFullscreenChange);
|
|
341
|
+
el.addEventListener("webkitbeginfullscreen", onWebkitBeginFullscreen);
|
|
342
|
+
el.addEventListener("webkitendfullscreen", onWebkitEndFullscreen);
|
|
343
|
+
el.addEventListener("enterpictureinpicture", onEnterPip);
|
|
344
|
+
el.addEventListener("leavepictureinpicture", onLeavePip);
|
|
345
|
+
container.appendChild(el);
|
|
346
|
+
el.textTracks?.addEventListener("addtrack", onTextTracksChanged);
|
|
347
|
+
el.textTracks?.addEventListener("removetrack", onTextTracksChanged);
|
|
348
|
+
el.textTracks?.addEventListener("change", onTextTracksChanged);
|
|
349
|
+
setupMediaSession();
|
|
350
|
+
},
|
|
351
|
+
swapSource(opts) {
|
|
352
|
+
if (!el) return;
|
|
353
|
+
if (opts.playbackId != null) el.playbackId = opts.tokens?.playback ? `${opts.playbackId}?token=${opts.tokens.playback}` : opts.playbackId;
|
|
354
|
+
if (opts.poster != null) el.poster = opts.poster;
|
|
355
|
+
else if (opts.playbackId != null) el.poster = buildImageUrl(opts.playbackId, "thumbnail", opts.tokens?.thumbnail);
|
|
356
|
+
if (opts.metadata) el.metadata = {
|
|
357
|
+
video_id: opts.metadata.videoId,
|
|
358
|
+
video_title: opts.metadata.videoTitle,
|
|
359
|
+
viewer_user_id: opts.metadata.viewerUserId
|
|
360
|
+
};
|
|
361
|
+
patch({
|
|
362
|
+
currentTime: 0,
|
|
363
|
+
duration: 0,
|
|
364
|
+
ended: false,
|
|
365
|
+
seeking: false,
|
|
366
|
+
error: null,
|
|
367
|
+
storyboard: opts.playbackId != null ? { vttUrl: buildImageUrl(opts.playbackId, "storyboard", opts.tokens?.storyboard) } : state.storyboard
|
|
368
|
+
});
|
|
369
|
+
},
|
|
370
|
+
getState: () => state,
|
|
371
|
+
subscribe: (l) => {
|
|
372
|
+
listeners.add(l);
|
|
373
|
+
return () => listeners.delete(l);
|
|
374
|
+
},
|
|
375
|
+
actions,
|
|
376
|
+
destroy() {
|
|
377
|
+
document.removeEventListener("fullscreenchange", onFullscreenChange);
|
|
378
|
+
teardownMediaSession();
|
|
379
|
+
if (activeTrack) activeTrack.removeEventListener("cuechange", onCueChange);
|
|
380
|
+
activeTrack = null;
|
|
381
|
+
if (renditionsBound) renditions?.removeEventListener("change", syncFromEl);
|
|
382
|
+
renditions = null;
|
|
383
|
+
renditionsBound = false;
|
|
384
|
+
if (el) {
|
|
385
|
+
for (const ev of MEDIA_EVENTS) el.removeEventListener(ev, syncFromEl);
|
|
386
|
+
el.removeEventListener("webkitbeginfullscreen", onWebkitBeginFullscreen);
|
|
387
|
+
el.removeEventListener("webkitendfullscreen", onWebkitEndFullscreen);
|
|
388
|
+
el.removeEventListener("enterpictureinpicture", onEnterPip);
|
|
389
|
+
el.removeEventListener("leavepictureinpicture", onLeavePip);
|
|
390
|
+
el.textTracks?.removeEventListener("addtrack", onTextTracksChanged);
|
|
391
|
+
el.textTracks?.removeEventListener("removetrack", onTextTracksChanged);
|
|
392
|
+
el.textTracks?.removeEventListener("change", onTextTracksChanged);
|
|
393
|
+
el.remove();
|
|
394
|
+
}
|
|
395
|
+
el = null;
|
|
396
|
+
listeners.clear();
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
//#endregion
|
|
401
|
+
//#region src/mux/mux-player.tsx
|
|
402
|
+
function MuxPlayer({ accentColor, theme, className, children, ...opts }) {
|
|
403
|
+
const providerRef = useRef(null);
|
|
404
|
+
if (providerRef.current === null) providerRef.current = createMuxProvider(opts);
|
|
405
|
+
const provider = providerRef.current;
|
|
406
|
+
const mountedRef = useRef(false);
|
|
407
|
+
useEffect(() => {
|
|
408
|
+
if (!mountedRef.current) {
|
|
409
|
+
mountedRef.current = true;
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
provider.swapSource?.({
|
|
413
|
+
playbackId: opts.playbackId,
|
|
414
|
+
poster: opts.poster,
|
|
415
|
+
tokens: opts.tokens,
|
|
416
|
+
metadata: opts.metadata
|
|
417
|
+
});
|
|
418
|
+
}, [
|
|
419
|
+
opts.playbackId,
|
|
420
|
+
opts.tokens?.playback,
|
|
421
|
+
opts.tokens?.thumbnail,
|
|
422
|
+
opts.tokens?.storyboard,
|
|
423
|
+
opts.poster
|
|
424
|
+
]);
|
|
425
|
+
return /* @__PURE__ */ jsxs(Player, {
|
|
426
|
+
provider,
|
|
427
|
+
accentColor,
|
|
428
|
+
theme,
|
|
429
|
+
className,
|
|
430
|
+
children: [
|
|
431
|
+
/* @__PURE__ */ jsx(IdleOverlay, {}),
|
|
432
|
+
/* @__PURE__ */ jsx(Captions, {}),
|
|
433
|
+
/* @__PURE__ */ jsx(ControlBar, {}),
|
|
434
|
+
children
|
|
435
|
+
]
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
//#endregion
|
|
439
|
+
export { MuxPlayer, createMuxProvider };
|