@codenkay/video-nsgplayer-core 3.0.1 → 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 +10 -0
- package/dist/index.cjs +154 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -17
- package/dist/index.d.ts +7 -17
- package/dist/index.js +154 -91
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
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
|
+
|
|
3
13
|
## 3.0.1
|
|
4
14
|
|
|
5
15
|
- Permanent AES key path: dedicated key loader with **session circuit breaker**
|
package/dist/index.cjs
CHANGED
|
@@ -213,7 +213,7 @@ var AuthManager = class {
|
|
|
213
213
|
|
|
214
214
|
// src/config/defaults.ts
|
|
215
215
|
var PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
216
|
-
var PACKAGE_VERSION = "3.0.
|
|
216
|
+
var PACKAGE_VERSION = "3.0.2";
|
|
217
217
|
var DEFAULT_PLAYER_BEHAVIOR = {
|
|
218
218
|
autoplay: true,
|
|
219
219
|
startMuted: true,
|
|
@@ -833,9 +833,12 @@ function seekToLiveEdge(media, options) {
|
|
|
833
833
|
}
|
|
834
834
|
|
|
835
835
|
// src/streaming/authParamsLoader.ts
|
|
836
|
-
var import_hls = require("hls.js");
|
|
837
836
|
var ENC_KEY_PATH_RE = /enc\.key$/i;
|
|
838
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();
|
|
839
842
|
var KEY_PROXY_MAX_FAILURES = 2;
|
|
840
843
|
function isEncryptionKeyUrl(rawUrl, baseOrigin = typeof location !== "undefined" ? location.origin : "http://localhost") {
|
|
841
844
|
try {
|
|
@@ -844,18 +847,34 @@ function isEncryptionKeyUrl(rawUrl, baseOrigin = typeof location !== "undefined"
|
|
|
844
847
|
return /enc\.key/i.test(rawUrl);
|
|
845
848
|
}
|
|
846
849
|
}
|
|
847
|
-
function
|
|
848
|
-
|
|
849
|
-
|
|
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);
|
|
850
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;
|
|
851
865
|
const start = HlsClass.DefaultConfig.loader;
|
|
852
866
|
let current = start;
|
|
853
867
|
const seen = /* @__PURE__ */ new Set();
|
|
854
868
|
while (current && !seen.has(current)) {
|
|
855
869
|
seen.add(current);
|
|
856
870
|
const name = typeof current.name === "string" ? current.name : "";
|
|
857
|
-
if (
|
|
858
|
-
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;
|
|
859
878
|
}
|
|
860
879
|
const parentCtor = Object.getPrototypeOf(current.prototype)?.constructor;
|
|
861
880
|
if (!parentCtor || parentCtor === Object || parentCtor === Function) {
|
|
@@ -863,7 +882,8 @@ function resolveBuiltinHlsLoader(HlsClass) {
|
|
|
863
882
|
}
|
|
864
883
|
current = parentCtor;
|
|
865
884
|
}
|
|
866
|
-
|
|
885
|
+
const fallback = builtinLoaderByHls.get(HlsClass);
|
|
886
|
+
return fallback ?? start;
|
|
867
887
|
}
|
|
868
888
|
function rewriteHlsRequestUrl(rawUrl, store, options = {}, baseOrigin = typeof location !== "undefined" ? location.origin : "http://localhost") {
|
|
869
889
|
if (!rawUrl) return rawUrl;
|
|
@@ -911,67 +931,82 @@ function isProxyUpstreamBlocked(status, contentType, bodyHead, errorText) {
|
|
|
911
931
|
}
|
|
912
932
|
return false;
|
|
913
933
|
}
|
|
914
|
-
async function
|
|
915
|
-
const
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
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,
|
|
938
967
|
response,
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
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
|
+
};
|
|
942
988
|
}
|
|
943
|
-
|
|
989
|
+
}
|
|
990
|
+
function reportKeySuccess(context, callbacks, result) {
|
|
991
|
+
const stats = createLoaderStats();
|
|
944
992
|
const end = performance.now();
|
|
993
|
+
stats.loading.first = stats.loading.start;
|
|
945
994
|
stats.loading.end = end;
|
|
946
|
-
stats.loaded = data.byteLength;
|
|
947
|
-
stats.total = data.byteLength;
|
|
948
|
-
const head = new TextDecoder().decode(data.slice(0, 32));
|
|
949
|
-
if (/<!DOCTYPE|<html/i.test(head)) {
|
|
950
|
-
callbacks.onError(
|
|
951
|
-
{
|
|
952
|
-
code: 403,
|
|
953
|
-
text: "enc.key returned HTML (CDN challenge/block), not an AES key"
|
|
954
|
-
},
|
|
955
|
-
context,
|
|
956
|
-
response,
|
|
957
|
-
stats
|
|
958
|
-
);
|
|
959
|
-
return;
|
|
960
|
-
}
|
|
995
|
+
stats.loaded = result.data.byteLength;
|
|
996
|
+
stats.total = result.data.byteLength;
|
|
961
997
|
callbacks.onSuccess(
|
|
962
998
|
{
|
|
963
|
-
url:
|
|
964
|
-
data,
|
|
965
|
-
code: response.status
|
|
999
|
+
url: result.url,
|
|
1000
|
+
data: result.data,
|
|
1001
|
+
code: result.response.status
|
|
966
1002
|
},
|
|
967
1003
|
stats,
|
|
968
1004
|
context,
|
|
969
|
-
response
|
|
1005
|
+
result.response
|
|
970
1006
|
);
|
|
971
1007
|
}
|
|
972
1008
|
async function loadEncryptionKeyEnterprise(context, callbacks, plan, signal, state) {
|
|
973
1009
|
const stats = createLoaderStats();
|
|
974
|
-
stats.loading.first = performance.now();
|
|
975
1010
|
const canTryProxy = Boolean(plan.proxyUrl) && !state.circuitOpen && state.proxyFailures < plan.maxProxyFailures;
|
|
976
1011
|
if (canTryProxy && plan.proxyUrl) {
|
|
977
1012
|
while (!state.circuitOpen && state.proxyFailures < plan.maxProxyFailures && !signal.aborted) {
|
|
@@ -1042,47 +1077,68 @@ async function loadEncryptionKeyEnterprise(context, callbacks, plan, signal, sta
|
|
|
1042
1077
|
}
|
|
1043
1078
|
state.circuitOpen = true;
|
|
1044
1079
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
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(
|
|
1048
1102
|
{ ...context, url: plan.signedKeyUrl },
|
|
1049
1103
|
callbacks,
|
|
1050
|
-
|
|
1051
|
-
signal
|
|
1052
|
-
);
|
|
1053
|
-
return "ok";
|
|
1054
|
-
} catch (error) {
|
|
1055
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
1056
|
-
callbacks.onError(
|
|
1057
|
-
{ code: 0, text: "enc.key fetch aborted/timeout" },
|
|
1058
|
-
{ ...context, url: plan.signedKeyUrl },
|
|
1059
|
-
null,
|
|
1060
|
-
stats
|
|
1061
|
-
);
|
|
1062
|
-
return "ok";
|
|
1063
|
-
}
|
|
1064
|
-
if (error instanceof TypeError || error instanceof Error && /Failed to fetch|NetworkError|CORS/i.test(error.message)) {
|
|
1065
|
-
return "try_xhr";
|
|
1066
|
-
}
|
|
1067
|
-
callbacks.onError(
|
|
1068
|
-
{
|
|
1069
|
-
code: 0,
|
|
1070
|
-
text: error instanceof Error ? error.message : "enc.key Bearer fetch failed"
|
|
1071
|
-
},
|
|
1072
|
-
{ ...context, url: plan.signedKeyUrl },
|
|
1073
|
-
null,
|
|
1074
|
-
stats
|
|
1104
|
+
bearerResult
|
|
1075
1105
|
);
|
|
1076
1106
|
return "ok";
|
|
1077
1107
|
}
|
|
1108
|
+
if (bearerResult.cors) {
|
|
1109
|
+
return "try_xhr";
|
|
1110
|
+
}
|
|
1111
|
+
callbacks.onError(
|
|
1112
|
+
{
|
|
1113
|
+
code: bearerResult.status,
|
|
1114
|
+
text: bearerResult.text
|
|
1115
|
+
},
|
|
1116
|
+
{ ...context, url: plan.signedKeyUrl },
|
|
1117
|
+
bearerResult.response,
|
|
1118
|
+
stats
|
|
1119
|
+
);
|
|
1120
|
+
return "ok";
|
|
1121
|
+
}
|
|
1122
|
+
if (queryResult.cors || queryResult.status === 0) {
|
|
1123
|
+
return "try_xhr";
|
|
1078
1124
|
}
|
|
1079
|
-
|
|
1125
|
+
callbacks.onError(
|
|
1126
|
+
{
|
|
1127
|
+
code: queryResult.status,
|
|
1128
|
+
text: queryResult.text
|
|
1129
|
+
},
|
|
1130
|
+
{ ...context, url: plan.signedKeyUrl },
|
|
1131
|
+
queryResult.response,
|
|
1132
|
+
stats
|
|
1133
|
+
);
|
|
1134
|
+
return "ok";
|
|
1080
1135
|
}
|
|
1081
1136
|
function createAuthParamsLoader(HlsClass, store, options = {}) {
|
|
1082
1137
|
const BaseLoader = resolveBuiltinHlsLoader(HlsClass);
|
|
1138
|
+
rememberBuiltin(HlsClass, BaseLoader);
|
|
1083
1139
|
const maxProxyFailures = typeof options.keyProxyMaxFailures === "number" && options.keyProxyMaxFailures >= 0 ? options.keyProxyMaxFailures : KEY_PROXY_MAX_FAILURES;
|
|
1084
1140
|
const keyProxyState = { proxyFailures: 0, circuitOpen: false };
|
|
1085
|
-
|
|
1141
|
+
class AuthParamsLoader {
|
|
1086
1142
|
constructor(config) {
|
|
1087
1143
|
this.context = null;
|
|
1088
1144
|
this.keyAbort = null;
|
|
@@ -1166,7 +1222,13 @@ function createAuthParamsLoader(HlsClass, store, options = {}) {
|
|
|
1166
1222
|
}
|
|
1167
1223
|
this.inner.load(context, config, callbacks);
|
|
1168
1224
|
}
|
|
1169
|
-
}
|
|
1225
|
+
}
|
|
1226
|
+
Object.defineProperty(AuthParamsLoader, BUILTIN_LOADER_REF, {
|
|
1227
|
+
value: BaseLoader,
|
|
1228
|
+
enumerable: false,
|
|
1229
|
+
configurable: false
|
|
1230
|
+
});
|
|
1231
|
+
return AuthParamsLoader;
|
|
1170
1232
|
}
|
|
1171
1233
|
|
|
1172
1234
|
// src/streaming/HlsEngine.ts
|
|
@@ -1656,10 +1718,11 @@ function createVideoPlayer(options) {
|
|
|
1656
1718
|
volume: config.player.volume,
|
|
1657
1719
|
videoId: options.videoId,
|
|
1658
1720
|
keyProxyUrlBuilder: config.streaming.keyProxyUrlBuilder,
|
|
1659
|
-
// Enterprise AES key auth:
|
|
1660
|
-
// 1) keyProxyUrlBuilder
|
|
1661
|
-
// 2)
|
|
1662
|
-
// 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
|
|
1663
1726
|
getKeyAccessToken: config.streaming.getKeyAccessToken ?? (() => authManager.getAccessToken()),
|
|
1664
1727
|
keyProxyMaxFailures: config.streaming.keyProxyMaxFailures,
|
|
1665
1728
|
hlsConfig: config.streaming.hls,
|