@codenkay/video-nsgplayer-core 1.0.0 → 3.0.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/CHANGELOG.md +26 -0
- package/dist/index.cjs +252 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -2
- package/dist/index.d.ts +52 -2
- package/dist/index.js +252 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -5,6 +5,22 @@ type PlayerState = "idle" | "loading" | "ready" | "playing" | "paused" | "buffer
|
|
|
5
5
|
type HlsSupportMode = "hls.js" | "native" | "unsupported";
|
|
6
6
|
type QualitySelection = "auto" | number;
|
|
7
7
|
type PlaybackRate = 0.5 | 0.75 | 1 | 1.25 | 1.5 | 2;
|
|
8
|
+
type PlayerTextTrack = {
|
|
9
|
+
index: number;
|
|
10
|
+
id: string;
|
|
11
|
+
label: string;
|
|
12
|
+
language?: string;
|
|
13
|
+
uri?: string;
|
|
14
|
+
};
|
|
15
|
+
type PlayerAudioTrack = {
|
|
16
|
+
index: number;
|
|
17
|
+
id: string;
|
|
18
|
+
label: string;
|
|
19
|
+
language?: string;
|
|
20
|
+
uri?: string;
|
|
21
|
+
};
|
|
22
|
+
/** Runtime caption selection: off or a concrete subtitle track index. */
|
|
23
|
+
type TextTrackSelection = "off" | number;
|
|
8
24
|
type HlsQualityLevel = {
|
|
9
25
|
index: number;
|
|
10
26
|
height: number;
|
|
@@ -222,6 +238,14 @@ type PlayerEventMap = {
|
|
|
222
238
|
selection: QualitySelection;
|
|
223
239
|
label?: string;
|
|
224
240
|
};
|
|
241
|
+
texttrackchange: {
|
|
242
|
+
tracks: PlayerTextTrack[];
|
|
243
|
+
selection: TextTrackSelection;
|
|
244
|
+
};
|
|
245
|
+
audiotrackchange: {
|
|
246
|
+
tracks: PlayerAudioTrack[];
|
|
247
|
+
selection: number;
|
|
248
|
+
};
|
|
225
249
|
volumechange: {
|
|
226
250
|
volume: number;
|
|
227
251
|
muted: boolean;
|
|
@@ -255,7 +279,7 @@ type PlayerEventMap = {
|
|
|
255
279
|
};
|
|
256
280
|
|
|
257
281
|
declare const PLAYBACK_RATES: readonly [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
258
|
-
declare const PACKAGE_VERSION = "0.1
|
|
282
|
+
declare const PACKAGE_VERSION = "2.0.1";
|
|
259
283
|
declare const DEFAULT_PLAYER_BEHAVIOR: Required<PlayerBehaviorConfig>;
|
|
260
284
|
declare const DEFAULT_CONTROLS: Required<ControlsConfig>;
|
|
261
285
|
declare const DEFAULT_THEME: Required<ThemeConfig>;
|
|
@@ -390,6 +414,10 @@ type HlsEngineCallbacks = {
|
|
|
390
414
|
onAuthRecoveryNeeded?: (reason: string) => void;
|
|
391
415
|
onQualitiesChange?: (levels: HlsQualityLevel[]) => void;
|
|
392
416
|
onQualitySelectionChange?: (selection: QualitySelection) => void;
|
|
417
|
+
onTextTracksChange?: (tracks: PlayerTextTrack[]) => void;
|
|
418
|
+
onTextTrackSelectionChange?: (selection: TextTrackSelection) => void;
|
|
419
|
+
onAudioTracksChange?: (tracks: PlayerAudioTrack[]) => void;
|
|
420
|
+
onAudioTrackSelectionChange?: (selection: number) => void;
|
|
393
421
|
onEnded?: () => void;
|
|
394
422
|
};
|
|
395
423
|
type HlsEngineOptions = {
|
|
@@ -431,6 +459,11 @@ declare class HlsEngine {
|
|
|
431
459
|
private mediaRecoveryCount;
|
|
432
460
|
private qualities;
|
|
433
461
|
private qualitySelection;
|
|
462
|
+
private textTracks;
|
|
463
|
+
private textTrackSelection;
|
|
464
|
+
private audioTracks;
|
|
465
|
+
private audioTrackSelection;
|
|
466
|
+
private nativeTextTrackCleanup;
|
|
434
467
|
constructor(options: HlsEngineOptions);
|
|
435
468
|
start(): Promise<void>;
|
|
436
469
|
private startWithHlsJs;
|
|
@@ -442,7 +475,16 @@ declare class HlsEngine {
|
|
|
442
475
|
setQuality(selection: QualitySelection): void;
|
|
443
476
|
getQuality(): QualitySelection;
|
|
444
477
|
getQualities(): HlsQualityLevel[];
|
|
478
|
+
getTextTracks(): PlayerTextTrack[];
|
|
479
|
+
getTextTrackSelection(): TextTrackSelection;
|
|
480
|
+
setTextTrack(selection: TextTrackSelection): void;
|
|
481
|
+
getAudioTracks(): PlayerAudioTrack[];
|
|
482
|
+
getAudioTrackSelection(): number;
|
|
483
|
+
setAudioTrack(index: number): void;
|
|
445
484
|
getSupportMode(): HlsSupportMode;
|
|
485
|
+
private syncHlsSubtitleTracks;
|
|
486
|
+
private syncHlsAudioTracks;
|
|
487
|
+
private bindNativeTextTracks;
|
|
446
488
|
destroy(): void;
|
|
447
489
|
}
|
|
448
490
|
|
|
@@ -468,9 +510,17 @@ type VideoPlayer = {
|
|
|
468
510
|
getQuality: () => QualitySelection;
|
|
469
511
|
setQuality: (selection: QualitySelection | string) => void;
|
|
470
512
|
getAvailableQualities: () => ReturnType<HlsEngine["getQualities"]>;
|
|
513
|
+
getTextTracks: () => PlayerTextTrack[];
|
|
514
|
+
getSelectedTextTrack: () => TextTrackSelection;
|
|
515
|
+
setTextTrack: (selection: TextTrackSelection) => void;
|
|
516
|
+
getAudioTracks: () => PlayerAudioTrack[];
|
|
517
|
+
getSelectedAudioTrack: () => number;
|
|
518
|
+
setAudioTrack: (index: number) => void;
|
|
471
519
|
enterFullscreen: () => Promise<void>;
|
|
472
520
|
exitFullscreen: () => Promise<void>;
|
|
473
521
|
isFullscreen: () => boolean;
|
|
522
|
+
/** Attach fullscreen target container (host UI chrome). Zoom stays UI-only. */
|
|
523
|
+
setFullscreenElement: (el: HTMLElement | null) => void;
|
|
474
524
|
reload: () => Promise<void>;
|
|
475
525
|
destroy: () => void;
|
|
476
526
|
getDiagnostics: () => PlayerDiagnostics;
|
|
@@ -642,4 +692,4 @@ declare function redactValue(key: string, value: unknown, seen?: WeakSet<object>
|
|
|
642
692
|
*/
|
|
643
693
|
declare function redactObject(obj: Record<string, unknown>, seen?: WeakSet<object>, depth?: number): Record<string, unknown>;
|
|
644
694
|
|
|
645
|
-
export { type AccessibilityConfig, type AnalyticsConfig, type AuthConfig, AuthManager, AuthParamsStore, type AuthToken, type AuthTokenProviderContext, type BufferedRange, type ControlsConfig, type CreateVideoPlayerOptions, DEFAULT_CONTROLS, DEFAULT_LIVE_EDGE_TOLERANCE_SECONDS, DEFAULT_PLAYER_BEHAVIOR, DEFAULT_PLAYER_CONFIG, DEFAULT_THEME, DEFAULT_WATERMARK, type DebugConfig, EventEmitter, type HlsQualityLevel, type HlsSupportMode, type LiveMediaSnapshot, type LoggingConfig, PACKAGE_VERSION, PLAYBACK_RATES, type PlayIconPreset, type PlaybackCredentialConfig, type PlaybackRate, type PlaybackSource, type PlaybackSourceContext, PlaybackSourceManager, type PlayerBehaviorConfig, type PlayerConfig, type PlayerDiagnostics, type PlayerError, PlayerErrorCode, type PlayerEventMap, type PlayerSkipSeconds, type PlayerState, type PlayerZoomLevel, type QualitySelection, RefreshManager, SECRET_SCAN_MAX_DEPTH, type SeekToLiveResult, type StreamingConfig, type ThemeConfig, type VideoPlayer, type WatermarkConfig, assertNoSecretsInConfig, createAuthParamsLoader, createPlayerError, createVideoPlayer, getLiveEdgeTime, isAtLiveEdge, isEncryptionKeyUrl, isPlainObject, mergePlayerConfig, redactObject, redactSensitive, redactValue, resolveBuiltinHlsLoader, rewriteHlsRequestUrl, seekToLiveEdge, themeToCssVars };
|
|
695
|
+
export { type AccessibilityConfig, type AnalyticsConfig, type AuthConfig, AuthManager, AuthParamsStore, type AuthToken, type AuthTokenProviderContext, type BufferedRange, type ControlsConfig, type CreateVideoPlayerOptions, DEFAULT_CONTROLS, DEFAULT_LIVE_EDGE_TOLERANCE_SECONDS, DEFAULT_PLAYER_BEHAVIOR, DEFAULT_PLAYER_CONFIG, DEFAULT_THEME, DEFAULT_WATERMARK, type DebugConfig, EventEmitter, type HlsQualityLevel, type HlsSupportMode, type LiveMediaSnapshot, type LoggingConfig, PACKAGE_VERSION, PLAYBACK_RATES, type PlayIconPreset, type PlaybackCredentialConfig, type PlaybackRate, type PlaybackSource, type PlaybackSourceContext, PlaybackSourceManager, type PlayerAudioTrack, type PlayerBehaviorConfig, type PlayerConfig, type PlayerDiagnostics, type PlayerError, PlayerErrorCode, type PlayerEventMap, type PlayerSkipSeconds, type PlayerState, type PlayerTextTrack, type PlayerZoomLevel, type QualitySelection, RefreshManager, SECRET_SCAN_MAX_DEPTH, type SeekToLiveResult, type StreamingConfig, type TextTrackSelection, type ThemeConfig, type VideoPlayer, type WatermarkConfig, assertNoSecretsInConfig, createAuthParamsLoader, createPlayerError, createVideoPlayer, getLiveEdgeTime, isAtLiveEdge, isEncryptionKeyUrl, isPlainObject, mergePlayerConfig, redactObject, redactSensitive, redactValue, resolveBuiltinHlsLoader, rewriteHlsRequestUrl, seekToLiveEdge, themeToCssVars };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,22 @@ type PlayerState = "idle" | "loading" | "ready" | "playing" | "paused" | "buffer
|
|
|
5
5
|
type HlsSupportMode = "hls.js" | "native" | "unsupported";
|
|
6
6
|
type QualitySelection = "auto" | number;
|
|
7
7
|
type PlaybackRate = 0.5 | 0.75 | 1 | 1.25 | 1.5 | 2;
|
|
8
|
+
type PlayerTextTrack = {
|
|
9
|
+
index: number;
|
|
10
|
+
id: string;
|
|
11
|
+
label: string;
|
|
12
|
+
language?: string;
|
|
13
|
+
uri?: string;
|
|
14
|
+
};
|
|
15
|
+
type PlayerAudioTrack = {
|
|
16
|
+
index: number;
|
|
17
|
+
id: string;
|
|
18
|
+
label: string;
|
|
19
|
+
language?: string;
|
|
20
|
+
uri?: string;
|
|
21
|
+
};
|
|
22
|
+
/** Runtime caption selection: off or a concrete subtitle track index. */
|
|
23
|
+
type TextTrackSelection = "off" | number;
|
|
8
24
|
type HlsQualityLevel = {
|
|
9
25
|
index: number;
|
|
10
26
|
height: number;
|
|
@@ -222,6 +238,14 @@ type PlayerEventMap = {
|
|
|
222
238
|
selection: QualitySelection;
|
|
223
239
|
label?: string;
|
|
224
240
|
};
|
|
241
|
+
texttrackchange: {
|
|
242
|
+
tracks: PlayerTextTrack[];
|
|
243
|
+
selection: TextTrackSelection;
|
|
244
|
+
};
|
|
245
|
+
audiotrackchange: {
|
|
246
|
+
tracks: PlayerAudioTrack[];
|
|
247
|
+
selection: number;
|
|
248
|
+
};
|
|
225
249
|
volumechange: {
|
|
226
250
|
volume: number;
|
|
227
251
|
muted: boolean;
|
|
@@ -255,7 +279,7 @@ type PlayerEventMap = {
|
|
|
255
279
|
};
|
|
256
280
|
|
|
257
281
|
declare const PLAYBACK_RATES: readonly [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
258
|
-
declare const PACKAGE_VERSION = "0.1
|
|
282
|
+
declare const PACKAGE_VERSION = "2.0.1";
|
|
259
283
|
declare const DEFAULT_PLAYER_BEHAVIOR: Required<PlayerBehaviorConfig>;
|
|
260
284
|
declare const DEFAULT_CONTROLS: Required<ControlsConfig>;
|
|
261
285
|
declare const DEFAULT_THEME: Required<ThemeConfig>;
|
|
@@ -390,6 +414,10 @@ type HlsEngineCallbacks = {
|
|
|
390
414
|
onAuthRecoveryNeeded?: (reason: string) => void;
|
|
391
415
|
onQualitiesChange?: (levels: HlsQualityLevel[]) => void;
|
|
392
416
|
onQualitySelectionChange?: (selection: QualitySelection) => void;
|
|
417
|
+
onTextTracksChange?: (tracks: PlayerTextTrack[]) => void;
|
|
418
|
+
onTextTrackSelectionChange?: (selection: TextTrackSelection) => void;
|
|
419
|
+
onAudioTracksChange?: (tracks: PlayerAudioTrack[]) => void;
|
|
420
|
+
onAudioTrackSelectionChange?: (selection: number) => void;
|
|
393
421
|
onEnded?: () => void;
|
|
394
422
|
};
|
|
395
423
|
type HlsEngineOptions = {
|
|
@@ -431,6 +459,11 @@ declare class HlsEngine {
|
|
|
431
459
|
private mediaRecoveryCount;
|
|
432
460
|
private qualities;
|
|
433
461
|
private qualitySelection;
|
|
462
|
+
private textTracks;
|
|
463
|
+
private textTrackSelection;
|
|
464
|
+
private audioTracks;
|
|
465
|
+
private audioTrackSelection;
|
|
466
|
+
private nativeTextTrackCleanup;
|
|
434
467
|
constructor(options: HlsEngineOptions);
|
|
435
468
|
start(): Promise<void>;
|
|
436
469
|
private startWithHlsJs;
|
|
@@ -442,7 +475,16 @@ declare class HlsEngine {
|
|
|
442
475
|
setQuality(selection: QualitySelection): void;
|
|
443
476
|
getQuality(): QualitySelection;
|
|
444
477
|
getQualities(): HlsQualityLevel[];
|
|
478
|
+
getTextTracks(): PlayerTextTrack[];
|
|
479
|
+
getTextTrackSelection(): TextTrackSelection;
|
|
480
|
+
setTextTrack(selection: TextTrackSelection): void;
|
|
481
|
+
getAudioTracks(): PlayerAudioTrack[];
|
|
482
|
+
getAudioTrackSelection(): number;
|
|
483
|
+
setAudioTrack(index: number): void;
|
|
445
484
|
getSupportMode(): HlsSupportMode;
|
|
485
|
+
private syncHlsSubtitleTracks;
|
|
486
|
+
private syncHlsAudioTracks;
|
|
487
|
+
private bindNativeTextTracks;
|
|
446
488
|
destroy(): void;
|
|
447
489
|
}
|
|
448
490
|
|
|
@@ -468,9 +510,17 @@ type VideoPlayer = {
|
|
|
468
510
|
getQuality: () => QualitySelection;
|
|
469
511
|
setQuality: (selection: QualitySelection | string) => void;
|
|
470
512
|
getAvailableQualities: () => ReturnType<HlsEngine["getQualities"]>;
|
|
513
|
+
getTextTracks: () => PlayerTextTrack[];
|
|
514
|
+
getSelectedTextTrack: () => TextTrackSelection;
|
|
515
|
+
setTextTrack: (selection: TextTrackSelection) => void;
|
|
516
|
+
getAudioTracks: () => PlayerAudioTrack[];
|
|
517
|
+
getSelectedAudioTrack: () => number;
|
|
518
|
+
setAudioTrack: (index: number) => void;
|
|
471
519
|
enterFullscreen: () => Promise<void>;
|
|
472
520
|
exitFullscreen: () => Promise<void>;
|
|
473
521
|
isFullscreen: () => boolean;
|
|
522
|
+
/** Attach fullscreen target container (host UI chrome). Zoom stays UI-only. */
|
|
523
|
+
setFullscreenElement: (el: HTMLElement | null) => void;
|
|
474
524
|
reload: () => Promise<void>;
|
|
475
525
|
destroy: () => void;
|
|
476
526
|
getDiagnostics: () => PlayerDiagnostics;
|
|
@@ -642,4 +692,4 @@ declare function redactValue(key: string, value: unknown, seen?: WeakSet<object>
|
|
|
642
692
|
*/
|
|
643
693
|
declare function redactObject(obj: Record<string, unknown>, seen?: WeakSet<object>, depth?: number): Record<string, unknown>;
|
|
644
694
|
|
|
645
|
-
export { type AccessibilityConfig, type AnalyticsConfig, type AuthConfig, AuthManager, AuthParamsStore, type AuthToken, type AuthTokenProviderContext, type BufferedRange, type ControlsConfig, type CreateVideoPlayerOptions, DEFAULT_CONTROLS, DEFAULT_LIVE_EDGE_TOLERANCE_SECONDS, DEFAULT_PLAYER_BEHAVIOR, DEFAULT_PLAYER_CONFIG, DEFAULT_THEME, DEFAULT_WATERMARK, type DebugConfig, EventEmitter, type HlsQualityLevel, type HlsSupportMode, type LiveMediaSnapshot, type LoggingConfig, PACKAGE_VERSION, PLAYBACK_RATES, type PlayIconPreset, type PlaybackCredentialConfig, type PlaybackRate, type PlaybackSource, type PlaybackSourceContext, PlaybackSourceManager, type PlayerBehaviorConfig, type PlayerConfig, type PlayerDiagnostics, type PlayerError, PlayerErrorCode, type PlayerEventMap, type PlayerSkipSeconds, type PlayerState, type PlayerZoomLevel, type QualitySelection, RefreshManager, SECRET_SCAN_MAX_DEPTH, type SeekToLiveResult, type StreamingConfig, type ThemeConfig, type VideoPlayer, type WatermarkConfig, assertNoSecretsInConfig, createAuthParamsLoader, createPlayerError, createVideoPlayer, getLiveEdgeTime, isAtLiveEdge, isEncryptionKeyUrl, isPlainObject, mergePlayerConfig, redactObject, redactSensitive, redactValue, resolveBuiltinHlsLoader, rewriteHlsRequestUrl, seekToLiveEdge, themeToCssVars };
|
|
695
|
+
export { type AccessibilityConfig, type AnalyticsConfig, type AuthConfig, AuthManager, AuthParamsStore, type AuthToken, type AuthTokenProviderContext, type BufferedRange, type ControlsConfig, type CreateVideoPlayerOptions, DEFAULT_CONTROLS, DEFAULT_LIVE_EDGE_TOLERANCE_SECONDS, DEFAULT_PLAYER_BEHAVIOR, DEFAULT_PLAYER_CONFIG, DEFAULT_THEME, DEFAULT_WATERMARK, type DebugConfig, EventEmitter, type HlsQualityLevel, type HlsSupportMode, type LiveMediaSnapshot, type LoggingConfig, PACKAGE_VERSION, PLAYBACK_RATES, type PlayIconPreset, type PlaybackCredentialConfig, type PlaybackRate, type PlaybackSource, type PlaybackSourceContext, PlaybackSourceManager, type PlayerAudioTrack, type PlayerBehaviorConfig, type PlayerConfig, type PlayerDiagnostics, type PlayerError, PlayerErrorCode, type PlayerEventMap, type PlayerSkipSeconds, type PlayerState, type PlayerTextTrack, type PlayerZoomLevel, type QualitySelection, RefreshManager, SECRET_SCAN_MAX_DEPTH, type SeekToLiveResult, type StreamingConfig, type TextTrackSelection, type ThemeConfig, type VideoPlayer, type WatermarkConfig, assertNoSecretsInConfig, createAuthParamsLoader, createPlayerError, createVideoPlayer, getLiveEdgeTime, isAtLiveEdge, isEncryptionKeyUrl, isPlainObject, mergePlayerConfig, redactObject, redactSensitive, redactValue, resolveBuiltinHlsLoader, rewriteHlsRequestUrl, seekToLiveEdge, themeToCssVars };
|
package/dist/index.js
CHANGED
|
@@ -146,7 +146,7 @@ var AuthManager = class {
|
|
|
146
146
|
|
|
147
147
|
// src/config/defaults.ts
|
|
148
148
|
var PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
149
|
-
var PACKAGE_VERSION = "0.1
|
|
149
|
+
var PACKAGE_VERSION = "2.0.1";
|
|
150
150
|
var DEFAULT_PLAYER_BEHAVIOR = {
|
|
151
151
|
autoplay: true,
|
|
152
152
|
startMuted: true,
|
|
@@ -915,9 +915,48 @@ function createAuthParamsLoader(HlsClass, store, options = {}) {
|
|
|
915
915
|
const usingKeyProxy = Boolean(
|
|
916
916
|
isKey && options.keyProxyUrlBuilder && options.videoId
|
|
917
917
|
);
|
|
918
|
+
const signedKeyUrl = isKey ? rewriteHlsRequestUrl(originalUrl, store, {
|
|
919
|
+
...options,
|
|
920
|
+
keyProxyUrlBuilder: void 0
|
|
921
|
+
}) : context.url;
|
|
918
922
|
context.url = rewriteHlsRequestUrl(originalUrl, store, options);
|
|
919
923
|
if (usingKeyProxy) {
|
|
920
|
-
|
|
924
|
+
const accessToken = options.getKeyAccessToken?.() ?? null;
|
|
925
|
+
if (!accessToken) {
|
|
926
|
+
this.inner.load(context, config, callbacks);
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
const proxyCallbacks = {
|
|
930
|
+
...callbacks,
|
|
931
|
+
onError: (error, errContext, networkDetails, stats) => {
|
|
932
|
+
const code = error?.code;
|
|
933
|
+
const retryable = code === 0 || code === 502 || code === 403 || code === 401 || /Cloudflare|HTML|DOCTYPE|blocked/i.test(error?.text ?? "");
|
|
934
|
+
if (!retryable) {
|
|
935
|
+
callbacks.onError(error, errContext, networkDetails, stats);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
this.keyAbort?.abort();
|
|
939
|
+
this.keyAbort = new AbortController();
|
|
940
|
+
const bearerContext = { ...context, url: signedKeyUrl };
|
|
941
|
+
void loadEncryptionKeyWithFetch(
|
|
942
|
+
bearerContext,
|
|
943
|
+
callbacks,
|
|
944
|
+
accessToken,
|
|
945
|
+
this.keyAbort.signal
|
|
946
|
+
).catch((fetchError) => {
|
|
947
|
+
callbacks.onError(
|
|
948
|
+
{
|
|
949
|
+
code: 0,
|
|
950
|
+
text: fetchError instanceof Error ? fetchError.message : error.text || "enc.key proxy and Bearer fetch failed"
|
|
951
|
+
},
|
|
952
|
+
bearerContext,
|
|
953
|
+
null,
|
|
954
|
+
stats
|
|
955
|
+
);
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
this.inner.load(context, config, proxyCallbacks);
|
|
921
960
|
return;
|
|
922
961
|
}
|
|
923
962
|
if (isKey && typeof options.getKeyAccessToken === "function") {
|
|
@@ -935,6 +974,10 @@ function createAuthParamsLoader(HlsClass, store, options = {}) {
|
|
|
935
974
|
).catch((error) => {
|
|
936
975
|
const stats = createLoaderStats();
|
|
937
976
|
const aborted = error instanceof DOMException && error.name === "AbortError";
|
|
977
|
+
if (!aborted && (error instanceof TypeError || error instanceof Error && /Failed to fetch|NetworkError|CORS/i.test(error.message))) {
|
|
978
|
+
this.inner.load(context, config, callbacks);
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
938
981
|
callbacks.onError(
|
|
939
982
|
{
|
|
940
983
|
code: 0,
|
|
@@ -971,6 +1014,11 @@ var HlsEngine = class {
|
|
|
971
1014
|
this.mediaRecoveryCount = 0;
|
|
972
1015
|
this.qualities = [];
|
|
973
1016
|
this.qualitySelection = "auto";
|
|
1017
|
+
this.textTracks = [];
|
|
1018
|
+
this.textTrackSelection = "off";
|
|
1019
|
+
this.audioTracks = [];
|
|
1020
|
+
this.audioTrackSelection = 0;
|
|
1021
|
+
this.nativeTextTrackCleanup = null;
|
|
974
1022
|
}
|
|
975
1023
|
async start() {
|
|
976
1024
|
const { videoEl, playbackUrl, startMuted, volume } = this.options;
|
|
@@ -990,6 +1038,7 @@ var HlsEngine = class {
|
|
|
990
1038
|
this.options.callbacks.onSupportMode?.("native");
|
|
991
1039
|
videoEl.src = this.options.authParams.applyToUrl(playbackUrl);
|
|
992
1040
|
this.bindMediaEvents();
|
|
1041
|
+
this.bindNativeTextTracks();
|
|
993
1042
|
this.options.callbacks.onStateChange?.("ready");
|
|
994
1043
|
await this.applyStartPositionAndAutoplay();
|
|
995
1044
|
return;
|
|
@@ -1033,9 +1082,19 @@ var HlsEngine = class {
|
|
|
1033
1082
|
label: level.height != null && level.height > 0 ? `${level.height}p` : `Level ${index}`
|
|
1034
1083
|
}));
|
|
1035
1084
|
callbacks.onQualitiesChange?.(this.qualities);
|
|
1085
|
+
this.syncHlsSubtitleTracks();
|
|
1086
|
+
this.syncHlsAudioTracks();
|
|
1036
1087
|
callbacks.onStateChange?.("ready");
|
|
1037
1088
|
void this.applyStartPositionAndAutoplay();
|
|
1038
1089
|
});
|
|
1090
|
+
this.hls.on(Hls.Events.SUBTITLE_TRACKS_UPDATED, () => {
|
|
1091
|
+
if (this.destroyed) return;
|
|
1092
|
+
this.syncHlsSubtitleTracks();
|
|
1093
|
+
});
|
|
1094
|
+
this.hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, () => {
|
|
1095
|
+
if (this.destroyed) return;
|
|
1096
|
+
this.syncHlsAudioTracks();
|
|
1097
|
+
});
|
|
1039
1098
|
this.hls.on(Hls.Events.ERROR, (_event, data) => {
|
|
1040
1099
|
this.handleHlsError(Hls, data);
|
|
1041
1100
|
});
|
|
@@ -1161,12 +1220,120 @@ var HlsEngine = class {
|
|
|
1161
1220
|
getQualities() {
|
|
1162
1221
|
return this.qualities;
|
|
1163
1222
|
}
|
|
1223
|
+
getTextTracks() {
|
|
1224
|
+
return this.textTracks;
|
|
1225
|
+
}
|
|
1226
|
+
getTextTrackSelection() {
|
|
1227
|
+
return this.textTrackSelection;
|
|
1228
|
+
}
|
|
1229
|
+
setTextTrack(selection) {
|
|
1230
|
+
if (this.destroyed) return;
|
|
1231
|
+
this.textTrackSelection = selection;
|
|
1232
|
+
this.options.callbacks.onTextTrackSelectionChange?.(selection);
|
|
1233
|
+
if (this.supportMode === "hls.js" && this.hls) {
|
|
1234
|
+
this.hls.subtitleDisplay = selection !== "off";
|
|
1235
|
+
this.hls.subtitleTrack = selection === "off" ? -1 : selection;
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
if (this.supportMode === "native" && this.options.videoEl) {
|
|
1239
|
+
const tracks = this.options.videoEl.textTracks;
|
|
1240
|
+
for (let i = 0; i < tracks.length; i += 1) {
|
|
1241
|
+
const track = tracks[i];
|
|
1242
|
+
track.mode = selection !== "off" && i === selection ? "showing" : "disabled";
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
getAudioTracks() {
|
|
1247
|
+
return this.audioTracks;
|
|
1248
|
+
}
|
|
1249
|
+
getAudioTrackSelection() {
|
|
1250
|
+
return this.audioTrackSelection;
|
|
1251
|
+
}
|
|
1252
|
+
setAudioTrack(index) {
|
|
1253
|
+
if (this.destroyed) return;
|
|
1254
|
+
if (!Number.isInteger(index) || index < 0 || this.audioTracks.length > 0 && index >= this.audioTracks.length) {
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
this.audioTrackSelection = index;
|
|
1258
|
+
this.options.callbacks.onAudioTrackSelectionChange?.(index);
|
|
1259
|
+
if (this.supportMode === "hls.js" && this.hls) {
|
|
1260
|
+
this.hls.audioTrack = index;
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
if (this.supportMode === "native" && this.options.videoEl) {
|
|
1264
|
+
const audioTracks = this.options.videoEl.audioTracks;
|
|
1265
|
+
if (audioTracks && audioTracks.length > 0) {
|
|
1266
|
+
for (let i = 0; i < audioTracks.length; i += 1) {
|
|
1267
|
+
audioTracks[i].enabled = i === index;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1164
1272
|
getSupportMode() {
|
|
1165
1273
|
return this.supportMode;
|
|
1166
1274
|
}
|
|
1275
|
+
syncHlsSubtitleTracks() {
|
|
1276
|
+
if (!this.hls) return;
|
|
1277
|
+
const tracks = (this.hls.subtitleTracks ?? []).map(
|
|
1278
|
+
(track, index) => ({
|
|
1279
|
+
index,
|
|
1280
|
+
id: String(track.id ?? index),
|
|
1281
|
+
label: track.name || track.lang || `Subtitles ${index + 1}`,
|
|
1282
|
+
language: track.lang
|
|
1283
|
+
})
|
|
1284
|
+
);
|
|
1285
|
+
this.textTracks = tracks;
|
|
1286
|
+
this.options.callbacks.onTextTracksChange?.(tracks);
|
|
1287
|
+
}
|
|
1288
|
+
syncHlsAudioTracks() {
|
|
1289
|
+
if (!this.hls) return;
|
|
1290
|
+
const tracks = (this.hls.audioTracks ?? []).map(
|
|
1291
|
+
(track, index) => ({
|
|
1292
|
+
index,
|
|
1293
|
+
id: String(track.id ?? index),
|
|
1294
|
+
label: track.name || track.lang || `Audio ${index + 1}`,
|
|
1295
|
+
language: track.lang
|
|
1296
|
+
})
|
|
1297
|
+
);
|
|
1298
|
+
this.audioTracks = tracks;
|
|
1299
|
+
this.options.callbacks.onAudioTracksChange?.(tracks);
|
|
1300
|
+
if (tracks.length > 0) {
|
|
1301
|
+
this.audioTrackSelection = 0;
|
|
1302
|
+
this.options.callbacks.onAudioTrackSelectionChange?.(0);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
bindNativeTextTracks() {
|
|
1306
|
+
const { videoEl } = this.options;
|
|
1307
|
+
const syncFromElement = () => {
|
|
1308
|
+
if (this.destroyed || this.textTracks.length > 0) return;
|
|
1309
|
+
const list = videoEl.textTracks;
|
|
1310
|
+
const tracks = [];
|
|
1311
|
+
for (let i = 0; i < list.length; i += 1) {
|
|
1312
|
+
const track = list[i];
|
|
1313
|
+
if (track.kind !== "subtitles" && track.kind !== "captions") continue;
|
|
1314
|
+
tracks.push({
|
|
1315
|
+
index: tracks.length,
|
|
1316
|
+
id: track.id || `native-${tracks.length}`,
|
|
1317
|
+
label: track.label || track.language || `Track ${tracks.length + 1}`,
|
|
1318
|
+
language: track.language || void 0
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
if (tracks.length === 0) return;
|
|
1322
|
+
this.textTracks = tracks;
|
|
1323
|
+
this.options.callbacks.onTextTracksChange?.(tracks);
|
|
1324
|
+
};
|
|
1325
|
+
const textTracks = videoEl.textTracks;
|
|
1326
|
+
textTracks.addEventListener?.("addtrack", syncFromElement);
|
|
1327
|
+
syncFromElement();
|
|
1328
|
+
this.nativeTextTrackCleanup = () => {
|
|
1329
|
+
textTracks.removeEventListener?.("addtrack", syncFromElement);
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1167
1332
|
destroy() {
|
|
1168
1333
|
if (this.destroyed) return;
|
|
1169
1334
|
this.destroyed = true;
|
|
1335
|
+
this.nativeTextTrackCleanup?.();
|
|
1336
|
+
this.nativeTextTrackCleanup = null;
|
|
1170
1337
|
this.hls?.destroy();
|
|
1171
1338
|
this.hls = null;
|
|
1172
1339
|
const { videoEl } = this.options;
|
|
@@ -1223,11 +1390,40 @@ function createVideoPlayer(options) {
|
|
|
1223
1390
|
let refreshManager = null;
|
|
1224
1391
|
let fullscreenEl = null;
|
|
1225
1392
|
let isLiveStream = false;
|
|
1393
|
+
let mediaCleanup = null;
|
|
1226
1394
|
const setState = (next) => {
|
|
1227
1395
|
if (destroyed && next !== "destroyed") return;
|
|
1228
1396
|
state = next;
|
|
1229
1397
|
events.emit("statechange", { state: next });
|
|
1230
1398
|
};
|
|
1399
|
+
const bindMediaProgressEvents = (el) => {
|
|
1400
|
+
mediaCleanup?.();
|
|
1401
|
+
const onTimeUpdate = () => {
|
|
1402
|
+
if (destroyed) return;
|
|
1403
|
+
events.emit("timeupdate", {
|
|
1404
|
+
currentTime: el.currentTime,
|
|
1405
|
+
duration: Number.isFinite(el.duration) ? el.duration : 0
|
|
1406
|
+
});
|
|
1407
|
+
};
|
|
1408
|
+
const onProgress = () => {
|
|
1409
|
+
if (destroyed) return;
|
|
1410
|
+
let bufferedEnd = 0;
|
|
1411
|
+
try {
|
|
1412
|
+
if (el.buffered.length > 0) {
|
|
1413
|
+
bufferedEnd = el.buffered.end(el.buffered.length - 1);
|
|
1414
|
+
}
|
|
1415
|
+
} catch {
|
|
1416
|
+
bufferedEnd = 0;
|
|
1417
|
+
}
|
|
1418
|
+
events.emit("progress", { bufferedEnd });
|
|
1419
|
+
};
|
|
1420
|
+
el.addEventListener("timeupdate", onTimeUpdate);
|
|
1421
|
+
el.addEventListener("progress", onProgress);
|
|
1422
|
+
mediaCleanup = () => {
|
|
1423
|
+
el.removeEventListener("timeupdate", onTimeUpdate);
|
|
1424
|
+
el.removeEventListener("progress", onProgress);
|
|
1425
|
+
};
|
|
1426
|
+
};
|
|
1231
1427
|
const authManager = new AuthManager(
|
|
1232
1428
|
options.auth,
|
|
1233
1429
|
options.auth?.refreshBeforeExpiryMs ?? 6e4,
|
|
@@ -1278,6 +1474,7 @@ function createVideoPlayer(options) {
|
|
|
1278
1474
|
isLiveStream = Boolean(source.live);
|
|
1279
1475
|
}
|
|
1280
1476
|
const el = ensureVideoElement();
|
|
1477
|
+
bindMediaProgressEvents(el);
|
|
1281
1478
|
engine?.destroy();
|
|
1282
1479
|
engine = new HlsEngine({
|
|
1283
1480
|
videoEl: el,
|
|
@@ -1289,12 +1486,11 @@ function createVideoPlayer(options) {
|
|
|
1289
1486
|
volume: config.player.volume,
|
|
1290
1487
|
videoId: options.videoId,
|
|
1291
1488
|
keyProxyUrlBuilder: config.streaming.keyProxyUrlBuilder,
|
|
1292
|
-
//
|
|
1293
|
-
//
|
|
1294
|
-
//
|
|
1295
|
-
//
|
|
1296
|
-
|
|
1297
|
-
getKeyAccessToken: config.streaming.getKeyAccessToken,
|
|
1489
|
+
// Enterprise AES key auth:
|
|
1490
|
+
// 1) keyProxyUrlBuilder (same-origin BFF) when configured
|
|
1491
|
+
// 2) else getKeyAccessToken / AuthManager Bearer via browser fetch
|
|
1492
|
+
// 3) else signed query + XHR (CDN hosts)
|
|
1493
|
+
getKeyAccessToken: config.streaming.getKeyAccessToken ?? (() => authManager.getAccessToken()),
|
|
1298
1494
|
hlsConfig: config.streaming.hls,
|
|
1299
1495
|
logger,
|
|
1300
1496
|
callbacks: {
|
|
@@ -1336,6 +1532,30 @@ function createVideoPlayer(options) {
|
|
|
1336
1532
|
label: typeof selection === "number" ? level?.label : "auto"
|
|
1337
1533
|
});
|
|
1338
1534
|
},
|
|
1535
|
+
onTextTracksChange: (tracks) => {
|
|
1536
|
+
events.emit("texttrackchange", {
|
|
1537
|
+
tracks,
|
|
1538
|
+
selection: engine?.getTextTrackSelection() ?? "off"
|
|
1539
|
+
});
|
|
1540
|
+
},
|
|
1541
|
+
onTextTrackSelectionChange: (selection) => {
|
|
1542
|
+
events.emit("texttrackchange", {
|
|
1543
|
+
tracks: engine?.getTextTracks() ?? [],
|
|
1544
|
+
selection
|
|
1545
|
+
});
|
|
1546
|
+
},
|
|
1547
|
+
onAudioTracksChange: (tracks) => {
|
|
1548
|
+
events.emit("audiotrackchange", {
|
|
1549
|
+
tracks,
|
|
1550
|
+
selection: engine?.getAudioTrackSelection() ?? 0
|
|
1551
|
+
});
|
|
1552
|
+
},
|
|
1553
|
+
onAudioTrackSelectionChange: (selection) => {
|
|
1554
|
+
events.emit("audiotrackchange", {
|
|
1555
|
+
tracks: engine?.getAudioTracks() ?? [],
|
|
1556
|
+
selection
|
|
1557
|
+
});
|
|
1558
|
+
},
|
|
1339
1559
|
onEnded: () => events.emit("ended")
|
|
1340
1560
|
}
|
|
1341
1561
|
});
|
|
@@ -1494,6 +1714,24 @@ function createVideoPlayer(options) {
|
|
|
1494
1714
|
getAvailableQualities() {
|
|
1495
1715
|
return engine?.getQualities() ?? [];
|
|
1496
1716
|
},
|
|
1717
|
+
getTextTracks() {
|
|
1718
|
+
return engine?.getTextTracks() ?? [];
|
|
1719
|
+
},
|
|
1720
|
+
getSelectedTextTrack() {
|
|
1721
|
+
return engine?.getTextTrackSelection() ?? "off";
|
|
1722
|
+
},
|
|
1723
|
+
setTextTrack(selection) {
|
|
1724
|
+
engine?.setTextTrack(selection);
|
|
1725
|
+
},
|
|
1726
|
+
getAudioTracks() {
|
|
1727
|
+
return engine?.getAudioTracks() ?? [];
|
|
1728
|
+
},
|
|
1729
|
+
getSelectedAudioTrack() {
|
|
1730
|
+
return engine?.getAudioTrackSelection() ?? 0;
|
|
1731
|
+
},
|
|
1732
|
+
setAudioTrack(index) {
|
|
1733
|
+
engine?.setAudioTrack(index);
|
|
1734
|
+
},
|
|
1497
1735
|
async enterFullscreen() {
|
|
1498
1736
|
const target = fullscreenEl ?? videoEl?.parentElement ?? videoEl;
|
|
1499
1737
|
if (target && "requestFullscreen" in target) {
|
|
@@ -1510,6 +1748,9 @@ function createVideoPlayer(options) {
|
|
|
1510
1748
|
isFullscreen() {
|
|
1511
1749
|
return Boolean(document.fullscreenElement);
|
|
1512
1750
|
},
|
|
1751
|
+
setFullscreenElement(el) {
|
|
1752
|
+
fullscreenEl = el;
|
|
1753
|
+
},
|
|
1513
1754
|
async reload() {
|
|
1514
1755
|
refreshManager?.stop();
|
|
1515
1756
|
engine?.destroy();
|
|
@@ -1518,6 +1759,8 @@ function createVideoPlayer(options) {
|
|
|
1518
1759
|
destroy() {
|
|
1519
1760
|
if (destroyed) return;
|
|
1520
1761
|
destroyed = true;
|
|
1762
|
+
mediaCleanup?.();
|
|
1763
|
+
mediaCleanup = null;
|
|
1521
1764
|
refreshManager?.stop();
|
|
1522
1765
|
refreshManager = null;
|
|
1523
1766
|
engine?.destroy();
|
|
@@ -1565,7 +1808,7 @@ function createVideoPlayer(options) {
|
|
|
1565
1808
|
engine: "hls",
|
|
1566
1809
|
hls: { ...config.streaming.hls, ...partial?.streaming?.hls },
|
|
1567
1810
|
keyProxyUrlBuilder: partial?.streaming?.keyProxyUrlBuilder ?? config.streaming.keyProxyUrlBuilder,
|
|
1568
|
-
getKeyAccessToken: partial?.streaming?.getKeyAccessToken ?? config.streaming.getKeyAccessToken
|
|
1811
|
+
getKeyAccessToken: partial?.streaming?.getKeyAccessToken ?? config.streaming.getKeyAccessToken ?? (() => authManager.getAccessToken())
|
|
1569
1812
|
},
|
|
1570
1813
|
logging: { ...config.logging, ...partial?.logging },
|
|
1571
1814
|
debug: { ...config.debug, ...partial?.debug },
|