@codenkay/video-nsgplayer-core 3.0.1 → 3.0.4
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 +23 -0
- package/dist/index.cjs +162 -94
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -22
- package/dist/index.d.ts +65 -22
- package/dist/index.js +162 -94
- package/dist/index.js.map +1 -1
- package/package.json +11 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import Hls, { LoaderStats, LoaderContext, LoaderConfiguration, LoaderCallbacks } from 'hls.js';
|
|
2
|
-
|
|
3
1
|
/** Player lifecycle states. */
|
|
4
2
|
type PlayerState = "idle" | "loading" | "ready" | "playing" | "paused" | "buffering" | "renewing" | "ended" | "error" | "destroyed";
|
|
5
3
|
type HlsSupportMode = "hls.js" | "native" | "unsupported";
|
|
@@ -89,6 +87,11 @@ type ControlsConfig = {
|
|
|
89
87
|
showLive?: boolean;
|
|
90
88
|
seekCueDurationMs?: number;
|
|
91
89
|
keyboardShortcuts?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* When true, UI chrome persists the last quality choice (by height) in
|
|
92
|
+
* localStorage and restores it for later sessions.
|
|
93
|
+
*/
|
|
94
|
+
persistQualityPreference?: boolean;
|
|
92
95
|
};
|
|
93
96
|
type ThemeConfig = {
|
|
94
97
|
playIconPreset?: PlayIconPreset;
|
|
@@ -286,7 +289,7 @@ type PlayerEventMap = {
|
|
|
286
289
|
};
|
|
287
290
|
|
|
288
291
|
declare const PLAYBACK_RATES: readonly [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
289
|
-
declare const PACKAGE_VERSION = "3.0.
|
|
292
|
+
declare const PACKAGE_VERSION = "3.0.3";
|
|
290
293
|
declare const DEFAULT_PLAYER_BEHAVIOR: Required<PlayerBehaviorConfig>;
|
|
291
294
|
declare const DEFAULT_CONTROLS: Required<ControlsConfig>;
|
|
292
295
|
declare const DEFAULT_THEME: Required<ThemeConfig>;
|
|
@@ -456,7 +459,8 @@ type HlsEngineOptions = {
|
|
|
456
459
|
* (playlists, segments, and AES `enc.key` on CDN hosts).
|
|
457
460
|
*
|
|
458
461
|
* Optional `streaming.keyProxyUrlBuilder` (same-origin BFF) or
|
|
459
|
-
* `streaming.getKeyAccessToken` (
|
|
462
|
+
* `streaming.getKeyAccessToken` (Bearer only after signed-query 401/403).
|
|
463
|
+
* CDN keys prefer signed query without Authorization (no CORS preflight).
|
|
460
464
|
* when query-signed CDN access is insufficient.
|
|
461
465
|
*/
|
|
462
466
|
declare class HlsEngine {
|
|
@@ -653,6 +657,54 @@ declare class RefreshManager {
|
|
|
653
657
|
private renew;
|
|
654
658
|
}
|
|
655
659
|
|
|
660
|
+
/**
|
|
661
|
+
* Local loader types (structurally compatible with hls.js) so the public
|
|
662
|
+
* `.d.ts` does not require a value import of optional peer `hls.js`.
|
|
663
|
+
*/
|
|
664
|
+
type HlsLoaderTiming = {
|
|
665
|
+
start: number;
|
|
666
|
+
first: number;
|
|
667
|
+
end: number;
|
|
668
|
+
};
|
|
669
|
+
type LoaderStats = {
|
|
670
|
+
aborted: boolean;
|
|
671
|
+
loaded: number;
|
|
672
|
+
retry: number;
|
|
673
|
+
total: number;
|
|
674
|
+
chunkCount: number;
|
|
675
|
+
bwEstimate: number;
|
|
676
|
+
loading: HlsLoaderTiming;
|
|
677
|
+
parsing: {
|
|
678
|
+
start: number;
|
|
679
|
+
end: number;
|
|
680
|
+
};
|
|
681
|
+
buffering: HlsLoaderTiming;
|
|
682
|
+
};
|
|
683
|
+
type LoaderContext = {
|
|
684
|
+
url: string;
|
|
685
|
+
responseType?: string;
|
|
686
|
+
rangeStart?: number;
|
|
687
|
+
rangeEnd?: number;
|
|
688
|
+
progressData?: boolean;
|
|
689
|
+
[key: string]: unknown;
|
|
690
|
+
};
|
|
691
|
+
type LoaderConfiguration = {
|
|
692
|
+
timeout?: number;
|
|
693
|
+
};
|
|
694
|
+
type LoaderCallbacks<TContext> = {
|
|
695
|
+
onSuccess: (response: {
|
|
696
|
+
url: string;
|
|
697
|
+
data: ArrayBuffer | string;
|
|
698
|
+
code?: number;
|
|
699
|
+
}, stats: LoaderStats, context: TContext, networkDetails: unknown) => void;
|
|
700
|
+
onError: (error: {
|
|
701
|
+
code: number;
|
|
702
|
+
text: string;
|
|
703
|
+
}, context: TContext, networkDetails: unknown, stats: LoaderStats) => void;
|
|
704
|
+
onTimeout?: (...args: unknown[]) => void;
|
|
705
|
+
onAbort?: (...args: unknown[]) => void;
|
|
706
|
+
onProgress?: (...args: unknown[]) => void;
|
|
707
|
+
};
|
|
656
708
|
type HlsLoaderInstance = {
|
|
657
709
|
stats: LoaderStats;
|
|
658
710
|
context: LoaderContext | null;
|
|
@@ -662,6 +714,8 @@ type HlsLoaderInstance = {
|
|
|
662
714
|
};
|
|
663
715
|
/** Constructor shape compatible with `Hls.DefaultConfig.loader`. */
|
|
664
716
|
type HlsLoaderConstructor = new (config: object) => HlsLoaderInstance;
|
|
717
|
+
/** Public alias — pass `hls.js` default export (typed as object at the boundary). */
|
|
718
|
+
type HlsModuleLike = object;
|
|
665
719
|
/** Max same-origin BFF key attempts before the session circuit opens. */
|
|
666
720
|
declare const KEY_PROXY_MAX_FAILURES = 2;
|
|
667
721
|
type AuthParamsLoaderOptions = {
|
|
@@ -673,30 +727,19 @@ type AuthParamsLoaderOptions = {
|
|
|
673
727
|
};
|
|
674
728
|
declare function isEncryptionKeyUrl(rawUrl: string, baseOrigin?: string): boolean;
|
|
675
729
|
/**
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
* custom wrapper, `extends` / nested `inner.load()` chains blow Firefox's
|
|
680
|
-
* stack (`InternalError: too much recursion`).
|
|
730
|
+
* Resolve the builtin loader from the **same** `hls.js` module instance the
|
|
731
|
+
* engine loaded. Never statically import `XhrLoader` — that can pull a second
|
|
732
|
+
* hls.js copy into the app bundle after npm pack/publish and break key loads.
|
|
681
733
|
*/
|
|
682
|
-
declare function resolveBuiltinHlsLoader(
|
|
734
|
+
declare function resolveBuiltinHlsLoader(HlsModule: HlsModuleLike): HlsLoaderConstructor;
|
|
683
735
|
/**
|
|
684
736
|
* Rewrites an HLS request URL so signed auth query params are present.
|
|
685
|
-
*
|
|
686
|
-
* AES `enc.key` handling (enterprise priority order):
|
|
687
|
-
* 1. `keyProxyUrlBuilder` — same-origin BFF (production; server IP must pass CDN/WAF)
|
|
688
|
-
* 2. Otherwise keep CDN/API URL and apply signed `token`/`expires` (and related) query
|
|
689
|
-
* 3. Optional `getKeyAccessToken` is applied at load-time (Bearer), not in this rewrite
|
|
690
737
|
*/
|
|
691
738
|
declare function rewriteHlsRequestUrl(rawUrl: string, store: AuthParamsStore, options?: AuthParamsLoaderOptions, baseOrigin?: string): string;
|
|
692
739
|
/**
|
|
693
|
-
* hls.js loader
|
|
694
|
-
* - Re-applies signed auth query params on every media request
|
|
695
|
-
* - Loads AES `enc.key` via controlled BFF (≤2) → Bearer → query XHR
|
|
696
|
-
* with a **session circuit breaker** so a dead Cloudflare BFF is not
|
|
697
|
-
* hammered by hls.js retries (which previously caused 9–10× 502s).
|
|
740
|
+
* hls.js loader: auth query rewrite + enterprise AES key loading.
|
|
698
741
|
*/
|
|
699
|
-
declare function createAuthParamsLoader(
|
|
742
|
+
declare function createAuthParamsLoader(HlsModule: HlsModuleLike, store: AuthParamsStore, options?: AuthParamsLoaderOptions): HlsLoaderConstructor;
|
|
700
743
|
|
|
701
744
|
declare function redactValue(key: string, value: unknown, seen?: WeakSet<object>, depth?: number): unknown;
|
|
702
745
|
/**
|
|
@@ -705,4 +748,4 @@ declare function redactValue(key: string, value: unknown, seen?: WeakSet<object>
|
|
|
705
748
|
*/
|
|
706
749
|
declare function redactObject(obj: Record<string, unknown>, seen?: WeakSet<object>, depth?: number): Record<string, unknown>;
|
|
707
750
|
|
|
708
|
-
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, KEY_PROXY_MAX_FAILURES, 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 };
|
|
751
|
+
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 HlsLoaderConstructor, type HlsModuleLike, type HlsQualityLevel, type HlsSupportMode, KEY_PROXY_MAX_FAILURES, 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
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import Hls, { LoaderStats, LoaderContext, LoaderConfiguration, LoaderCallbacks } from 'hls.js';
|
|
2
|
-
|
|
3
1
|
/** Player lifecycle states. */
|
|
4
2
|
type PlayerState = "idle" | "loading" | "ready" | "playing" | "paused" | "buffering" | "renewing" | "ended" | "error" | "destroyed";
|
|
5
3
|
type HlsSupportMode = "hls.js" | "native" | "unsupported";
|
|
@@ -89,6 +87,11 @@ type ControlsConfig = {
|
|
|
89
87
|
showLive?: boolean;
|
|
90
88
|
seekCueDurationMs?: number;
|
|
91
89
|
keyboardShortcuts?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* When true, UI chrome persists the last quality choice (by height) in
|
|
92
|
+
* localStorage and restores it for later sessions.
|
|
93
|
+
*/
|
|
94
|
+
persistQualityPreference?: boolean;
|
|
92
95
|
};
|
|
93
96
|
type ThemeConfig = {
|
|
94
97
|
playIconPreset?: PlayIconPreset;
|
|
@@ -286,7 +289,7 @@ type PlayerEventMap = {
|
|
|
286
289
|
};
|
|
287
290
|
|
|
288
291
|
declare const PLAYBACK_RATES: readonly [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
289
|
-
declare const PACKAGE_VERSION = "3.0.
|
|
292
|
+
declare const PACKAGE_VERSION = "3.0.3";
|
|
290
293
|
declare const DEFAULT_PLAYER_BEHAVIOR: Required<PlayerBehaviorConfig>;
|
|
291
294
|
declare const DEFAULT_CONTROLS: Required<ControlsConfig>;
|
|
292
295
|
declare const DEFAULT_THEME: Required<ThemeConfig>;
|
|
@@ -456,7 +459,8 @@ type HlsEngineOptions = {
|
|
|
456
459
|
* (playlists, segments, and AES `enc.key` on CDN hosts).
|
|
457
460
|
*
|
|
458
461
|
* Optional `streaming.keyProxyUrlBuilder` (same-origin BFF) or
|
|
459
|
-
* `streaming.getKeyAccessToken` (
|
|
462
|
+
* `streaming.getKeyAccessToken` (Bearer only after signed-query 401/403).
|
|
463
|
+
* CDN keys prefer signed query without Authorization (no CORS preflight).
|
|
460
464
|
* when query-signed CDN access is insufficient.
|
|
461
465
|
*/
|
|
462
466
|
declare class HlsEngine {
|
|
@@ -653,6 +657,54 @@ declare class RefreshManager {
|
|
|
653
657
|
private renew;
|
|
654
658
|
}
|
|
655
659
|
|
|
660
|
+
/**
|
|
661
|
+
* Local loader types (structurally compatible with hls.js) so the public
|
|
662
|
+
* `.d.ts` does not require a value import of optional peer `hls.js`.
|
|
663
|
+
*/
|
|
664
|
+
type HlsLoaderTiming = {
|
|
665
|
+
start: number;
|
|
666
|
+
first: number;
|
|
667
|
+
end: number;
|
|
668
|
+
};
|
|
669
|
+
type LoaderStats = {
|
|
670
|
+
aborted: boolean;
|
|
671
|
+
loaded: number;
|
|
672
|
+
retry: number;
|
|
673
|
+
total: number;
|
|
674
|
+
chunkCount: number;
|
|
675
|
+
bwEstimate: number;
|
|
676
|
+
loading: HlsLoaderTiming;
|
|
677
|
+
parsing: {
|
|
678
|
+
start: number;
|
|
679
|
+
end: number;
|
|
680
|
+
};
|
|
681
|
+
buffering: HlsLoaderTiming;
|
|
682
|
+
};
|
|
683
|
+
type LoaderContext = {
|
|
684
|
+
url: string;
|
|
685
|
+
responseType?: string;
|
|
686
|
+
rangeStart?: number;
|
|
687
|
+
rangeEnd?: number;
|
|
688
|
+
progressData?: boolean;
|
|
689
|
+
[key: string]: unknown;
|
|
690
|
+
};
|
|
691
|
+
type LoaderConfiguration = {
|
|
692
|
+
timeout?: number;
|
|
693
|
+
};
|
|
694
|
+
type LoaderCallbacks<TContext> = {
|
|
695
|
+
onSuccess: (response: {
|
|
696
|
+
url: string;
|
|
697
|
+
data: ArrayBuffer | string;
|
|
698
|
+
code?: number;
|
|
699
|
+
}, stats: LoaderStats, context: TContext, networkDetails: unknown) => void;
|
|
700
|
+
onError: (error: {
|
|
701
|
+
code: number;
|
|
702
|
+
text: string;
|
|
703
|
+
}, context: TContext, networkDetails: unknown, stats: LoaderStats) => void;
|
|
704
|
+
onTimeout?: (...args: unknown[]) => void;
|
|
705
|
+
onAbort?: (...args: unknown[]) => void;
|
|
706
|
+
onProgress?: (...args: unknown[]) => void;
|
|
707
|
+
};
|
|
656
708
|
type HlsLoaderInstance = {
|
|
657
709
|
stats: LoaderStats;
|
|
658
710
|
context: LoaderContext | null;
|
|
@@ -662,6 +714,8 @@ type HlsLoaderInstance = {
|
|
|
662
714
|
};
|
|
663
715
|
/** Constructor shape compatible with `Hls.DefaultConfig.loader`. */
|
|
664
716
|
type HlsLoaderConstructor = new (config: object) => HlsLoaderInstance;
|
|
717
|
+
/** Public alias — pass `hls.js` default export (typed as object at the boundary). */
|
|
718
|
+
type HlsModuleLike = object;
|
|
665
719
|
/** Max same-origin BFF key attempts before the session circuit opens. */
|
|
666
720
|
declare const KEY_PROXY_MAX_FAILURES = 2;
|
|
667
721
|
type AuthParamsLoaderOptions = {
|
|
@@ -673,30 +727,19 @@ type AuthParamsLoaderOptions = {
|
|
|
673
727
|
};
|
|
674
728
|
declare function isEncryptionKeyUrl(rawUrl: string, baseOrigin?: string): boolean;
|
|
675
729
|
/**
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
* custom wrapper, `extends` / nested `inner.load()` chains blow Firefox's
|
|
680
|
-
* stack (`InternalError: too much recursion`).
|
|
730
|
+
* Resolve the builtin loader from the **same** `hls.js` module instance the
|
|
731
|
+
* engine loaded. Never statically import `XhrLoader` — that can pull a second
|
|
732
|
+
* hls.js copy into the app bundle after npm pack/publish and break key loads.
|
|
681
733
|
*/
|
|
682
|
-
declare function resolveBuiltinHlsLoader(
|
|
734
|
+
declare function resolveBuiltinHlsLoader(HlsModule: HlsModuleLike): HlsLoaderConstructor;
|
|
683
735
|
/**
|
|
684
736
|
* Rewrites an HLS request URL so signed auth query params are present.
|
|
685
|
-
*
|
|
686
|
-
* AES `enc.key` handling (enterprise priority order):
|
|
687
|
-
* 1. `keyProxyUrlBuilder` — same-origin BFF (production; server IP must pass CDN/WAF)
|
|
688
|
-
* 2. Otherwise keep CDN/API URL and apply signed `token`/`expires` (and related) query
|
|
689
|
-
* 3. Optional `getKeyAccessToken` is applied at load-time (Bearer), not in this rewrite
|
|
690
737
|
*/
|
|
691
738
|
declare function rewriteHlsRequestUrl(rawUrl: string, store: AuthParamsStore, options?: AuthParamsLoaderOptions, baseOrigin?: string): string;
|
|
692
739
|
/**
|
|
693
|
-
* hls.js loader
|
|
694
|
-
* - Re-applies signed auth query params on every media request
|
|
695
|
-
* - Loads AES `enc.key` via controlled BFF (≤2) → Bearer → query XHR
|
|
696
|
-
* with a **session circuit breaker** so a dead Cloudflare BFF is not
|
|
697
|
-
* hammered by hls.js retries (which previously caused 9–10× 502s).
|
|
740
|
+
* hls.js loader: auth query rewrite + enterprise AES key loading.
|
|
698
741
|
*/
|
|
699
|
-
declare function createAuthParamsLoader(
|
|
742
|
+
declare function createAuthParamsLoader(HlsModule: HlsModuleLike, store: AuthParamsStore, options?: AuthParamsLoaderOptions): HlsLoaderConstructor;
|
|
700
743
|
|
|
701
744
|
declare function redactValue(key: string, value: unknown, seen?: WeakSet<object>, depth?: number): unknown;
|
|
702
745
|
/**
|
|
@@ -705,4 +748,4 @@ declare function redactValue(key: string, value: unknown, seen?: WeakSet<object>
|
|
|
705
748
|
*/
|
|
706
749
|
declare function redactObject(obj: Record<string, unknown>, seen?: WeakSet<object>, depth?: number): Record<string, unknown>;
|
|
707
750
|
|
|
708
|
-
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, KEY_PROXY_MAX_FAILURES, 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 };
|
|
751
|
+
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 HlsLoaderConstructor, type HlsModuleLike, type HlsQualityLevel, type HlsSupportMode, KEY_PROXY_MAX_FAILURES, 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 = "3.0.
|
|
149
|
+
var PACKAGE_VERSION = "3.0.3";
|
|
150
150
|
var DEFAULT_PLAYER_BEHAVIOR = {
|
|
151
151
|
autoplay: true,
|
|
152
152
|
startMuted: true,
|
|
@@ -170,7 +170,8 @@ var DEFAULT_CONTROLS = {
|
|
|
170
170
|
showSkip: true,
|
|
171
171
|
showLive: true,
|
|
172
172
|
seekCueDurationMs: 720,
|
|
173
|
-
keyboardShortcuts: true
|
|
173
|
+
keyboardShortcuts: true,
|
|
174
|
+
persistQualityPreference: false
|
|
174
175
|
};
|
|
175
176
|
var DEFAULT_THEME = {
|
|
176
177
|
playIconPreset: "triangle",
|
|
@@ -766,9 +767,12 @@ function seekToLiveEdge(media, options) {
|
|
|
766
767
|
}
|
|
767
768
|
|
|
768
769
|
// src/streaming/authParamsLoader.ts
|
|
769
|
-
import { XhrLoader as BuiltinXhrLoader } from "hls.js";
|
|
770
770
|
var ENC_KEY_PATH_RE = /enc\.key$/i;
|
|
771
771
|
var BUILTIN_LOADER_NAMES = /* @__PURE__ */ new Set(["XhrLoader", "FetchLoader"]);
|
|
772
|
+
var BUILTIN_LOADER_REF = /* @__PURE__ */ Symbol.for(
|
|
773
|
+
"@codenkay/video-nsgplayer-core/builtin-hls-loader"
|
|
774
|
+
);
|
|
775
|
+
var builtinLoaderByHls = /* @__PURE__ */ new WeakMap();
|
|
772
776
|
var KEY_PROXY_MAX_FAILURES = 2;
|
|
773
777
|
function isEncryptionKeyUrl(rawUrl, baseOrigin = typeof location !== "undefined" ? location.origin : "http://localhost") {
|
|
774
778
|
try {
|
|
@@ -777,18 +781,35 @@ function isEncryptionKeyUrl(rawUrl, baseOrigin = typeof location !== "undefined"
|
|
|
777
781
|
return /enc\.key/i.test(rawUrl);
|
|
778
782
|
}
|
|
779
783
|
}
|
|
780
|
-
function
|
|
781
|
-
|
|
782
|
-
|
|
784
|
+
function isBuiltinLoaderName(name) {
|
|
785
|
+
return BUILTIN_LOADER_NAMES.has(name);
|
|
786
|
+
}
|
|
787
|
+
function rememberBuiltin(HlsClass, loader) {
|
|
788
|
+
if (isBuiltinLoaderName(typeof loader.name === "string" ? loader.name : "")) {
|
|
789
|
+
builtinLoaderByHls.set(HlsClass, loader);
|
|
783
790
|
}
|
|
791
|
+
return loader;
|
|
792
|
+
}
|
|
793
|
+
function getStashedBuiltinLoader(loader) {
|
|
794
|
+
return loader[BUILTIN_LOADER_REF];
|
|
795
|
+
}
|
|
796
|
+
function resolveBuiltinHlsLoader(HlsModule) {
|
|
797
|
+
const HlsClass = HlsModule;
|
|
798
|
+
const cached = builtinLoaderByHls.get(HlsClass);
|
|
799
|
+
if (cached) return cached;
|
|
784
800
|
const start = HlsClass.DefaultConfig.loader;
|
|
785
801
|
let current = start;
|
|
786
802
|
const seen = /* @__PURE__ */ new Set();
|
|
787
803
|
while (current && !seen.has(current)) {
|
|
788
804
|
seen.add(current);
|
|
789
805
|
const name = typeof current.name === "string" ? current.name : "";
|
|
790
|
-
if (
|
|
791
|
-
return current;
|
|
806
|
+
if (isBuiltinLoaderName(name)) {
|
|
807
|
+
return rememberBuiltin(HlsClass, current);
|
|
808
|
+
}
|
|
809
|
+
const stashed = getStashedBuiltinLoader(current);
|
|
810
|
+
if (stashed) {
|
|
811
|
+
current = stashed;
|
|
812
|
+
continue;
|
|
792
813
|
}
|
|
793
814
|
const parentCtor = Object.getPrototypeOf(current.prototype)?.constructor;
|
|
794
815
|
if (!parentCtor || parentCtor === Object || parentCtor === Function) {
|
|
@@ -796,7 +817,8 @@ function resolveBuiltinHlsLoader(HlsClass) {
|
|
|
796
817
|
}
|
|
797
818
|
current = parentCtor;
|
|
798
819
|
}
|
|
799
|
-
|
|
820
|
+
const fallback = builtinLoaderByHls.get(HlsClass);
|
|
821
|
+
return fallback ?? start;
|
|
800
822
|
}
|
|
801
823
|
function rewriteHlsRequestUrl(rawUrl, store, options = {}, baseOrigin = typeof location !== "undefined" ? location.origin : "http://localhost") {
|
|
802
824
|
if (!rawUrl) return rawUrl;
|
|
@@ -844,67 +866,82 @@ function isProxyUpstreamBlocked(status, contentType, bodyHead, errorText) {
|
|
|
844
866
|
}
|
|
845
867
|
return false;
|
|
846
868
|
}
|
|
847
|
-
async function
|
|
848
|
-
const
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
869
|
+
async function fetchEncryptionKeyBytes(url, accessToken, signal) {
|
|
870
|
+
const headers = accessToken ? { Authorization: `Bearer ${accessToken}`, Accept: "*/*" } : void 0;
|
|
871
|
+
try {
|
|
872
|
+
const response = await fetch(url, {
|
|
873
|
+
method: "GET",
|
|
874
|
+
...headers ? { headers } : {},
|
|
875
|
+
credentials: "omit",
|
|
876
|
+
cache: "no-store",
|
|
877
|
+
signal
|
|
878
|
+
});
|
|
879
|
+
if (!response.ok) {
|
|
880
|
+
return {
|
|
881
|
+
ok: false,
|
|
882
|
+
status: response.status,
|
|
883
|
+
text: response.statusText || `enc.key HTTP ${response.status}`,
|
|
884
|
+
cors: false,
|
|
885
|
+
response
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
const data = await response.arrayBuffer();
|
|
889
|
+
const head = new TextDecoder().decode(data.slice(0, 32));
|
|
890
|
+
if (/<!DOCTYPE|<html/i.test(head)) {
|
|
891
|
+
return {
|
|
892
|
+
ok: false,
|
|
893
|
+
status: 403,
|
|
894
|
+
text: "enc.key returned HTML (CDN challenge/block), not an AES key",
|
|
895
|
+
cors: false,
|
|
896
|
+
response
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
return {
|
|
900
|
+
ok: true,
|
|
901
|
+
data,
|
|
871
902
|
response,
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
903
|
+
url: response.url || url
|
|
904
|
+
};
|
|
905
|
+
} catch (error) {
|
|
906
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
907
|
+
return {
|
|
908
|
+
ok: false,
|
|
909
|
+
status: 0,
|
|
910
|
+
text: "enc.key fetch aborted/timeout",
|
|
911
|
+
cors: false,
|
|
912
|
+
response: null
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
const cors = error instanceof TypeError || error instanceof Error && /Failed to fetch|NetworkError|CORS/i.test(error.message);
|
|
916
|
+
return {
|
|
917
|
+
ok: false,
|
|
918
|
+
status: 0,
|
|
919
|
+
text: error instanceof Error ? error.message : "enc.key fetch failed",
|
|
920
|
+
cors,
|
|
921
|
+
response: null
|
|
922
|
+
};
|
|
875
923
|
}
|
|
876
|
-
|
|
924
|
+
}
|
|
925
|
+
function reportKeySuccess(context, callbacks, result) {
|
|
926
|
+
const stats = createLoaderStats();
|
|
877
927
|
const end = performance.now();
|
|
928
|
+
stats.loading.first = stats.loading.start;
|
|
878
929
|
stats.loading.end = end;
|
|
879
|
-
stats.loaded = data.byteLength;
|
|
880
|
-
stats.total = data.byteLength;
|
|
881
|
-
const head = new TextDecoder().decode(data.slice(0, 32));
|
|
882
|
-
if (/<!DOCTYPE|<html/i.test(head)) {
|
|
883
|
-
callbacks.onError(
|
|
884
|
-
{
|
|
885
|
-
code: 403,
|
|
886
|
-
text: "enc.key returned HTML (CDN challenge/block), not an AES key"
|
|
887
|
-
},
|
|
888
|
-
context,
|
|
889
|
-
response,
|
|
890
|
-
stats
|
|
891
|
-
);
|
|
892
|
-
return;
|
|
893
|
-
}
|
|
930
|
+
stats.loaded = result.data.byteLength;
|
|
931
|
+
stats.total = result.data.byteLength;
|
|
894
932
|
callbacks.onSuccess(
|
|
895
933
|
{
|
|
896
|
-
url:
|
|
897
|
-
data,
|
|
898
|
-
code: response.status
|
|
934
|
+
url: result.url,
|
|
935
|
+
data: result.data,
|
|
936
|
+
code: result.response.status
|
|
899
937
|
},
|
|
900
938
|
stats,
|
|
901
939
|
context,
|
|
902
|
-
response
|
|
940
|
+
result.response
|
|
903
941
|
);
|
|
904
942
|
}
|
|
905
943
|
async function loadEncryptionKeyEnterprise(context, callbacks, plan, signal, state) {
|
|
906
944
|
const stats = createLoaderStats();
|
|
907
|
-
stats.loading.first = performance.now();
|
|
908
945
|
const canTryProxy = Boolean(plan.proxyUrl) && !state.circuitOpen && state.proxyFailures < plan.maxProxyFailures;
|
|
909
946
|
if (canTryProxy && plan.proxyUrl) {
|
|
910
947
|
while (!state.circuitOpen && state.proxyFailures < plan.maxProxyFailures && !signal.aborted) {
|
|
@@ -975,47 +1012,71 @@ async function loadEncryptionKeyEnterprise(context, callbacks, plan, signal, sta
|
|
|
975
1012
|
}
|
|
976
1013
|
state.circuitOpen = true;
|
|
977
1014
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1015
|
+
const queryResult = await fetchEncryptionKeyBytes(
|
|
1016
|
+
plan.signedKeyUrl,
|
|
1017
|
+
null,
|
|
1018
|
+
signal
|
|
1019
|
+
);
|
|
1020
|
+
if (queryResult.ok) {
|
|
1021
|
+
reportKeySuccess(
|
|
1022
|
+
{ ...context, url: plan.signedKeyUrl },
|
|
1023
|
+
callbacks,
|
|
1024
|
+
queryResult
|
|
1025
|
+
);
|
|
1026
|
+
return "ok";
|
|
1027
|
+
}
|
|
1028
|
+
const needsBearer = Boolean(plan.accessToken) && (queryResult.status === 401 || queryResult.status === 403 || // Browser-direct: opaque CORS/network on unsigned GET — try Bearer once
|
|
1029
|
+
// Appextech allows ACAO + Authorization (AUTH_MODE=browser, no BFF).
|
|
1030
|
+
queryResult.cors || queryResult.status === 0);
|
|
1031
|
+
if (needsBearer && plan.accessToken) {
|
|
1032
|
+
const bearerResult = await fetchEncryptionKeyBytes(
|
|
1033
|
+
plan.signedKeyUrl,
|
|
1034
|
+
plan.accessToken,
|
|
1035
|
+
signal
|
|
1036
|
+
);
|
|
1037
|
+
if (bearerResult.ok) {
|
|
1038
|
+
reportKeySuccess(
|
|
981
1039
|
{ ...context, url: plan.signedKeyUrl },
|
|
982
1040
|
callbacks,
|
|
983
|
-
|
|
984
|
-
signal
|
|
985
|
-
);
|
|
986
|
-
return "ok";
|
|
987
|
-
} catch (error) {
|
|
988
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
989
|
-
callbacks.onError(
|
|
990
|
-
{ code: 0, text: "enc.key fetch aborted/timeout" },
|
|
991
|
-
{ ...context, url: plan.signedKeyUrl },
|
|
992
|
-
null,
|
|
993
|
-
stats
|
|
994
|
-
);
|
|
995
|
-
return "ok";
|
|
996
|
-
}
|
|
997
|
-
if (error instanceof TypeError || error instanceof Error && /Failed to fetch|NetworkError|CORS/i.test(error.message)) {
|
|
998
|
-
return "try_xhr";
|
|
999
|
-
}
|
|
1000
|
-
callbacks.onError(
|
|
1001
|
-
{
|
|
1002
|
-
code: 0,
|
|
1003
|
-
text: error instanceof Error ? error.message : "enc.key Bearer fetch failed"
|
|
1004
|
-
},
|
|
1005
|
-
{ ...context, url: plan.signedKeyUrl },
|
|
1006
|
-
null,
|
|
1007
|
-
stats
|
|
1041
|
+
bearerResult
|
|
1008
1042
|
);
|
|
1009
1043
|
return "ok";
|
|
1010
1044
|
}
|
|
1045
|
+
if (bearerResult.cors || bearerResult.status === 0) {
|
|
1046
|
+
return "try_xhr";
|
|
1047
|
+
}
|
|
1048
|
+
callbacks.onError(
|
|
1049
|
+
{
|
|
1050
|
+
code: bearerResult.status,
|
|
1051
|
+
text: bearerResult.text
|
|
1052
|
+
},
|
|
1053
|
+
{ ...context, url: plan.signedKeyUrl },
|
|
1054
|
+
bearerResult.response,
|
|
1055
|
+
stats
|
|
1056
|
+
);
|
|
1057
|
+
return "ok";
|
|
1058
|
+
}
|
|
1059
|
+
if (queryResult.cors || queryResult.status === 0) {
|
|
1060
|
+
return "try_xhr";
|
|
1011
1061
|
}
|
|
1012
|
-
|
|
1062
|
+
callbacks.onError(
|
|
1063
|
+
{
|
|
1064
|
+
code: queryResult.status,
|
|
1065
|
+
text: queryResult.text
|
|
1066
|
+
},
|
|
1067
|
+
{ ...context, url: plan.signedKeyUrl },
|
|
1068
|
+
queryResult.response,
|
|
1069
|
+
stats
|
|
1070
|
+
);
|
|
1071
|
+
return "ok";
|
|
1013
1072
|
}
|
|
1014
|
-
function createAuthParamsLoader(
|
|
1073
|
+
function createAuthParamsLoader(HlsModule, store, options = {}) {
|
|
1074
|
+
const HlsClass = HlsModule;
|
|
1015
1075
|
const BaseLoader = resolveBuiltinHlsLoader(HlsClass);
|
|
1076
|
+
rememberBuiltin(HlsClass, BaseLoader);
|
|
1016
1077
|
const maxProxyFailures = typeof options.keyProxyMaxFailures === "number" && options.keyProxyMaxFailures >= 0 ? options.keyProxyMaxFailures : KEY_PROXY_MAX_FAILURES;
|
|
1017
1078
|
const keyProxyState = { proxyFailures: 0, circuitOpen: false };
|
|
1018
|
-
|
|
1079
|
+
class AuthParamsLoader {
|
|
1019
1080
|
constructor(config) {
|
|
1020
1081
|
this.context = null;
|
|
1021
1082
|
this.keyAbort = null;
|
|
@@ -1099,7 +1160,13 @@ function createAuthParamsLoader(HlsClass, store, options = {}) {
|
|
|
1099
1160
|
}
|
|
1100
1161
|
this.inner.load(context, config, callbacks);
|
|
1101
1162
|
}
|
|
1102
|
-
}
|
|
1163
|
+
}
|
|
1164
|
+
Object.defineProperty(AuthParamsLoader, BUILTIN_LOADER_REF, {
|
|
1165
|
+
value: BaseLoader,
|
|
1166
|
+
enumerable: false,
|
|
1167
|
+
configurable: false
|
|
1168
|
+
});
|
|
1169
|
+
return AuthParamsLoader;
|
|
1103
1170
|
}
|
|
1104
1171
|
|
|
1105
1172
|
// src/streaming/HlsEngine.ts
|
|
@@ -1214,7 +1281,7 @@ var HlsEngine = class {
|
|
|
1214
1281
|
if (!data.fatal) return;
|
|
1215
1282
|
} else if (!data.fatal) {
|
|
1216
1283
|
callbacks.onFatalError?.(
|
|
1217
|
-
status ? `Encryption key load failed (HTTP ${status}).` : "Encryption key load failed (network/CORS).
|
|
1284
|
+
status ? `Encryption key load failed (HTTP ${status}).` : "Encryption key load failed (network/CORS). For browser-direct mode the key host must allow CORS (and Authorization if Bearer is required). Otherwise use keyProxyUrlBuilder (same-origin BFF) when the server IP can reach Appextech."
|
|
1218
1285
|
);
|
|
1219
1286
|
return;
|
|
1220
1287
|
}
|
|
@@ -1589,10 +1656,11 @@ function createVideoPlayer(options) {
|
|
|
1589
1656
|
volume: config.player.volume,
|
|
1590
1657
|
videoId: options.videoId,
|
|
1591
1658
|
keyProxyUrlBuilder: config.streaming.keyProxyUrlBuilder,
|
|
1592
|
-
// Enterprise AES key auth:
|
|
1593
|
-
// 1) keyProxyUrlBuilder
|
|
1594
|
-
// 2)
|
|
1595
|
-
// 3)
|
|
1659
|
+
// Enterprise AES key auth (see authParamsLoader):
|
|
1660
|
+
// 1) keyProxyUrlBuilder BFF — max 2 failures then circuit
|
|
1661
|
+
// 2) signed-query fetch (no Bearer / no CORS preflight)
|
|
1662
|
+
// 3) Bearer only on HTTP 401/403 when getKeyAccessToken is set
|
|
1663
|
+
// 4) signed-query XHR fallback if fetch is CORS-blocked
|
|
1596
1664
|
getKeyAccessToken: config.streaming.getKeyAccessToken ?? (() => authManager.getAccessToken()),
|
|
1597
1665
|
keyProxyMaxFailures: config.streaming.keyProxyMaxFailures,
|
|
1598
1666
|
hlsConfig: config.streaming.hls,
|