@kkcompany/player 2.25.0-canary.24 → 2.25.0-canary.26

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.
@@ -0,0 +1,13 @@
1
+ //#region src/util/loadScript.js
2
+ const loadScript = (url) => new Promise((resolve) => {
3
+ const script = Object.assign(document.createElement("script"), {
4
+ async: true,
5
+ src: url
6
+ });
7
+ script.addEventListener("load", resolve);
8
+ document.body.appendChild(script);
9
+ });
10
+ var loadScript_default = loadScript;
11
+
12
+ //#endregion
13
+ export { loadScript_default as t };
@@ -0,0 +1,542 @@
1
+ import UAParser from "ua-parser-js";
2
+
3
+ //#region src/util/events.js
4
+ const on = (target, name, handler, ...rest) => {
5
+ target.addEventListener(name, handler, ...rest);
6
+ return () => target.removeEventListener(name, handler, ...rest);
7
+ };
8
+ const once = (target, name, handler) => {
9
+ const oneTime = (...args) => {
10
+ handler(...args);
11
+ target.removeEventListener(name, oneTime);
12
+ };
13
+ target.addEventListener(name, oneTime);
14
+ return () => target.removeEventListener(name, oneTime);
15
+ };
16
+ const waitFor = (check, handler) => {
17
+ const checkInterval = setInterval(() => {
18
+ if (check()) {
19
+ clearInterval(checkInterval);
20
+ handler();
21
+ }
22
+ }, 50);
23
+ return () => clearInterval(checkInterval);
24
+ };
25
+ const dispatchCustomEvent = (element, name, detail) => {
26
+ if (element && typeof element.dispatchEvent === "function") return element.dispatchEvent(new CustomEvent(name, { detail }));
27
+ else console.error("The media element is undefined or does not have the dispatchEvent method");
28
+ };
29
+
30
+ //#endregion
31
+ //#region src/Enum.js
32
+ const LanguageCode = {
33
+ EN: "en",
34
+ JA: "ja",
35
+ ZHTW: "zh-TW"
36
+ };
37
+ const EnvironmentErrorName = {
38
+ NOT_SUPPORT_DEVICE: "KKS.ERROR.DEVICE_IS_NOT_SUPPORTED",
39
+ NOT_SUPPORT_OS: "KKS.ERROR.OS_IS_NOT_SUPPORTED",
40
+ NOT_SUPPORT_OS_VERSION: "KKS.ERROR.PLEASE_UPGRADE_OS",
41
+ NOT_SUPPORT_BROWSER: "KKS.ERROR.BROWSER_IS_NOT_SUPPORTED",
42
+ NOT_SUPPORT_BROWSER_VERSION: "KKS.ERROR.PLEASE_UPGRADE_BROWSER"
43
+ };
44
+ const SeekOrigin = {
45
+ START: "START",
46
+ CURRENT: "CURRENT"
47
+ };
48
+ const CastState = {
49
+ NO_DEVICES_AVAILABLE: "NO_DEVICES_AVAILABLE",
50
+ CONNECTED: "CONNECTED",
51
+ CONNECTING: "CONNECTING",
52
+ NOT_CONNECTED: "NOT_CONNECTED"
53
+ };
54
+ const ItemType = {
55
+ VIDEOS: "videos",
56
+ LIVES: "lives"
57
+ };
58
+
59
+ //#endregion
60
+ //#region src/util/environment.js
61
+ const parser = new UAParser();
62
+ function getOS() {
63
+ return parser.getOS();
64
+ }
65
+ function getDevice() {
66
+ const device = parser.getDevice();
67
+ const osName = getOS().name;
68
+ if (device.type === void 0 && osName === "Android") device.type = "tablet";
69
+ return device;
70
+ }
71
+ function getBrowser() {
72
+ return parser.getBrowser();
73
+ }
74
+ const isSafari = () => /^((?!chrome|android|X11|Linux).)*(safari|iPad|iPhone|Version)/i.test(navigator.userAgent);
75
+ function needNativeHls() {
76
+ return /android|X11|Linux/i.test(navigator.userAgent) || /firefox/i.test(navigator.userAgent) ? "" : isSafari() ? "maybe" : document.createElement("video").canPlayType("application/vnd.apple.mpegURL");
77
+ }
78
+ const isDesktop = () => !getDevice().type;
79
+ const isIOS = () => /iPad|iPhone|iPod/.test(navigator.platform) || navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
80
+ function compareVersion(v1, v2) {
81
+ if (!/\d+(\.\d+)*/.test(v1)) throw Error(`the version format ${v1} is wrong`);
82
+ if (!/\d+(\.\d+)*/.test(v2)) throw Error(`the version format ${v2} is wrong`);
83
+ const v1parts = v1.split(".").map((p) => Number(p));
84
+ const v2parts = v2.split(".").map((p) => Number(p));
85
+ for (let i = 0, I = Math.max(v1parts.length, v2parts.length); i < I; i++) if (v1parts[i] !== v2parts[i]) return (v1parts[i] || 0) - (v2parts[i] || 0);
86
+ return 0;
87
+ }
88
+ const validateEnvironment = (supportEnvironmentList = []) => {
89
+ if (supportEnvironmentList.length === 0) return;
90
+ const device = getDevice();
91
+ const os = getOS();
92
+ const browser = getBrowser();
93
+ const toUnique = (list) => Array.from(new Set(list));
94
+ const validators = [
95
+ {
96
+ filter: ({ device: { name, type } }) => name === "*" || type === "desktop" && device.type === void 0 || type === device.type,
97
+ errorName: EnvironmentErrorName.NOT_SUPPORT_DEVICE,
98
+ getErrorProps: (list) => ({ allowDevices: toUnique(list.map((env) => env.device.type)) })
99
+ },
100
+ {
101
+ filter: ({ os: { name } }) => name === "*" || name === os.name,
102
+ errorName: EnvironmentErrorName.NOT_SUPPORT_OS,
103
+ getErrorProps: (list) => ({ allowOSs: toUnique(list.map((env) => env.os.name)) })
104
+ },
105
+ {
106
+ filter: ({ os: { version } }) => version === "*" || compareVersion(os.version, version) >= 0,
107
+ errorName: EnvironmentErrorName.NOT_SUPPORT_OS_VERSION,
108
+ getErrorProps: (list) => ({ minVersion: list[0].os.version })
109
+ },
110
+ {
111
+ filter: ({ browser: { name } }) => name === browser.name,
112
+ errorName: EnvironmentErrorName.NOT_SUPPORT_BROWSER,
113
+ getErrorProps: (list) => ({ allowBrowsers: toUnique(list.map((env) => env.browser.name)) })
114
+ },
115
+ {
116
+ filter: ({ browser: { version } }) => compareVersion(browser.version, version) >= 0,
117
+ errorName: EnvironmentErrorName.NOT_SUPPORT_BROWSER_VERSION,
118
+ getErrorProps: (list) => ({ minVersion: list[0].browser.version })
119
+ }
120
+ ];
121
+ let scopes = supportEnvironmentList;
122
+ for (let i = 0; i < validators.length; i++) {
123
+ const validator = validators[i];
124
+ const newScopes = scopes.filter(validator.filter);
125
+ if (newScopes.length === 0) return {
126
+ name: validator.errorName,
127
+ ...validator.getErrorProps(scopes)
128
+ };
129
+ scopes = newScopes;
130
+ }
131
+ };
132
+ const havePointer = () => {
133
+ if (havePointer.memo) return havePointer.memo;
134
+ havePointer.memo = typeof window !== "undefined" && ["(hover: hover) and (pointer: fine)", "not all and (any-pointer: coarse)"].every((query) => window.matchMedia(query).matches);
135
+ return havePointer.memo;
136
+ };
137
+
138
+ //#endregion
139
+ //#region src/playerCore/source.js
140
+ const protocolExtensions = {
141
+ hls: "m3u8",
142
+ dash: "mpd"
143
+ };
144
+ const mimeTypes = {
145
+ hls: "application/x-mpegurl",
146
+ dash: "application/dash+xml"
147
+ };
148
+ const matchType = (source, manifestType) => source.type?.toLowerCase().includes(manifestType) || source.type?.toLowerCase() === mimeTypes[manifestType] || (source.src || source)?.endsWith?.(protocolExtensions[manifestType]);
149
+ const getDrmOptions$1 = (fallbackDrm) => {
150
+ if (!fallbackDrm?.url) return;
151
+ const drmOptions = {
152
+ licenseUri: fallbackDrm.url,
153
+ headers: fallbackDrm.headers
154
+ };
155
+ return {
156
+ widevine: drmOptions,
157
+ fairplay: {
158
+ ...drmOptions,
159
+ certificateUri: `${fallbackDrm.url}/fairplay_cert`,
160
+ ...fallbackDrm.fairplay
161
+ },
162
+ playready: drmOptions
163
+ };
164
+ };
165
+ /**
166
+ * @typedef {{src: string, type: string}} SourceObject
167
+ * @typedef {{hls: string, dash: string}} SourceObjectAlt backward compatiable form
168
+ *
169
+ * @param {SourceObject[]|SourceObject|SourceObjectAlt|string} sourceOptions
170
+ * @param {{preferManifestType?: ('dash'|'hls'|'platform')}} options
171
+ * @return {{src: string, type: string, drm: Object}}
172
+ */
173
+ const getSource = (sourceOptions, { preferManifestType, fallbackDrm } = {}) => {
174
+ if (sourceOptions.dash || sourceOptions.hls) {
175
+ const { dash, hls } = sourceOptions;
176
+ return getSource([hls && {
177
+ src: hls,
178
+ type: mimeTypes.hls
179
+ }, dash && {
180
+ src: dash,
181
+ type: mimeTypes.dash
182
+ }].filter(Boolean), {
183
+ preferManifestType,
184
+ fallbackDrm
185
+ });
186
+ }
187
+ if (!Array.isArray(sourceOptions)) return getSource([sourceOptions], {
188
+ preferManifestType,
189
+ fallbackDrm
190
+ });
191
+ if (fallbackDrm) return getSource(sourceOptions.map((option) => ({
192
+ ...option.src ? option : { src: option },
193
+ drm: getDrmOptions$1(fallbackDrm)
194
+ })), { preferManifestType });
195
+ const targetType = preferManifestType !== "platform" ? preferManifestType : isSafari() ? "hls" : "dash";
196
+ const matched = sourceOptions.find((source) => matchType(source, targetType));
197
+ const selected = matched || sourceOptions[0];
198
+ if (!selected) return;
199
+ const type = matched && preferManifestType === "hls" && mimeTypes.hls;
200
+ return {
201
+ ...selected.src ? selected : { src: selected },
202
+ type
203
+ };
204
+ };
205
+ const getSourceText = (source) => [].concat(source).filter((item) => item.type === "text/vtt");
206
+
207
+ //#endregion
208
+ //#region src/playerCore/drm.js
209
+ const keySystems = {
210
+ widevine: "com.widevine.alpha",
211
+ fairplay: "com.apple.fps.1_0",
212
+ playready: "com.microsoft.playready"
213
+ };
214
+ const getDrmOptions = (source) => {
215
+ return [source.drm && Object.entries(source.drm).reduce((result, [keySystemId, options]) => {
216
+ const uri = typeof options === "string" ? options : options.licenseUri;
217
+ if (uri) {
218
+ const keySystemName = keySystems[keySystemId] || keySystemId;
219
+ result.servers[keySystemName] = uri;
220
+ const { headers, certificateUri } = options;
221
+ const advanced = [headers && { headers }, certificateUri && { serverCertificateUri: certificateUri }].filter(Boolean);
222
+ if (advanced.length > 0) result.advanced[keySystemName] = Object.assign({}, ...advanced);
223
+ }
224
+ return result;
225
+ }, {
226
+ servers: {},
227
+ advanced: {}
228
+ }), { drm: source.drm && Object.entries(source.drm).reduce((result, [keySystemId, options]) => {
229
+ const keySystemName = keySystems[keySystemId] || keySystemId;
230
+ if (options.headers || options.certificateHeaders) result[keySystemName] = {
231
+ headers: options.headers,
232
+ ...options.certificateHeaders && { certificateHeaders: options.certificateHeaders }
233
+ };
234
+ return result;
235
+ }, {}) }];
236
+ };
237
+
238
+ //#endregion
239
+ //#region src/playerCore/mediaBindings.js
240
+ const LIVE_EDGE_GAP = 10;
241
+ const SHAKA_LIVE_DURATION = 4294967296;
242
+ const isLiveDuration = (duration) => duration >= SHAKA_LIVE_DURATION;
243
+ const isEnded = (media) => !isLiveDuration(media.initialDuration) && media.initialDuration - media.currentTime < 1;
244
+ const isBuffered = (media) => isSafari() || Array.from({ length: media.buffered.length }, (_, index) => ({
245
+ start: media.buffered.start(index),
246
+ end: media.buffered.end(index)
247
+ })).some((range) => range.start <= media.currentTime && media.currentTime <= range.end + 1);
248
+ const getLiveTime = (media, { player }) => {
249
+ const now = Date.now() / 1e3;
250
+ const currentOffset = media.currentTime - media.defaultLiveOffset - now;
251
+ const seekDuration = media.seekDurationDiff + now;
252
+ const { start, end } = player.seekRange();
253
+ return {
254
+ streamType: "live",
255
+ startTime: -seekDuration,
256
+ currentTime: currentOffset < -LIVE_EDGE_GAP ? currentOffset : 0,
257
+ duration: end - start > 5 * LIVE_EDGE_GAP ? seekDuration : 0
258
+ };
259
+ };
260
+ const getMediaTime = (media, { player, plugins = [] }) => {
261
+ const { duration, ...data } = Object.assign(isLiveDuration(media.initialDuration) ? getLiveTime(media, {
262
+ player,
263
+ plugins
264
+ }) : {
265
+ currentTime: media.currentTime,
266
+ bufferTime: Math.max(...Array.from({ length: media.buffered.length }, (_, index) => media.buffered.end(index))),
267
+ duration: media.initialDuration
268
+ }, ...plugins.map((plugin) => plugin.getPlaybackStatus?.()));
269
+ return {
270
+ ...data,
271
+ ...(isLiveDuration(media.initialDuration) || Math.abs(media.duration - media.initialDuration) < .5) && { duration }
272
+ };
273
+ };
274
+ const HAVE_METADATA = 1;
275
+ const getCurrentPlaybackState = (media) => media.autoplay ? "playing" : "paused";
276
+ const subscribePlaybackState = (media, updateState, { iOSFullscreenDetectThreshold = 500 } = {}) => {
277
+ const lastUpdate = {
278
+ state: "",
279
+ time: 0
280
+ };
281
+ const updateIfChanged = (event, state) => {
282
+ if (media.currentTime > 0x9184e72a000) return;
283
+ lastUpdate.time = media.currentTime;
284
+ lastUpdate.eventTime = Date.now();
285
+ lastUpdate.eventType = event.type;
286
+ if (state !== lastUpdate.state) {
287
+ lastUpdate.state = state;
288
+ lastUpdate.batched = media.webkitDisplayingFullscreen;
289
+ if (!lastUpdate.batched) updateState(event, state);
290
+ }
291
+ };
292
+ const updateBufferingState = (event) => {
293
+ if (!media.paused && !media.ended) updateIfChanged(event, "buffering");
294
+ };
295
+ const updatePlaybackTime = (event) => {
296
+ if (!media.paused && isBuffered(media) && media.currentTime - lastUpdate.time > .01) updateIfChanged(event, "playing");
297
+ };
298
+ const updateEnd = (event) => {
299
+ if (event.type === "emptied" && lastUpdate.state !== "loading") {
300
+ updateIfChanged(event, "emptied");
301
+ return true;
302
+ }
303
+ if (isEnded(media) || event.type === "ended") {
304
+ updateIfChanged(event, "ended");
305
+ return true;
306
+ }
307
+ };
308
+ const registered = [
309
+ on(media, "error", (event) => updateIfChanged(event, "error")),
310
+ on(media, "waiting", updateBufferingState),
311
+ on(media, "loadsource", (event) => updateIfChanged(event, "loading")),
312
+ on(media, "loadedmetadata", (event) => setTimeout(() => {
313
+ if (media.paused && !(media.readyState > HAVE_METADATA)) updateIfChanged(event, getCurrentPlaybackState(media));
314
+ }, 1500)),
315
+ on(media, "loadeddata", (event) => setTimeout(() => {
316
+ if (media.paused && !event.defaultPrevented) updateIfChanged(event, getCurrentPlaybackState(media));
317
+ }, 1)),
318
+ on(media, "canplay", (event) => {
319
+ if (media.paused && (lastUpdate.state !== "loading" || isIOS())) updateIfChanged(event, getCurrentPlaybackState(media));
320
+ updatePlaybackTime(event);
321
+ }),
322
+ on(media, "pause", (event) => {
323
+ if (Date.now() - lastUpdate.endFullscreenAt < 5 * iOSFullscreenDetectThreshold) {
324
+ lastUpdate.endFullscreenAt = 0;
325
+ media.play();
326
+ console.debug("Ignore pause from iOS exit fullscreen");
327
+ return;
328
+ }
329
+ if (!updateEnd(event)) updateIfChanged(event, "paused");
330
+ }),
331
+ on(media, "seeking", updateBufferingState),
332
+ on(media, "seeked", (event) => {
333
+ if (lastUpdate.state === "loading") updateIfChanged(event, getCurrentPlaybackState(media));
334
+ }),
335
+ on(media, "timeupdate", updatePlaybackTime),
336
+ on(media, "ended", updateEnd),
337
+ on(media, "emptied", updateEnd),
338
+ on(media, "webkitendfullscreen", (event) => {
339
+ if (lastUpdate.state === "paused" && Date.now() - lastUpdate.eventTime < iOSFullscreenDetectThreshold) {
340
+ lastUpdate.endFullscreenAt = Date.now();
341
+ waitFor(() => !media.webkitDisplayingFullscreen, () => updateState(event, "playing"));
342
+ } else updateState(event, lastUpdate.state);
343
+ })
344
+ ];
345
+ return () => registered.forEach((off) => off());
346
+ };
347
+ const seek = async (media, { player, plugins = [] }, time, issuer) => {
348
+ if (media.readyState < HAVE_METADATA) await new Promise((resolve) => {
349
+ media.addEventListener("loadeddata", resolve, { once: true });
350
+ });
351
+ const seekPlugin = plugins.find((plugin) => typeof plugin.handleSeek === "function" && plugin.isActive());
352
+ const seekInternal = (seekTime) => {
353
+ if (seekTime <= media.duration + 7 && seekTime >= media.duration - 1) return seekInternal(media.duration - 1.1);
354
+ player.shouldPlayFromEdge = false;
355
+ const seekOrigin = player.isLive?.() ? media.defaultLiveOffset + Date.now() / 1e3 : 0;
356
+ player.seek?.(seekTime, issuer);
357
+ if (Math.abs(seekTime) <= LIVE_EDGE_GAP && seekOrigin !== 0) player.goToLive();
358
+ else media.currentTime = seekTime + seekOrigin;
359
+ once(media, "seeked", () => {
360
+ if (Math.abs(seekTime + seekOrigin - media.currentTime) > .5) media.currentTime = seekTime + seekOrigin;
361
+ });
362
+ };
363
+ if (seekPlugin) seekPlugin.handleSeek(time, seekInternal);
364
+ else seekInternal(time);
365
+ };
366
+ const load = async (media, { player, startTime, plugins = [] }, source) => {
367
+ const preferred = getSource(source, { preferManifestType: "platform" });
368
+ if (player.lastSrc === preferred?.src) {
369
+ console.info("src is unchanged, skip load", preferred.src);
370
+ return;
371
+ }
372
+ player.lastSrc = preferred?.src;
373
+ media.dispatchEvent(new CustomEvent("loadsource"));
374
+ const merged = await plugins.reduce(async (loadChain, plugin) => {
375
+ const currentSource = await loadChain;
376
+ const overrides = await plugin.load?.(currentSource, {
377
+ video: media,
378
+ player,
379
+ source: currentSource,
380
+ startTime,
381
+ streamFormat: source.type,
382
+ reload: async () => {
383
+ const restoreMuted = player.isMuted && { muted: player.isMuted() };
384
+ player.lastSrc = "";
385
+ await load(media, {
386
+ player,
387
+ startTime,
388
+ plugins
389
+ }, source);
390
+ if (restoreMuted) player[restoreMuted.muted ? "mute" : "unmute"]();
391
+ }
392
+ });
393
+ return overrides ? {
394
+ ...currentSource,
395
+ ...overrides.url && { src: overrides.url },
396
+ ...overrides.startTime >= 0 && { startTime: overrides.startTime }
397
+ } : currentSource;
398
+ }, {
399
+ ...preferred,
400
+ startTime
401
+ });
402
+ media.addEventListener("durationchange", () => {
403
+ media.initialDuration = media.duration;
404
+ }, { once: true });
405
+ once(media, "loadeddata", () => {
406
+ const seekToStart = (delay = 1) => {
407
+ if (merged.startTime > 0 || merged.startTime < 0) setTimeout(() => seek(media, {
408
+ player,
409
+ plugins
410
+ }, merged.startTime), delay);
411
+ };
412
+ if (player.isLive()) {
413
+ player.shouldPlayFromEdge = player.isLive() && !(merged.startTime < 0);
414
+ once(media, "timeupdate", () => {
415
+ player.shouldPlayFromEdge = false;
416
+ const { start, end } = player.seekRange();
417
+ media.defaultLiveOffset = media.currentTime - Date.now() / 1e3;
418
+ media.seekDurationDiff = end - start - Date.now() / 1e3;
419
+ seekToStart();
420
+ });
421
+ } else seekToStart();
422
+ });
423
+ const [drmOptions, extensions] = getDrmOptions(preferred);
424
+ player.configure({ drm: drmOptions });
425
+ player.configureExtensions(extensions);
426
+ let loadStartTime;
427
+ if (merged.type !== "application/x-mpegurl") loadStartTime = merged.startTime;
428
+ return player.unload().then(() => player.load(merged.src, loadStartTime, merged.type)).then((loadResult) => {
429
+ getSourceText(source).forEach(({ src, language = "en", type = "text/vtt", label = language }) => player.addTextTrackAsync(src, language, "subtitles", type, "", label).catch((error) => console.warn("Failed to add text track", error)));
430
+ return loadResult;
431
+ }).catch((error) => {
432
+ media.dispatchEvent(Object.assign(new CustomEvent("error"), { error }));
433
+ });
434
+ };
435
+ const waitMediaReady = (media) => media.readyState >= HAVE_METADATA || new Promise((resolve) => {
436
+ media.addEventListener("loadedmetadata", resolve, { once: true });
437
+ });
438
+ const toggleMute = (media) => {
439
+ media.muted = !media.muted;
440
+ if (!media.muted) media.volume = Math.max(media.volume, .05);
441
+ };
442
+ const setVolume = (media, { player }, level) => {
443
+ const capped = Math.max(0, Math.min(level, 1));
444
+ media.muted = capped <= 0;
445
+ if (capped > 0) {
446
+ player?.setVolume?.(capped * 100);
447
+ media.volume = capped;
448
+ }
449
+ if (capped <= 0) player?.mute?.();
450
+ };
451
+ const syncPlaybackState = async (media, { player, liveResume }, target) => {
452
+ if (media.webkitDisplayingFullscreen || media.paused === (target === "paused")) return;
453
+ await player?.pendingOperation?.catch(() => 1);
454
+ if (target === "paused") {
455
+ media.autoplay = false;
456
+ if (player) player.pendingOperation = new Promise((resolve) => {
457
+ once(media, "pause", resolve);
458
+ setTimeout(resolve, 1);
459
+ });
460
+ return media.pause();
461
+ }
462
+ await waitMediaReady(media);
463
+ if (isEnded(media)) seek(media, { player }, 0);
464
+ if (media.paused) {
465
+ media.autoplay = true;
466
+ const { streamType, duration, currentTime } = getMediaTime(media, { player });
467
+ if (streamType === "live" && (liveResume || player.shouldPlayFromEdge || duration === 0) && currentTime < 0) seek(media, { player }, 0);
468
+ player.play?.().catch((error) => console.warn(error));
469
+ player.pendingOperation = media.play().catch((error) => {
470
+ media.autoplay = false;
471
+ media.dispatchEvent(new CustomEvent("pause"));
472
+ console.log("autoplay blocked", error);
473
+ return Promise.reject(error);
474
+ });
475
+ return player.pendingOperation;
476
+ }
477
+ };
478
+ const setPlaybackRate = (media, { player }, rate) => {
479
+ if (!rate) return;
480
+ player?.setPlaybackSpeed?.(rate);
481
+ media.playbackRate = rate;
482
+ };
483
+ const getTextTracks = (_, { player }) => {
484
+ if (!player) return [];
485
+ return (player.getTextTracks?.() || []).map((track) => {
486
+ const label = track.label?.trim() || track.language;
487
+ const id = `${track.language}:${label}`;
488
+ return {
489
+ ...track,
490
+ label,
491
+ type: "subtitles",
492
+ selected: player.isTextTrackVisible() ? track.active : false,
493
+ value: {
494
+ id,
495
+ label,
496
+ language: track.language
497
+ }
498
+ };
499
+ }).filter((track) => track.label !== "unknown" && track.language && !/^un/i.test(track.language));
500
+ };
501
+ const textTrackOptionOff = {
502
+ label: "none",
503
+ language: "none"
504
+ };
505
+ const textTrackLabel = "playcraft-text-track";
506
+ /** @param media HTMLMediaElement */
507
+ const syncTextTrack = (media, { player }, selected) => {
508
+ if (!media) return;
509
+ const { label, language, textTracks = [] } = selected === textTrackOptionOff.label ? textTrackOptionOff : selected;
510
+ const cues = textTracks.find((track) => track.mode === "showing" && track.cues?.length > 0)?.cues;
511
+ let customTextTrack = Array.from(media.textTracks).find((track) => track.label === textTrackLabel);
512
+ Array.from(customTextTrack?.cues || []).forEach((cue) => customTextTrack.removeCue(cue));
513
+ player.setTextTrackVisibility(true);
514
+ if (cues) {
515
+ player?.setTextTrackVisibility(false);
516
+ if (!customTextTrack) customTextTrack = media.addTextTrack("subtitles", textTrackLabel);
517
+ Array.from(cues).forEach((cue) => customTextTrack.addCue(new VTTCue(cue.startTime, cue.endTime, cue.text)));
518
+ customTextTrack.mode = "showing";
519
+ return;
520
+ }
521
+ if (customTextTrack) customTextTrack.mode = "hidden";
522
+ const matchedTrack = getTextTracks(media, { player }).find((t) => (!language || t.language === language) && (!label || t.label === label));
523
+ if (matchedTrack) {
524
+ player?.selectTextTrack(matchedTrack);
525
+ return;
526
+ }
527
+ player.setTextTrackVisibility(false);
528
+ if (!/off|none/i.test(label + language)) console.warn(`turn off text track: ${label} ${language} is not found`);
529
+ return media.textTracks?.addEventListener && once(media.textTracks, "change", () => player.setTextTrackVisibility(false));
530
+ };
531
+ const setAudioTrack = (_, { player }, next) => {
532
+ if (!next || !player) return;
533
+ try {
534
+ player.selectAudioLanguage(next.language);
535
+ if (next.label) player.selectVariantsByLabel(next.label);
536
+ } catch (error) {
537
+ console.warn("Unable to set audio", error, next);
538
+ }
539
+ };
540
+
541
+ //#endregion
542
+ export { once as A, validateEnvironment as C, SeekOrigin as D, LanguageCode as E, dispatchCustomEvent as O, needNativeHls as S, ItemType as T, getOS as _, load as a, isIOS as b, setPlaybackRate as c, syncPlaybackState as d, syncTextTrack as f, getBrowser as g, mimeTypes as h, isLiveDuration as i, on as k, setVolume as l, getSource as m, getTextTracks as n, seek as o, toggleMute as p, isBuffered as r, setAudioTrack as s, getMediaTime as t, subscribePlaybackState as u, havePointer as v, CastState as w, isSafari as x, isDesktop as y };
@@ -0,0 +1,51 @@
1
+ import { getVersion } from "util/index";
2
+ import { dispatchChapterEvents } from "premium/timeline";
3
+ import latencyManager from "playerCore/latencyManager";
4
+ import { createApi, getContentInfo, getStreamInfo } from "playbackSession/api";
5
+ import startSession from "playbackSession/startSession";
6
+ import { logEventNames, mapLogEvents } from "playerCore/playlogv2";
7
+ import * as playlogv3 from "playerCore/playlogv3";
8
+ import { selectHlsQualities } from "playerCore/adaptation";
9
+ import fixDashManifest from "plugins/shaka/fixDashManifest";
10
+ import ensureTabLock from "util/ensureTabLock";
11
+ import { validateEnvironment } from "util/environment";
12
+ import addSentry from "util/addSentry";
13
+ import handleIOSHeadphonesDisconnection from "util/handleIOSHeadphonesDisconnection";
14
+ import { disconnect, initSender, linkCast, loadMedia, subscribeCastState } from "cast/framework";
15
+
16
+ //#region src/modules/retrieveModuleConfig.d.ts
17
+ type ModulesConfig = {
18
+ [key: string]: string;
19
+ };
20
+ //#endregion
21
+ //#region src/modules/analytics.d.ts
22
+ type createAnalyticsParameter = {
23
+ video: HTMLVideoElement;
24
+ sessionId: string;
25
+ onPlaylogFired: any;
26
+ modulesConfig: ModulesConfig;
27
+ };
28
+ /**
29
+ * This method creates analytics module instance and return it.
30
+ * @param createAnalyticsParameter
31
+ * @returns Analytics module instance
32
+ */
33
+ declare const createAnalytics: ({
34
+ video,
35
+ onPlaylogFired,
36
+ modulesConfig
37
+ }: createAnalyticsParameter) => {
38
+ sendEvent: (name: any, {
39
+ currentTime
40
+ }: {
41
+ currentTime?: string | undefined;
42
+ }, properties: any) => void;
43
+ sendLog: (name: any, {
44
+ currentTime
45
+ }: {
46
+ currentTime?: string | undefined;
47
+ }, properties: any) => void;
48
+ reset: () => void;
49
+ };
50
+ //#endregion
51
+ export { addSentry, loadMedia as castMedia, createAnalytics, createApi, dispatchChapterEvents, ensureTabLock, fixDashManifest, getContentInfo, getStreamInfo, getVersion, handleIOSHeadphonesDisconnection, initSender, latencyManager, linkCast, logEventNames, mapLogEvents, playlogv3, selectHlsQualities, startSession, disconnect as stopCast, subscribeCastState, validateEnvironment };