@codenkay/video-nsgplayer-core 3.0.0 → 3.0.2
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 +18 -0
- package/dist/index.cjs +309 -139
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -24
- package/dist/index.d.ts +27 -24
- package/dist/index.js +308 -139
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.0.2
|
|
4
|
+
|
|
5
|
+
- Fix pack/build regression on AES `enc.key`: do **not** statically import
|
|
6
|
+
`XhrLoader` from `hls.js` (second module copy after npm pack broke key loads).
|
|
7
|
+
- Resolve builtin loader via same-module `DefaultConfig` + composition unwrap
|
|
8
|
+
(`Symbol` stash / WeakMap) so nested wrappers never replace real XhrLoader.
|
|
9
|
+
- Key auth order: BFF (≤2) → **signed-query without Bearer** → Bearer only on
|
|
10
|
+
401/403. Query-only fetch uses no custom headers (CORS simple request).
|
|
11
|
+
- `keyProxyMaxFailures` circuit breaker retained.
|
|
12
|
+
|
|
13
|
+
## 3.0.1
|
|
14
|
+
|
|
15
|
+
- Permanent AES key path: dedicated key loader with **session circuit breaker**
|
|
16
|
+
(`keyProxyMaxFailures` default **2**). A Cloudflare-blocked BFF is not hammered
|
|
17
|
+
by hls.js retries (previously 9–10× `/hls-key` 502s).
|
|
18
|
+
- After the circuit opens, the session uses AuthManager Bearer (or signed-query XHR) only.
|
|
19
|
+
- Host apps with `AUTH_MODE=browser` should omit `keyProxyUrlBuilder` entirely.
|
|
20
|
+
|
|
3
21
|
## 3.0.0
|
|
4
22
|
|
|
5
23
|
### Major Changes
|
package/dist/index.cjs
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
DEFAULT_THEME: () => DEFAULT_THEME,
|
|
40
40
|
DEFAULT_WATERMARK: () => DEFAULT_WATERMARK,
|
|
41
41
|
EventEmitter: () => EventEmitter,
|
|
42
|
+
KEY_PROXY_MAX_FAILURES: () => KEY_PROXY_MAX_FAILURES,
|
|
42
43
|
PACKAGE_VERSION: () => PACKAGE_VERSION,
|
|
43
44
|
PLAYBACK_RATES: () => PLAYBACK_RATES,
|
|
44
45
|
PlaybackSourceManager: () => PlaybackSourceManager,
|
|
@@ -212,7 +213,7 @@ var AuthManager = class {
|
|
|
212
213
|
|
|
213
214
|
// src/config/defaults.ts
|
|
214
215
|
var PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
215
|
-
var PACKAGE_VERSION = "
|
|
216
|
+
var PACKAGE_VERSION = "3.0.2";
|
|
216
217
|
var DEFAULT_PLAYER_BEHAVIOR = {
|
|
217
218
|
autoplay: true,
|
|
218
219
|
startMuted: true,
|
|
@@ -391,7 +392,8 @@ function mergePlayerConfig(partial) {
|
|
|
391
392
|
...partial?.streaming?.hls
|
|
392
393
|
},
|
|
393
394
|
keyProxyUrlBuilder: partial?.streaming?.keyProxyUrlBuilder,
|
|
394
|
-
getKeyAccessToken: partial?.streaming?.getKeyAccessToken
|
|
395
|
+
getKeyAccessToken: partial?.streaming?.getKeyAccessToken,
|
|
396
|
+
keyProxyMaxFailures: partial?.streaming?.keyProxyMaxFailures
|
|
395
397
|
},
|
|
396
398
|
logging: { ...DEFAULT_PLAYER_CONFIG.logging, ...partial?.logging },
|
|
397
399
|
debug: { ...DEFAULT_PLAYER_CONFIG.debug, ...partial?.debug },
|
|
@@ -831,9 +833,13 @@ function seekToLiveEdge(media, options) {
|
|
|
831
833
|
}
|
|
832
834
|
|
|
833
835
|
// src/streaming/authParamsLoader.ts
|
|
834
|
-
var import_hls = require("hls.js");
|
|
835
836
|
var ENC_KEY_PATH_RE = /enc\.key$/i;
|
|
836
837
|
var BUILTIN_LOADER_NAMES = /* @__PURE__ */ new Set(["XhrLoader", "FetchLoader"]);
|
|
838
|
+
var BUILTIN_LOADER_REF = /* @__PURE__ */ Symbol.for(
|
|
839
|
+
"@codenkay/video-nsgplayer-core/builtin-hls-loader"
|
|
840
|
+
);
|
|
841
|
+
var builtinLoaderByHls = /* @__PURE__ */ new WeakMap();
|
|
842
|
+
var KEY_PROXY_MAX_FAILURES = 2;
|
|
837
843
|
function isEncryptionKeyUrl(rawUrl, baseOrigin = typeof location !== "undefined" ? location.origin : "http://localhost") {
|
|
838
844
|
try {
|
|
839
845
|
return ENC_KEY_PATH_RE.test(new URL(rawUrl, baseOrigin).pathname);
|
|
@@ -841,18 +847,34 @@ function isEncryptionKeyUrl(rawUrl, baseOrigin = typeof location !== "undefined"
|
|
|
841
847
|
return /enc\.key/i.test(rawUrl);
|
|
842
848
|
}
|
|
843
849
|
}
|
|
844
|
-
function
|
|
845
|
-
|
|
846
|
-
|
|
850
|
+
function isBuiltinLoaderName(name) {
|
|
851
|
+
return BUILTIN_LOADER_NAMES.has(name);
|
|
852
|
+
}
|
|
853
|
+
function rememberBuiltin(HlsClass, loader) {
|
|
854
|
+
if (isBuiltinLoaderName(typeof loader.name === "string" ? loader.name : "")) {
|
|
855
|
+
builtinLoaderByHls.set(HlsClass, loader);
|
|
847
856
|
}
|
|
857
|
+
return loader;
|
|
858
|
+
}
|
|
859
|
+
function getStashedBuiltinLoader(loader) {
|
|
860
|
+
return loader[BUILTIN_LOADER_REF];
|
|
861
|
+
}
|
|
862
|
+
function resolveBuiltinHlsLoader(HlsClass) {
|
|
863
|
+
const cached = builtinLoaderByHls.get(HlsClass);
|
|
864
|
+
if (cached) return cached;
|
|
848
865
|
const start = HlsClass.DefaultConfig.loader;
|
|
849
866
|
let current = start;
|
|
850
867
|
const seen = /* @__PURE__ */ new Set();
|
|
851
868
|
while (current && !seen.has(current)) {
|
|
852
869
|
seen.add(current);
|
|
853
870
|
const name = typeof current.name === "string" ? current.name : "";
|
|
854
|
-
if (
|
|
855
|
-
return current;
|
|
871
|
+
if (isBuiltinLoaderName(name)) {
|
|
872
|
+
return rememberBuiltin(HlsClass, current);
|
|
873
|
+
}
|
|
874
|
+
const stashed = getStashedBuiltinLoader(current);
|
|
875
|
+
if (stashed) {
|
|
876
|
+
current = stashed;
|
|
877
|
+
continue;
|
|
856
878
|
}
|
|
857
879
|
const parentCtor = Object.getPrototypeOf(current.prototype)?.constructor;
|
|
858
880
|
if (!parentCtor || parentCtor === Object || parentCtor === Function) {
|
|
@@ -860,7 +882,8 @@ function resolveBuiltinHlsLoader(HlsClass) {
|
|
|
860
882
|
}
|
|
861
883
|
current = parentCtor;
|
|
862
884
|
}
|
|
863
|
-
|
|
885
|
+
const fallback = builtinLoaderByHls.get(HlsClass);
|
|
886
|
+
return fallback ?? start;
|
|
864
887
|
}
|
|
865
888
|
function rewriteHlsRequestUrl(rawUrl, store, options = {}, baseOrigin = typeof location !== "undefined" ? location.origin : "http://localhost") {
|
|
866
889
|
if (!rawUrl) return rawUrl;
|
|
@@ -894,68 +917,230 @@ function createLoaderStats() {
|
|
|
894
917
|
buffering: { start: 0, first: 0, end: 0 }
|
|
895
918
|
};
|
|
896
919
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
{
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
920
|
+
function isProxyUpstreamBlocked(status, contentType, bodyHead, errorText) {
|
|
921
|
+
if (status === 0 || status === 502 || status === 503 || status === 504) {
|
|
922
|
+
return true;
|
|
923
|
+
}
|
|
924
|
+
if (status === 403 || status === 401) return true;
|
|
925
|
+
if (contentType?.includes("text/html")) return true;
|
|
926
|
+
if (/<!DOCTYPE|<html|Just a moment|Cloudflare|blocked/i.test(bodyHead)) {
|
|
927
|
+
return true;
|
|
928
|
+
}
|
|
929
|
+
if (errorText && /Cloudflare|HTML|DOCTYPE|blocked|502/i.test(errorText)) {
|
|
930
|
+
return true;
|
|
931
|
+
}
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
async function fetchEncryptionKeyBytes(url, accessToken, signal) {
|
|
935
|
+
const headers = accessToken ? { Authorization: `Bearer ${accessToken}`, Accept: "*/*" } : void 0;
|
|
936
|
+
try {
|
|
937
|
+
const response = await fetch(url, {
|
|
938
|
+
method: "GET",
|
|
939
|
+
...headers ? { headers } : {},
|
|
940
|
+
credentials: "omit",
|
|
941
|
+
cache: "no-store",
|
|
942
|
+
signal
|
|
943
|
+
});
|
|
944
|
+
if (!response.ok) {
|
|
945
|
+
return {
|
|
946
|
+
ok: false,
|
|
947
|
+
status: response.status,
|
|
948
|
+
text: response.statusText || `enc.key HTTP ${response.status}`,
|
|
949
|
+
cors: false,
|
|
950
|
+
response
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
const data = await response.arrayBuffer();
|
|
954
|
+
const head = new TextDecoder().decode(data.slice(0, 32));
|
|
955
|
+
if (/<!DOCTYPE|<html/i.test(head)) {
|
|
956
|
+
return {
|
|
957
|
+
ok: false,
|
|
958
|
+
status: 403,
|
|
959
|
+
text: "enc.key returned HTML (CDN challenge/block), not an AES key",
|
|
960
|
+
cors: false,
|
|
961
|
+
response
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
return {
|
|
965
|
+
ok: true,
|
|
966
|
+
data,
|
|
921
967
|
response,
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
968
|
+
url: response.url || url
|
|
969
|
+
};
|
|
970
|
+
} catch (error) {
|
|
971
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
972
|
+
return {
|
|
973
|
+
ok: false,
|
|
974
|
+
status: 0,
|
|
975
|
+
text: "enc.key fetch aborted/timeout",
|
|
976
|
+
cors: false,
|
|
977
|
+
response: null
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
const cors = error instanceof TypeError || error instanceof Error && /Failed to fetch|NetworkError|CORS/i.test(error.message);
|
|
981
|
+
return {
|
|
982
|
+
ok: false,
|
|
983
|
+
status: 0,
|
|
984
|
+
text: error instanceof Error ? error.message : "enc.key fetch failed",
|
|
985
|
+
cors,
|
|
986
|
+
response: null
|
|
987
|
+
};
|
|
925
988
|
}
|
|
926
|
-
|
|
989
|
+
}
|
|
990
|
+
function reportKeySuccess(context, callbacks, result) {
|
|
991
|
+
const stats = createLoaderStats();
|
|
927
992
|
const end = performance.now();
|
|
993
|
+
stats.loading.first = stats.loading.start;
|
|
928
994
|
stats.loading.end = end;
|
|
929
|
-
stats.loaded = data.byteLength;
|
|
930
|
-
stats.total = data.byteLength;
|
|
931
|
-
|
|
932
|
-
|
|
995
|
+
stats.loaded = result.data.byteLength;
|
|
996
|
+
stats.total = result.data.byteLength;
|
|
997
|
+
callbacks.onSuccess(
|
|
998
|
+
{
|
|
999
|
+
url: result.url,
|
|
1000
|
+
data: result.data,
|
|
1001
|
+
code: result.response.status
|
|
1002
|
+
},
|
|
1003
|
+
stats,
|
|
1004
|
+
context,
|
|
1005
|
+
result.response
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
async function loadEncryptionKeyEnterprise(context, callbacks, plan, signal, state) {
|
|
1009
|
+
const stats = createLoaderStats();
|
|
1010
|
+
const canTryProxy = Boolean(plan.proxyUrl) && !state.circuitOpen && state.proxyFailures < plan.maxProxyFailures;
|
|
1011
|
+
if (canTryProxy && plan.proxyUrl) {
|
|
1012
|
+
while (!state.circuitOpen && state.proxyFailures < plan.maxProxyFailures && !signal.aborted) {
|
|
1013
|
+
state.proxyFailures += 1;
|
|
1014
|
+
try {
|
|
1015
|
+
const response = await fetch(plan.proxyUrl, {
|
|
1016
|
+
method: "GET",
|
|
1017
|
+
headers: { Accept: "*/*" },
|
|
1018
|
+
credentials: "same-origin",
|
|
1019
|
+
cache: "no-store",
|
|
1020
|
+
signal
|
|
1021
|
+
});
|
|
1022
|
+
const contentType = response.headers.get("content-type");
|
|
1023
|
+
const data = await response.arrayBuffer();
|
|
1024
|
+
const head = new TextDecoder().decode(data.slice(0, 64));
|
|
1025
|
+
if (response.ok && !isProxyUpstreamBlocked(response.status, contentType, head)) {
|
|
1026
|
+
const end = performance.now();
|
|
1027
|
+
stats.loading.end = end;
|
|
1028
|
+
stats.loaded = data.byteLength;
|
|
1029
|
+
stats.total = data.byteLength;
|
|
1030
|
+
callbacks.onSuccess(
|
|
1031
|
+
{
|
|
1032
|
+
url: response.url || plan.proxyUrl,
|
|
1033
|
+
data,
|
|
1034
|
+
code: response.status
|
|
1035
|
+
},
|
|
1036
|
+
stats,
|
|
1037
|
+
{ ...context, url: plan.proxyUrl },
|
|
1038
|
+
response
|
|
1039
|
+
);
|
|
1040
|
+
return "ok";
|
|
1041
|
+
}
|
|
1042
|
+
if (isProxyUpstreamBlocked(
|
|
1043
|
+
response.status,
|
|
1044
|
+
contentType,
|
|
1045
|
+
head,
|
|
1046
|
+
response.statusText
|
|
1047
|
+
)) {
|
|
1048
|
+
if (response.status === 502 || contentType?.includes("text/html") || /Cloudflare|DOCTYPE|blocked/i.test(head)) {
|
|
1049
|
+
state.circuitOpen = true;
|
|
1050
|
+
break;
|
|
1051
|
+
}
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
callbacks.onError(
|
|
1055
|
+
{
|
|
1056
|
+
code: response.status,
|
|
1057
|
+
text: `enc.key proxy HTTP ${response.status}`
|
|
1058
|
+
},
|
|
1059
|
+
{ ...context, url: plan.proxyUrl },
|
|
1060
|
+
response,
|
|
1061
|
+
stats
|
|
1062
|
+
);
|
|
1063
|
+
return "ok";
|
|
1064
|
+
} catch (error) {
|
|
1065
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
1066
|
+
callbacks.onError(
|
|
1067
|
+
{ code: 0, text: "enc.key proxy aborted/timeout" },
|
|
1068
|
+
context,
|
|
1069
|
+
null,
|
|
1070
|
+
stats
|
|
1071
|
+
);
|
|
1072
|
+
return "ok";
|
|
1073
|
+
}
|
|
1074
|
+
state.circuitOpen = true;
|
|
1075
|
+
break;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
state.circuitOpen = true;
|
|
1079
|
+
}
|
|
1080
|
+
const queryResult = await fetchEncryptionKeyBytes(
|
|
1081
|
+
plan.signedKeyUrl,
|
|
1082
|
+
null,
|
|
1083
|
+
signal
|
|
1084
|
+
);
|
|
1085
|
+
if (queryResult.ok) {
|
|
1086
|
+
reportKeySuccess(
|
|
1087
|
+
{ ...context, url: plan.signedKeyUrl },
|
|
1088
|
+
callbacks,
|
|
1089
|
+
queryResult
|
|
1090
|
+
);
|
|
1091
|
+
return "ok";
|
|
1092
|
+
}
|
|
1093
|
+
const needsBearer = Boolean(plan.accessToken) && (queryResult.status === 401 || queryResult.status === 403);
|
|
1094
|
+
if (needsBearer && plan.accessToken) {
|
|
1095
|
+
const bearerResult = await fetchEncryptionKeyBytes(
|
|
1096
|
+
plan.signedKeyUrl,
|
|
1097
|
+
plan.accessToken,
|
|
1098
|
+
signal
|
|
1099
|
+
);
|
|
1100
|
+
if (bearerResult.ok) {
|
|
1101
|
+
reportKeySuccess(
|
|
1102
|
+
{ ...context, url: plan.signedKeyUrl },
|
|
1103
|
+
callbacks,
|
|
1104
|
+
bearerResult
|
|
1105
|
+
);
|
|
1106
|
+
return "ok";
|
|
1107
|
+
}
|
|
1108
|
+
if (bearerResult.cors) {
|
|
1109
|
+
return "try_xhr";
|
|
1110
|
+
}
|
|
933
1111
|
callbacks.onError(
|
|
934
1112
|
{
|
|
935
|
-
code:
|
|
936
|
-
text:
|
|
1113
|
+
code: bearerResult.status,
|
|
1114
|
+
text: bearerResult.text
|
|
937
1115
|
},
|
|
938
|
-
context,
|
|
939
|
-
response,
|
|
1116
|
+
{ ...context, url: plan.signedKeyUrl },
|
|
1117
|
+
bearerResult.response,
|
|
940
1118
|
stats
|
|
941
1119
|
);
|
|
942
|
-
return;
|
|
1120
|
+
return "ok";
|
|
943
1121
|
}
|
|
944
|
-
|
|
1122
|
+
if (queryResult.cors || queryResult.status === 0) {
|
|
1123
|
+
return "try_xhr";
|
|
1124
|
+
}
|
|
1125
|
+
callbacks.onError(
|
|
945
1126
|
{
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
code: response.status
|
|
1127
|
+
code: queryResult.status,
|
|
1128
|
+
text: queryResult.text
|
|
949
1129
|
},
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
1130
|
+
{ ...context, url: plan.signedKeyUrl },
|
|
1131
|
+
queryResult.response,
|
|
1132
|
+
stats
|
|
953
1133
|
);
|
|
1134
|
+
return "ok";
|
|
954
1135
|
}
|
|
955
1136
|
function createAuthParamsLoader(HlsClass, store, options = {}) {
|
|
956
1137
|
const BaseLoader = resolveBuiltinHlsLoader(HlsClass);
|
|
957
|
-
|
|
1138
|
+
rememberBuiltin(HlsClass, BaseLoader);
|
|
1139
|
+
const maxProxyFailures = typeof options.keyProxyMaxFailures === "number" && options.keyProxyMaxFailures >= 0 ? options.keyProxyMaxFailures : KEY_PROXY_MAX_FAILURES;
|
|
1140
|
+
const keyProxyState = { proxyFailures: 0, circuitOpen: false };
|
|
1141
|
+
class AuthParamsLoader {
|
|
958
1142
|
constructor(config) {
|
|
1143
|
+
this.context = null;
|
|
959
1144
|
this.keyAbort = null;
|
|
960
1145
|
this.inner = new BaseLoader(config);
|
|
961
1146
|
this.stats = this.inner.stats;
|
|
@@ -963,14 +1148,16 @@ function createAuthParamsLoader(HlsClass, store, options = {}) {
|
|
|
963
1148
|
abort() {
|
|
964
1149
|
this.keyAbort?.abort();
|
|
965
1150
|
this.keyAbort = null;
|
|
966
|
-
this.inner.abort
|
|
1151
|
+
this.inner.abort();
|
|
967
1152
|
}
|
|
968
1153
|
destroy() {
|
|
969
1154
|
this.keyAbort?.abort();
|
|
970
1155
|
this.keyAbort = null;
|
|
1156
|
+
this.context = null;
|
|
971
1157
|
this.inner.destroy();
|
|
972
1158
|
}
|
|
973
1159
|
load(context, config, callbacks) {
|
|
1160
|
+
this.context = context;
|
|
974
1161
|
if (!context.url) {
|
|
975
1162
|
this.inner.load(context, config, callbacks);
|
|
976
1163
|
return;
|
|
@@ -978,92 +1165,70 @@ function createAuthParamsLoader(HlsClass, store, options = {}) {
|
|
|
978
1165
|
try {
|
|
979
1166
|
const originalUrl = context.url;
|
|
980
1167
|
const isKey = isEncryptionKeyUrl(originalUrl);
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
}) : context.url;
|
|
988
|
-
context.url = rewriteHlsRequestUrl(originalUrl, store, options);
|
|
989
|
-
if (usingKeyProxy) {
|
|
990
|
-
const accessToken = options.getKeyAccessToken?.() ?? null;
|
|
991
|
-
if (!accessToken) {
|
|
992
|
-
this.inner.load(context, config, callbacks);
|
|
993
|
-
return;
|
|
1168
|
+
if (!isKey) {
|
|
1169
|
+
if (store.hasParams()) {
|
|
1170
|
+
context.url = rewriteHlsRequestUrl(originalUrl, store, {
|
|
1171
|
+
...options,
|
|
1172
|
+
keyProxyUrlBuilder: void 0
|
|
1173
|
+
});
|
|
994
1174
|
}
|
|
995
|
-
|
|
996
|
-
...callbacks,
|
|
997
|
-
onError: (error, errContext, networkDetails, stats) => {
|
|
998
|
-
const code = error?.code;
|
|
999
|
-
const retryable = code === 0 || code === 502 || code === 403 || code === 401 || /Cloudflare|HTML|DOCTYPE|blocked/i.test(error?.text ?? "");
|
|
1000
|
-
if (!retryable) {
|
|
1001
|
-
callbacks.onError(error, errContext, networkDetails, stats);
|
|
1002
|
-
return;
|
|
1003
|
-
}
|
|
1004
|
-
this.keyAbort?.abort();
|
|
1005
|
-
this.keyAbort = new AbortController();
|
|
1006
|
-
const bearerContext = { ...context, url: signedKeyUrl };
|
|
1007
|
-
void loadEncryptionKeyWithFetch(
|
|
1008
|
-
bearerContext,
|
|
1009
|
-
callbacks,
|
|
1010
|
-
accessToken,
|
|
1011
|
-
this.keyAbort.signal
|
|
1012
|
-
).catch((fetchError) => {
|
|
1013
|
-
callbacks.onError(
|
|
1014
|
-
{
|
|
1015
|
-
code: 0,
|
|
1016
|
-
text: fetchError instanceof Error ? fetchError.message : error.text || "enc.key proxy and Bearer fetch failed"
|
|
1017
|
-
},
|
|
1018
|
-
bearerContext,
|
|
1019
|
-
null,
|
|
1020
|
-
stats
|
|
1021
|
-
);
|
|
1022
|
-
});
|
|
1023
|
-
}
|
|
1024
|
-
};
|
|
1025
|
-
this.inner.load(context, config, proxyCallbacks);
|
|
1175
|
+
this.inner.load(context, config, callbacks);
|
|
1026
1176
|
return;
|
|
1027
1177
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
}).finally(() => {
|
|
1057
|
-
clearTimeout(timer);
|
|
1058
|
-
});
|
|
1059
|
-
return;
|
|
1178
|
+
const signedKeyUrl = rewriteHlsRequestUrl(originalUrl, store, {
|
|
1179
|
+
...options,
|
|
1180
|
+
keyProxyUrlBuilder: void 0
|
|
1181
|
+
});
|
|
1182
|
+
const proxyConfigured = Boolean(
|
|
1183
|
+
options.keyProxyUrlBuilder && options.videoId
|
|
1184
|
+
);
|
|
1185
|
+
const proxyUrl = proxyConfigured && !keyProxyState.circuitOpen ? rewriteHlsRequestUrl(originalUrl, store, options) : null;
|
|
1186
|
+
const accessToken = options.getKeyAccessToken?.() ?? null;
|
|
1187
|
+
this.keyAbort?.abort();
|
|
1188
|
+
this.keyAbort = new AbortController();
|
|
1189
|
+
const timeoutMs = typeof config?.timeout === "number" && config.timeout > 0 ? config.timeout : 2e4;
|
|
1190
|
+
const timer = setTimeout(() => this.keyAbort?.abort(), timeoutMs);
|
|
1191
|
+
void loadEncryptionKeyEnterprise(
|
|
1192
|
+
context,
|
|
1193
|
+
callbacks,
|
|
1194
|
+
{
|
|
1195
|
+
proxyUrl,
|
|
1196
|
+
signedKeyUrl,
|
|
1197
|
+
accessToken,
|
|
1198
|
+
maxProxyFailures
|
|
1199
|
+
},
|
|
1200
|
+
this.keyAbort.signal,
|
|
1201
|
+
keyProxyState
|
|
1202
|
+
).then((result) => {
|
|
1203
|
+
if (result === "try_xhr") {
|
|
1204
|
+
context.url = signedKeyUrl;
|
|
1205
|
+
this.inner.load(context, config, callbacks);
|
|
1060
1206
|
}
|
|
1061
|
-
}
|
|
1207
|
+
}).catch((error) => {
|
|
1208
|
+
callbacks.onError(
|
|
1209
|
+
{
|
|
1210
|
+
code: 0,
|
|
1211
|
+
text: error instanceof Error ? error.message : "enc.key load failed"
|
|
1212
|
+
},
|
|
1213
|
+
context,
|
|
1214
|
+
null,
|
|
1215
|
+
createLoaderStats()
|
|
1216
|
+
);
|
|
1217
|
+
}).finally(() => {
|
|
1218
|
+
clearTimeout(timer);
|
|
1219
|
+
});
|
|
1220
|
+
return;
|
|
1062
1221
|
} catch {
|
|
1063
1222
|
}
|
|
1064
1223
|
this.inner.load(context, config, callbacks);
|
|
1065
1224
|
}
|
|
1066
|
-
}
|
|
1225
|
+
}
|
|
1226
|
+
Object.defineProperty(AuthParamsLoader, BUILTIN_LOADER_REF, {
|
|
1227
|
+
value: BaseLoader,
|
|
1228
|
+
enumerable: false,
|
|
1229
|
+
configurable: false
|
|
1230
|
+
});
|
|
1231
|
+
return AuthParamsLoader;
|
|
1067
1232
|
}
|
|
1068
1233
|
|
|
1069
1234
|
// src/streaming/HlsEngine.ts
|
|
@@ -1128,7 +1293,8 @@ var HlsEngine = class {
|
|
|
1128
1293
|
const loader = createAuthParamsLoader(Hls, authParams, {
|
|
1129
1294
|
videoId,
|
|
1130
1295
|
keyProxyUrlBuilder,
|
|
1131
|
-
getKeyAccessToken
|
|
1296
|
+
getKeyAccessToken,
|
|
1297
|
+
keyProxyMaxFailures: this.options.keyProxyMaxFailures
|
|
1132
1298
|
});
|
|
1133
1299
|
this.hls = new Hls({
|
|
1134
1300
|
enableWorker: hlsConfig.enableWorker,
|
|
@@ -1552,11 +1718,13 @@ function createVideoPlayer(options) {
|
|
|
1552
1718
|
volume: config.player.volume,
|
|
1553
1719
|
videoId: options.videoId,
|
|
1554
1720
|
keyProxyUrlBuilder: config.streaming.keyProxyUrlBuilder,
|
|
1555
|
-
// Enterprise AES key auth:
|
|
1556
|
-
// 1) keyProxyUrlBuilder
|
|
1557
|
-
// 2)
|
|
1558
|
-
// 3)
|
|
1721
|
+
// Enterprise AES key auth (see authParamsLoader):
|
|
1722
|
+
// 1) keyProxyUrlBuilder BFF — max 2 failures then circuit
|
|
1723
|
+
// 2) signed-query fetch (no Bearer / no CORS preflight)
|
|
1724
|
+
// 3) Bearer only on HTTP 401/403 when getKeyAccessToken is set
|
|
1725
|
+
// 4) signed-query XHR fallback if fetch is CORS-blocked
|
|
1559
1726
|
getKeyAccessToken: config.streaming.getKeyAccessToken ?? (() => authManager.getAccessToken()),
|
|
1727
|
+
keyProxyMaxFailures: config.streaming.keyProxyMaxFailures,
|
|
1560
1728
|
hlsConfig: config.streaming.hls,
|
|
1561
1729
|
logger,
|
|
1562
1730
|
callbacks: {
|
|
@@ -1874,7 +2042,8 @@ function createVideoPlayer(options) {
|
|
|
1874
2042
|
engine: "hls",
|
|
1875
2043
|
hls: { ...config.streaming.hls, ...partial?.streaming?.hls },
|
|
1876
2044
|
keyProxyUrlBuilder: partial?.streaming?.keyProxyUrlBuilder ?? config.streaming.keyProxyUrlBuilder,
|
|
1877
|
-
getKeyAccessToken: partial?.streaming?.getKeyAccessToken ?? config.streaming.getKeyAccessToken ?? (() => authManager.getAccessToken())
|
|
2045
|
+
getKeyAccessToken: partial?.streaming?.getKeyAccessToken ?? config.streaming.getKeyAccessToken ?? (() => authManager.getAccessToken()),
|
|
2046
|
+
keyProxyMaxFailures: partial?.streaming?.keyProxyMaxFailures ?? config.streaming.keyProxyMaxFailures
|
|
1878
2047
|
},
|
|
1879
2048
|
logging: { ...config.logging, ...partial?.logging },
|
|
1880
2049
|
debug: { ...config.debug, ...partial?.debug },
|
|
@@ -1906,6 +2075,7 @@ function createVideoPlayer(options) {
|
|
|
1906
2075
|
DEFAULT_THEME,
|
|
1907
2076
|
DEFAULT_WATERMARK,
|
|
1908
2077
|
EventEmitter,
|
|
2078
|
+
KEY_PROXY_MAX_FAILURES,
|
|
1909
2079
|
PACKAGE_VERSION,
|
|
1910
2080
|
PLAYBACK_RATES,
|
|
1911
2081
|
PlaybackSourceManager,
|