@codenkay/video-nsgplayer-core 0.1.5 → 1.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 +37 -11
- package/dist/index.cjs +207 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -14
- package/dist/index.d.ts +37 -14
- package/dist/index.js +205 -43
- package/dist/index.js.map +1 -1
- package/package.json +14 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Hls, { LoaderContext, LoaderConfiguration, LoaderCallbacks } from 'hls.js';
|
|
1
|
+
import Hls, { LoaderStats, LoaderContext, LoaderConfiguration, LoaderCallbacks } from 'hls.js';
|
|
2
2
|
|
|
3
3
|
/** Player lifecycle states. */
|
|
4
4
|
type PlayerState = "idle" | "loading" | "ready" | "playing" | "paused" | "buffering" | "renewing" | "ended" | "error" | "destroyed";
|
|
@@ -255,7 +255,7 @@ type PlayerEventMap = {
|
|
|
255
255
|
};
|
|
256
256
|
|
|
257
257
|
declare const PLAYBACK_RATES: readonly [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
258
|
-
declare const PACKAGE_VERSION = "0.1.
|
|
258
|
+
declare const PACKAGE_VERSION = "0.1.8";
|
|
259
259
|
declare const DEFAULT_PLAYER_BEHAVIOR: Required<PlayerBehaviorConfig>;
|
|
260
260
|
declare const DEFAULT_CONTROLS: Required<ControlsConfig>;
|
|
261
261
|
declare const DEFAULT_THEME: Required<ThemeConfig>;
|
|
@@ -415,9 +415,12 @@ type HlsEngineOptions = {
|
|
|
415
415
|
/**
|
|
416
416
|
* Dual-path HLS engine: hls.js when supported, otherwise native Safari HLS.
|
|
417
417
|
*
|
|
418
|
-
* Signed CDN playback requires auth query params on every HLS request
|
|
419
|
-
*
|
|
420
|
-
*
|
|
418
|
+
* Signed CDN playback requires auth query params on every HLS request
|
|
419
|
+
* (playlists, segments, and AES `enc.key` on CDN hosts).
|
|
420
|
+
*
|
|
421
|
+
* Optional `streaming.keyProxyUrlBuilder` (same-origin BFF) or
|
|
422
|
+
* `streaming.getKeyAccessToken` (browser Bearer) for Video API–origin keys
|
|
423
|
+
* when query-signed CDN access is insufficient.
|
|
421
424
|
*/
|
|
422
425
|
declare class HlsEngine {
|
|
423
426
|
private readonly options;
|
|
@@ -591,32 +594,52 @@ declare class RefreshManager {
|
|
|
591
594
|
private renew;
|
|
592
595
|
}
|
|
593
596
|
|
|
594
|
-
type
|
|
597
|
+
type HlsLoaderInstance = {
|
|
598
|
+
stats: LoaderStats;
|
|
595
599
|
load(context: LoaderContext, config: LoaderConfiguration, callbacks: LoaderCallbacks<LoaderContext>): void;
|
|
596
600
|
destroy(): void;
|
|
601
|
+
abort?: () => void;
|
|
597
602
|
};
|
|
603
|
+
type HlsLoader = new (config: object) => HlsLoaderInstance;
|
|
598
604
|
type AuthParamsLoaderOptions = {
|
|
599
605
|
videoId?: string;
|
|
600
606
|
keyProxyUrlBuilder?: StreamingConfig["keyProxyUrlBuilder"];
|
|
601
607
|
getKeyAccessToken?: StreamingConfig["getKeyAccessToken"];
|
|
602
608
|
};
|
|
609
|
+
declare function isEncryptionKeyUrl(rawUrl: string, baseOrigin?: string): boolean;
|
|
610
|
+
/**
|
|
611
|
+
* Always resolve a builtin hls.js loader (XhrLoader).
|
|
612
|
+
*
|
|
613
|
+
* Never trust `Hls.DefaultConfig.loader` alone — if it was reassigned to a
|
|
614
|
+
* custom wrapper, `extends` / nested `inner.load()` chains blow Firefox's
|
|
615
|
+
* stack (`InternalError: too much recursion`).
|
|
616
|
+
*/
|
|
617
|
+
declare function resolveBuiltinHlsLoader(HlsClass: typeof Hls): HlsLoader;
|
|
603
618
|
/**
|
|
604
619
|
* Rewrites an HLS request URL so signed auth query params are present.
|
|
605
|
-
* Master playlists are signed on loadSource; child playlists / segments / keys
|
|
606
|
-
* are relative and would otherwise be fetched unsigned → CDN 403.
|
|
607
620
|
*
|
|
608
|
-
* AES `enc.key` handling (
|
|
609
|
-
* 1. `keyProxyUrlBuilder` — same-origin BFF (
|
|
610
|
-
* 2.
|
|
621
|
+
* AES `enc.key` handling (enterprise priority order):
|
|
622
|
+
* 1. `keyProxyUrlBuilder` — same-origin BFF (production; server IP must pass CDN/WAF)
|
|
623
|
+
* 2. Otherwise keep CDN/API URL and apply signed `token`/`expires` (and related) query
|
|
624
|
+
* params — same auth model as segments on Appextech CDN hosts
|
|
625
|
+
* 3. Optional `getKeyAccessToken` is applied at load-time (Bearer), not in this rewrite
|
|
611
626
|
*/
|
|
612
627
|
declare function rewriteHlsRequestUrl(rawUrl: string, store: AuthParamsStore, options?: AuthParamsLoaderOptions, baseOrigin?: string): string;
|
|
613
628
|
/**
|
|
614
|
-
* hls.js loader that
|
|
615
|
-
*
|
|
629
|
+
* hls.js loader that:
|
|
630
|
+
* - Re-applies signed auth query params on every media request
|
|
631
|
+
* - Loads AES `enc.key` via BFF / optional Bearer fetch / signed-query XHR
|
|
632
|
+
*
|
|
633
|
+
* Uses composition (not `extends DefaultConfig.loader`) so repeated mounts,
|
|
634
|
+
* HMR, or a polluted DefaultConfig cannot nest `super.load()` into recursion.
|
|
616
635
|
*/
|
|
617
636
|
declare function createAuthParamsLoader(HlsClass: typeof Hls, store: AuthParamsStore, options?: AuthParamsLoaderOptions): HlsLoader;
|
|
618
637
|
|
|
619
638
|
declare function redactValue(key: string, value: unknown, seen?: WeakSet<object>, depth?: number): unknown;
|
|
639
|
+
/**
|
|
640
|
+
* Redacts sensitive fields. Cycle-safe and depth-capped.
|
|
641
|
+
* Only walks plain objects/arrays — never DOM / host graphs.
|
|
642
|
+
*/
|
|
620
643
|
declare function redactObject(obj: Record<string, unknown>, seen?: WeakSet<object>, depth?: number): Record<string, unknown>;
|
|
621
644
|
|
|
622
|
-
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, isPlainObject, mergePlayerConfig, redactObject, redactSensitive, redactValue, rewriteHlsRequestUrl, seekToLiveEdge, themeToCssVars };
|
|
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 };
|
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 = "0.1.8";
|
|
150
150
|
var DEFAULT_PLAYER_BEHAVIOR = {
|
|
151
151
|
autoplay: true,
|
|
152
152
|
startMuted: true,
|
|
@@ -406,23 +406,25 @@ function isPlainObject2(value) {
|
|
|
406
406
|
const proto = Object.getPrototypeOf(value);
|
|
407
407
|
return proto === Object.prototype || proto === null;
|
|
408
408
|
}
|
|
409
|
-
function
|
|
410
|
-
if (
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
return url.toString();
|
|
421
|
-
} catch {
|
|
422
|
-
return "[REDACTED_URL]";
|
|
409
|
+
function redactStringUrl(value) {
|
|
410
|
+
if (!/^https?:\/\//i.test(value) || !/[?&](token|expires)=/i.test(value)) {
|
|
411
|
+
return value;
|
|
412
|
+
}
|
|
413
|
+
try {
|
|
414
|
+
const url = new URL(value);
|
|
415
|
+
for (const param of ["token", "expires", "signature", "sig"]) {
|
|
416
|
+
if (url.searchParams.has(param)) {
|
|
417
|
+
url.searchParams.set(param, "[REDACTED]");
|
|
423
418
|
}
|
|
424
419
|
}
|
|
420
|
+
return url.toString();
|
|
421
|
+
} catch {
|
|
422
|
+
return "[REDACTED_URL]";
|
|
425
423
|
}
|
|
424
|
+
}
|
|
425
|
+
function redactValue(key, value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
|
|
426
|
+
if (SENSITIVE_KEYS.test(key)) return "[REDACTED]";
|
|
427
|
+
if (typeof value === "string") return redactStringUrl(value);
|
|
426
428
|
if (Array.isArray(value)) {
|
|
427
429
|
if (depth >= REDACT_MAX_DEPTH || seen.has(value)) return "[REDACTED_CYCLE]";
|
|
428
430
|
seen.add(value);
|
|
@@ -689,10 +691,11 @@ var RefreshManager = class {
|
|
|
689
691
|
this.options.logger.debug("Playback source renewed", { reason });
|
|
690
692
|
} catch (error) {
|
|
691
693
|
this.consecutiveFailures += 1;
|
|
692
|
-
const message = error
|
|
694
|
+
const message = refreshErrorMessage(error);
|
|
693
695
|
this.options.logger.warn("Playback refresh failed", {
|
|
694
696
|
reason,
|
|
695
|
-
failure: this.consecutiveFailures
|
|
697
|
+
failure: this.consecutiveFailures,
|
|
698
|
+
message
|
|
696
699
|
});
|
|
697
700
|
if (this.consecutiveFailures >= this.options.renewMaxRetries) {
|
|
698
701
|
this.options.onFatal?.(message);
|
|
@@ -712,6 +715,13 @@ var RefreshManager = class {
|
|
|
712
715
|
}
|
|
713
716
|
}
|
|
714
717
|
};
|
|
718
|
+
function refreshErrorMessage(error) {
|
|
719
|
+
if (error instanceof Error && error.message) return error.message;
|
|
720
|
+
if (error && typeof error === "object" && "message" in error && typeof error.message === "string" && error.message) {
|
|
721
|
+
return error.message;
|
|
722
|
+
}
|
|
723
|
+
return "Playback refresh failed";
|
|
724
|
+
}
|
|
715
725
|
|
|
716
726
|
// src/playback/liveEdge.ts
|
|
717
727
|
var DEFAULT_LIVE_EDGE_TOLERANCE_SECONDS = 3;
|
|
@@ -755,7 +765,9 @@ function seekToLiveEdge(media, options) {
|
|
|
755
765
|
}
|
|
756
766
|
|
|
757
767
|
// src/streaming/authParamsLoader.ts
|
|
768
|
+
import { XhrLoader as BuiltinXhrLoader } from "hls.js";
|
|
758
769
|
var ENC_KEY_PATH_RE = /enc\.key$/i;
|
|
770
|
+
var BUILTIN_LOADER_NAMES = /* @__PURE__ */ new Set(["XhrLoader", "FetchLoader"]);
|
|
759
771
|
function isEncryptionKeyUrl(rawUrl, baseOrigin = typeof location !== "undefined" ? location.origin : "http://localhost") {
|
|
760
772
|
try {
|
|
761
773
|
return ENC_KEY_PATH_RE.test(new URL(rawUrl, baseOrigin).pathname);
|
|
@@ -763,6 +775,27 @@ function isEncryptionKeyUrl(rawUrl, baseOrigin = typeof location !== "undefined"
|
|
|
763
775
|
return /enc\.key/i.test(rawUrl);
|
|
764
776
|
}
|
|
765
777
|
}
|
|
778
|
+
function resolveBuiltinHlsLoader(HlsClass) {
|
|
779
|
+
if (typeof BuiltinXhrLoader === "function") {
|
|
780
|
+
return BuiltinXhrLoader;
|
|
781
|
+
}
|
|
782
|
+
const start = HlsClass.DefaultConfig.loader;
|
|
783
|
+
let current = start;
|
|
784
|
+
const seen = /* @__PURE__ */ new Set();
|
|
785
|
+
while (current && !seen.has(current)) {
|
|
786
|
+
seen.add(current);
|
|
787
|
+
const name = typeof current.name === "string" ? current.name : "";
|
|
788
|
+
if (BUILTIN_LOADER_NAMES.has(name)) {
|
|
789
|
+
return current;
|
|
790
|
+
}
|
|
791
|
+
const parentCtor = Object.getPrototypeOf(current.prototype)?.constructor;
|
|
792
|
+
if (!parentCtor || parentCtor === Object || parentCtor === Function) {
|
|
793
|
+
break;
|
|
794
|
+
}
|
|
795
|
+
current = parentCtor;
|
|
796
|
+
}
|
|
797
|
+
return start;
|
|
798
|
+
}
|
|
766
799
|
function rewriteHlsRequestUrl(rawUrl, store, options = {}, baseOrigin = typeof location !== "undefined" ? location.origin : "http://localhost") {
|
|
767
800
|
if (!rawUrl) return rawUrl;
|
|
768
801
|
const parsed = new URL(rawUrl, baseOrigin);
|
|
@@ -781,28 +814,145 @@ function rewriteHlsRequestUrl(rawUrl, store, options = {}, baseOrigin = typeof l
|
|
|
781
814
|
if (!store.hasParams()) return parsed.toString();
|
|
782
815
|
return store.applyToUrl(rawUrl, baseOrigin);
|
|
783
816
|
}
|
|
817
|
+
function createLoaderStats() {
|
|
818
|
+
const now = performance.now();
|
|
819
|
+
return {
|
|
820
|
+
aborted: false,
|
|
821
|
+
loaded: 0,
|
|
822
|
+
retry: 0,
|
|
823
|
+
total: 0,
|
|
824
|
+
chunkCount: 0,
|
|
825
|
+
bwEstimate: 0,
|
|
826
|
+
loading: { start: now, first: 0, end: 0 },
|
|
827
|
+
parsing: { start: 0, end: 0 },
|
|
828
|
+
buffering: { start: 0, first: 0, end: 0 }
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
async function loadEncryptionKeyWithFetch(context, callbacks, accessToken, signal) {
|
|
832
|
+
const stats = createLoaderStats();
|
|
833
|
+
stats.loading.first = performance.now();
|
|
834
|
+
const headers = {
|
|
835
|
+
Accept: "*/*",
|
|
836
|
+
...context.headers
|
|
837
|
+
};
|
|
838
|
+
if (accessToken) {
|
|
839
|
+
headers.Authorization = `Bearer ${accessToken}`;
|
|
840
|
+
}
|
|
841
|
+
const response = await fetch(context.url, {
|
|
842
|
+
method: "GET",
|
|
843
|
+
headers,
|
|
844
|
+
credentials: "omit",
|
|
845
|
+
cache: "no-store",
|
|
846
|
+
signal
|
|
847
|
+
});
|
|
848
|
+
if (!response.ok) {
|
|
849
|
+
callbacks.onError(
|
|
850
|
+
{
|
|
851
|
+
code: response.status,
|
|
852
|
+
text: response.statusText || `enc.key HTTP ${response.status}`
|
|
853
|
+
},
|
|
854
|
+
context,
|
|
855
|
+
response,
|
|
856
|
+
stats
|
|
857
|
+
);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
const data = await response.arrayBuffer();
|
|
861
|
+
const end = performance.now();
|
|
862
|
+
stats.loading.end = end;
|
|
863
|
+
stats.loaded = data.byteLength;
|
|
864
|
+
stats.total = data.byteLength;
|
|
865
|
+
const head = new TextDecoder().decode(data.slice(0, 32));
|
|
866
|
+
if (/<!DOCTYPE|<html/i.test(head)) {
|
|
867
|
+
callbacks.onError(
|
|
868
|
+
{
|
|
869
|
+
code: 403,
|
|
870
|
+
text: "enc.key returned HTML (CDN challenge/block), not an AES key"
|
|
871
|
+
},
|
|
872
|
+
context,
|
|
873
|
+
response,
|
|
874
|
+
stats
|
|
875
|
+
);
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
callbacks.onSuccess(
|
|
879
|
+
{
|
|
880
|
+
url: response.url || context.url,
|
|
881
|
+
data,
|
|
882
|
+
code: response.status
|
|
883
|
+
},
|
|
884
|
+
stats,
|
|
885
|
+
context,
|
|
886
|
+
response
|
|
887
|
+
);
|
|
888
|
+
}
|
|
784
889
|
function createAuthParamsLoader(HlsClass, store, options = {}) {
|
|
785
|
-
const BaseLoader = HlsClass
|
|
786
|
-
return class AuthParamsLoader
|
|
890
|
+
const BaseLoader = resolveBuiltinHlsLoader(HlsClass);
|
|
891
|
+
return class AuthParamsLoader {
|
|
892
|
+
constructor(config) {
|
|
893
|
+
this.keyAbort = null;
|
|
894
|
+
this.inner = new BaseLoader(config);
|
|
895
|
+
this.stats = this.inner.stats;
|
|
896
|
+
}
|
|
897
|
+
abort() {
|
|
898
|
+
this.keyAbort?.abort();
|
|
899
|
+
this.keyAbort = null;
|
|
900
|
+
this.inner.abort?.();
|
|
901
|
+
}
|
|
902
|
+
destroy() {
|
|
903
|
+
this.keyAbort?.abort();
|
|
904
|
+
this.keyAbort = null;
|
|
905
|
+
this.inner.destroy();
|
|
906
|
+
}
|
|
787
907
|
load(context, config, callbacks) {
|
|
788
|
-
if (context.url) {
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
908
|
+
if (!context.url) {
|
|
909
|
+
this.inner.load(context, config, callbacks);
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
try {
|
|
913
|
+
const originalUrl = context.url;
|
|
914
|
+
const isKey = isEncryptionKeyUrl(originalUrl);
|
|
915
|
+
const usingKeyProxy = Boolean(
|
|
916
|
+
isKey && options.keyProxyUrlBuilder && options.videoId
|
|
917
|
+
);
|
|
918
|
+
context.url = rewriteHlsRequestUrl(originalUrl, store, options);
|
|
919
|
+
if (usingKeyProxy) {
|
|
920
|
+
this.inner.load(context, config, callbacks);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
if (isKey && typeof options.getKeyAccessToken === "function") {
|
|
924
|
+
const accessToken = options.getKeyAccessToken() ?? null;
|
|
925
|
+
if (accessToken) {
|
|
926
|
+
this.keyAbort?.abort();
|
|
927
|
+
this.keyAbort = new AbortController();
|
|
928
|
+
const timeoutMs = typeof config?.timeout === "number" && config.timeout > 0 ? config.timeout : 2e4;
|
|
929
|
+
const timer = setTimeout(() => this.keyAbort?.abort(), timeoutMs);
|
|
930
|
+
void loadEncryptionKeyWithFetch(
|
|
931
|
+
context,
|
|
932
|
+
callbacks,
|
|
933
|
+
accessToken,
|
|
934
|
+
this.keyAbort.signal
|
|
935
|
+
).catch((error) => {
|
|
936
|
+
const stats = createLoaderStats();
|
|
937
|
+
const aborted = error instanceof DOMException && error.name === "AbortError";
|
|
938
|
+
callbacks.onError(
|
|
939
|
+
{
|
|
940
|
+
code: 0,
|
|
941
|
+
text: aborted ? "enc.key fetch aborted/timeout" : error instanceof Error ? error.message : "enc.key fetch failed"
|
|
942
|
+
},
|
|
943
|
+
context,
|
|
944
|
+
null,
|
|
945
|
+
stats
|
|
946
|
+
);
|
|
947
|
+
}).finally(() => {
|
|
948
|
+
clearTimeout(timer);
|
|
949
|
+
});
|
|
950
|
+
return;
|
|
801
951
|
}
|
|
802
|
-
} catch {
|
|
803
952
|
}
|
|
953
|
+
} catch {
|
|
804
954
|
}
|
|
805
|
-
|
|
955
|
+
this.inner.load(context, config, callbacks);
|
|
806
956
|
}
|
|
807
957
|
};
|
|
808
958
|
}
|
|
@@ -893,19 +1043,24 @@ var HlsEngine = class {
|
|
|
893
1043
|
}
|
|
894
1044
|
handleHlsError(Hls, data) {
|
|
895
1045
|
if (this.destroyed) return;
|
|
896
|
-
const { callbacks, hlsConfig
|
|
1046
|
+
const { callbacks, hlsConfig } = this.options;
|
|
897
1047
|
const status = data.response?.code;
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
1048
|
+
const authFailure = isAuthHttpStatus(status);
|
|
1049
|
+
if (data.details === Hls.ErrorDetails.KEY_LOAD_ERROR) {
|
|
1050
|
+
if (authFailure) {
|
|
1051
|
+
callbacks.onAuthRecoveryNeeded?.("key_error");
|
|
1052
|
+
if (!data.fatal) return;
|
|
1053
|
+
} else if (!data.fatal) {
|
|
1054
|
+
callbacks.onFatalError?.(
|
|
1055
|
+
status ? `Encryption key load failed (HTTP ${status}).` : "Encryption key load failed (network/CORS). Ensure signed query params are applied, or use keyProxyUrlBuilder / getKeyAccessToken when the key host requires API Bearer."
|
|
1056
|
+
);
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
903
1059
|
}
|
|
904
|
-
const authFailure = isAuthHttpStatus(status) || data.details === Hls.ErrorDetails.KEY_LOAD_ERROR && (status === void 0 || status === 0 || isAuthHttpStatus(status));
|
|
905
1060
|
if (!data.fatal) {
|
|
906
|
-
if (authFailure && data.type === Hls.ErrorTypes.NETWORK_ERROR && (data.details === Hls.ErrorDetails.FRAG_LOAD_ERROR || data.details === Hls.ErrorDetails.LEVEL_LOAD_ERROR
|
|
1061
|
+
if (authFailure && data.type === Hls.ErrorTypes.NETWORK_ERROR && (data.details === Hls.ErrorDetails.FRAG_LOAD_ERROR || data.details === Hls.ErrorDetails.LEVEL_LOAD_ERROR)) {
|
|
907
1062
|
callbacks.onAuthRecoveryNeeded?.(
|
|
908
|
-
data.details === Hls.ErrorDetails.
|
|
1063
|
+
data.details === Hls.ErrorDetails.LEVEL_LOAD_ERROR ? "level_error" : "frag_error"
|
|
909
1064
|
);
|
|
910
1065
|
}
|
|
911
1066
|
return;
|
|
@@ -1134,6 +1289,11 @@ function createVideoPlayer(options) {
|
|
|
1134
1289
|
volume: config.player.volume,
|
|
1135
1290
|
videoId: options.videoId,
|
|
1136
1291
|
keyProxyUrlBuilder: config.streaming.keyProxyUrlBuilder,
|
|
1292
|
+
// Optional: browser Bearer for Video API–origin enc.key (CORS-dependent).
|
|
1293
|
+
// CDN keys use signed query via the auth loader (no Bearer) — same as
|
|
1294
|
+
// segments. Apps can pass getKeyAccessToken or rely on keyProxyUrlBuilder.
|
|
1295
|
+
// When unset, defaults to AuthManager token only if explicitly requested
|
|
1296
|
+
// via streaming.getKeyAccessToken; otherwise query-signed XHR.
|
|
1137
1297
|
getKeyAccessToken: config.streaming.getKeyAccessToken,
|
|
1138
1298
|
hlsConfig: config.streaming.hls,
|
|
1139
1299
|
logger,
|
|
@@ -1448,11 +1608,13 @@ export {
|
|
|
1448
1608
|
createVideoPlayer,
|
|
1449
1609
|
getLiveEdgeTime,
|
|
1450
1610
|
isAtLiveEdge,
|
|
1611
|
+
isEncryptionKeyUrl,
|
|
1451
1612
|
isPlainObject,
|
|
1452
1613
|
mergePlayerConfig,
|
|
1453
1614
|
redactObject,
|
|
1454
1615
|
redactSensitive,
|
|
1455
1616
|
redactValue,
|
|
1617
|
+
resolveBuiltinHlsLoader,
|
|
1456
1618
|
rewriteHlsRequestUrl,
|
|
1457
1619
|
seekToLiveEdge,
|
|
1458
1620
|
themeToCssVars
|