@codenkay/video-nsgplayer-core 0.1.3 → 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/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.3";
149
+ var PACKAGE_VERSION = "0.1.8";
150
150
  var DEFAULT_PLAYER_BEHAVIOR = {
151
151
  autoplay: true,
152
152
  startMuted: true,
@@ -239,7 +239,8 @@ var SKIP_DEEP_SCAN_KEYS = /* @__PURE__ */ new Set([
239
239
  "gettoken",
240
240
  "getsource",
241
241
  "refreshsource",
242
- "keyproxyurlbuilder"
242
+ "keyproxyurlbuilder",
243
+ "getkeyaccesstoken"
243
244
  ]);
244
245
  var SECRET_SCAN_MAX_DEPTH = 32;
245
246
  function isPlainObject(value) {
@@ -323,7 +324,8 @@ function mergePlayerConfig(partial) {
323
324
  ...DEFAULT_PLAYER_CONFIG.streaming.hls,
324
325
  ...partial?.streaming?.hls
325
326
  },
326
- keyProxyUrlBuilder: partial?.streaming?.keyProxyUrlBuilder
327
+ keyProxyUrlBuilder: partial?.streaming?.keyProxyUrlBuilder,
328
+ getKeyAccessToken: partial?.streaming?.getKeyAccessToken
327
329
  },
328
330
  logging: { ...DEFAULT_PLAYER_CONFIG.logging, ...partial?.logging },
329
331
  debug: { ...DEFAULT_PLAYER_CONFIG.debug, ...partial?.debug },
@@ -404,23 +406,25 @@ function isPlainObject2(value) {
404
406
  const proto = Object.getPrototypeOf(value);
405
407
  return proto === Object.prototype || proto === null;
406
408
  }
407
- function redactValue(key, value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
408
- if (SENSITIVE_KEYS.test(key)) return "[REDACTED]";
409
- if (typeof value === "string") {
410
- if (/^https?:\/\//i.test(value) && /[?&](token|expires)=/i.test(value)) {
411
- try {
412
- const url = new URL(value);
413
- for (const param of ["token", "expires", "signature", "sig"]) {
414
- if (url.searchParams.has(param)) {
415
- url.searchParams.set(param, "[REDACTED]");
416
- }
417
- }
418
- return url.toString();
419
- } catch {
420
- 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]");
421
418
  }
422
419
  }
420
+ return url.toString();
421
+ } catch {
422
+ return "[REDACTED_URL]";
423
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);
424
428
  if (Array.isArray(value)) {
425
429
  if (depth >= REDACT_MAX_DEPTH || seen.has(value)) return "[REDACTED_CYCLE]";
426
430
  seen.add(value);
@@ -687,10 +691,11 @@ var RefreshManager = class {
687
691
  this.options.logger.debug("Playback source renewed", { reason });
688
692
  } catch (error) {
689
693
  this.consecutiveFailures += 1;
690
- const message = error instanceof Error ? error.message : "Playback refresh failed";
694
+ const message = refreshErrorMessage(error);
691
695
  this.options.logger.warn("Playback refresh failed", {
692
696
  reason,
693
- failure: this.consecutiveFailures
697
+ failure: this.consecutiveFailures,
698
+ message
694
699
  });
695
700
  if (this.consecutiveFailures >= this.options.renewMaxRetries) {
696
701
  this.options.onFatal?.(message);
@@ -710,6 +715,13 @@ var RefreshManager = class {
710
715
  }
711
716
  }
712
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
+ }
713
725
 
714
726
  // src/playback/liveEdge.ts
715
727
  var DEFAULT_LIVE_EDGE_TOLERANCE_SECONDS = 3;
@@ -753,7 +765,37 @@ function seekToLiveEdge(media, options) {
753
765
  }
754
766
 
755
767
  // src/streaming/authParamsLoader.ts
756
- var ENC_KEY_PATH_RE = /\/enc\.key$/i;
768
+ import { XhrLoader as BuiltinXhrLoader } from "hls.js";
769
+ var ENC_KEY_PATH_RE = /enc\.key$/i;
770
+ var BUILTIN_LOADER_NAMES = /* @__PURE__ */ new Set(["XhrLoader", "FetchLoader"]);
771
+ function isEncryptionKeyUrl(rawUrl, baseOrigin = typeof location !== "undefined" ? location.origin : "http://localhost") {
772
+ try {
773
+ return ENC_KEY_PATH_RE.test(new URL(rawUrl, baseOrigin).pathname);
774
+ } catch {
775
+ return /enc\.key/i.test(rawUrl);
776
+ }
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
+ }
757
799
  function rewriteHlsRequestUrl(rawUrl, store, options = {}, baseOrigin = typeof location !== "undefined" ? location.origin : "http://localhost") {
758
800
  if (!rawUrl) return rawUrl;
759
801
  const parsed = new URL(rawUrl, baseOrigin);
@@ -764,28 +806,161 @@ function rewriteHlsRequestUrl(rawUrl, store, options = {}, baseOrigin = typeof l
764
806
  videoId: options.videoId,
765
807
  keyUrl: parsed.toString(),
766
808
  token: snapshot.token,
767
- expires: snapshot.expires
809
+ expires: snapshot.expires,
810
+ username: snapshot.username,
811
+ mobile: snapshot.mobile
768
812
  });
769
813
  }
770
814
  if (!store.hasParams()) return parsed.toString();
771
815
  return store.applyToUrl(rawUrl, baseOrigin);
772
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
+ }
773
889
  function createAuthParamsLoader(HlsClass, store, options = {}) {
774
- const BaseLoader = HlsClass.DefaultConfig.loader;
775
- return class AuthParamsLoader extends BaseLoader {
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
+ }
776
907
  load(context, config, callbacks) {
777
- if (context.url) {
778
- try {
779
- context.url = rewriteHlsRequestUrl(context.url, store, options);
780
- } catch {
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;
781
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;
951
+ }
952
+ }
953
+ } catch {
782
954
  }
783
- super.load(context, config, callbacks);
955
+ this.inner.load(context, config, callbacks);
784
956
  }
785
957
  };
786
958
  }
787
959
 
788
960
  // src/streaming/HlsEngine.ts
961
+ function isAuthHttpStatus(code) {
962
+ return code === 401 || code === 403;
963
+ }
789
964
  var HlsEngine = class {
790
965
  constructor(options) {
791
966
  this.options = options;
@@ -798,49 +973,47 @@ var HlsEngine = class {
798
973
  this.qualitySelection = "auto";
799
974
  }
800
975
  async start() {
801
- const { videoEl, playbackUrl, autoplay, startMuted, volume, startPosition } = this.options;
976
+ const { videoEl, playbackUrl, startMuted, volume } = this.options;
802
977
  videoEl.muted = startMuted;
803
- if (typeof volume === "number") videoEl.volume = Math.min(1, Math.max(0, volume));
978
+ if (typeof volume === "number") {
979
+ videoEl.volume = Math.min(1, Math.max(0, volume));
980
+ }
804
981
  const Hls = (await import("hls.js")).default;
805
982
  if (Hls.isSupported()) {
806
983
  this.supportMode = "hls.js";
807
984
  this.options.callbacks.onSupportMode?.("hls.js");
808
985
  await this.startWithHlsJs(Hls, playbackUrl);
809
- } else if (videoEl.canPlayType("application/vnd.apple.mpegurl")) {
986
+ return;
987
+ }
988
+ if (videoEl.canPlayType("application/vnd.apple.mpegurl")) {
810
989
  this.supportMode = "native";
811
990
  this.options.callbacks.onSupportMode?.("native");
812
991
  videoEl.src = this.options.authParams.applyToUrl(playbackUrl);
813
992
  this.bindMediaEvents();
814
993
  this.options.callbacks.onStateChange?.("ready");
815
- } else {
816
- this.supportMode = "unsupported";
817
- this.options.callbacks.onSupportMode?.("unsupported");
818
- this.options.callbacks.onFatalError?.("HLS is not supported in this browser");
994
+ await this.applyStartPositionAndAutoplay();
819
995
  return;
820
996
  }
821
- if (typeof startPosition === "number" && startPosition > 0) {
822
- videoEl.currentTime = startPosition;
823
- }
824
- if (autoplay) {
825
- try {
826
- await videoEl.play();
827
- } catch {
828
- if (!videoEl.muted) {
829
- videoEl.muted = true;
830
- try {
831
- await videoEl.play();
832
- } catch {
833
- this.options.logger.warn("Autoplay blocked");
834
- }
835
- }
836
- }
837
- }
997
+ this.supportMode = "unsupported";
998
+ this.options.callbacks.onSupportMode?.("unsupported");
999
+ this.options.callbacks.onFatalError?.(
1000
+ "HLS is not supported in this browser"
1001
+ );
838
1002
  }
839
1003
  async startWithHlsJs(Hls, playbackUrl) {
840
- const { videoEl, authParams, hlsConfig, callbacks, videoId, keyProxyUrlBuilder } = this.options;
1004
+ const {
1005
+ videoEl,
1006
+ authParams,
1007
+ hlsConfig,
1008
+ callbacks,
1009
+ videoId,
1010
+ keyProxyUrlBuilder,
1011
+ getKeyAccessToken
1012
+ } = this.options;
841
1013
  const loader = createAuthParamsLoader(Hls, authParams, {
842
1014
  videoId,
843
- keyProxyUrlBuilder
1015
+ keyProxyUrlBuilder,
1016
+ getKeyAccessToken
844
1017
  });
845
1018
  this.hls = new Hls({
846
1019
  enableWorker: hlsConfig.enableWorker,
@@ -851,6 +1024,7 @@ var HlsEngine = class {
851
1024
  this.hls.attachMedia(videoEl);
852
1025
  this.hls.loadSource(authParams.applyToUrl(playbackUrl));
853
1026
  this.hls.on(Hls.Events.MANIFEST_PARSED, (_event, data) => {
1027
+ if (this.destroyed) return;
854
1028
  this.qualities = (data.levels ?? []).map((level, index) => ({
855
1029
  index,
856
1030
  height: level.height ?? 0,
@@ -860,42 +1034,100 @@ var HlsEngine = class {
860
1034
  }));
861
1035
  callbacks.onQualitiesChange?.(this.qualities);
862
1036
  callbacks.onStateChange?.("ready");
1037
+ void this.applyStartPositionAndAutoplay();
863
1038
  });
864
1039
  this.hls.on(Hls.Events.ERROR, (_event, data) => {
865
- if (!data.fatal) {
866
- if (data.type === Hls.ErrorTypes.NETWORK_ERROR && (data.details === Hls.ErrorDetails.FRAG_LOAD_ERROR || data.details === Hls.ErrorDetails.LEVEL_LOAD_ERROR || data.details === Hls.ErrorDetails.KEY_LOAD_ERROR)) {
867
- callbacks.onAuthRecoveryNeeded?.(
868
- data.details === Hls.ErrorDetails.KEY_LOAD_ERROR ? "key_error" : data.details === Hls.ErrorDetails.LEVEL_LOAD_ERROR ? "level_error" : "frag_error"
869
- );
870
- }
1040
+ this.handleHlsError(Hls, data);
1041
+ });
1042
+ this.bindMediaEvents();
1043
+ }
1044
+ handleHlsError(Hls, data) {
1045
+ if (this.destroyed) return;
1046
+ const { callbacks, hlsConfig } = this.options;
1047
+ const status = data.response?.code;
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
+ );
871
1057
  return;
872
1058
  }
873
- if (data.type === Hls.ErrorTypes.NETWORK_ERROR) {
874
- this.networkRecoveryCount += 1;
875
- if (this.networkRecoveryCount <= hlsConfig.fatalRecoveryLimit) {
1059
+ }
1060
+ if (!data.fatal) {
1061
+ if (authFailure && data.type === Hls.ErrorTypes.NETWORK_ERROR && (data.details === Hls.ErrorDetails.FRAG_LOAD_ERROR || data.details === Hls.ErrorDetails.LEVEL_LOAD_ERROR)) {
1062
+ callbacks.onAuthRecoveryNeeded?.(
1063
+ data.details === Hls.ErrorDetails.LEVEL_LOAD_ERROR ? "level_error" : "frag_error"
1064
+ );
1065
+ }
1066
+ return;
1067
+ }
1068
+ if (data.type === Hls.ErrorTypes.NETWORK_ERROR) {
1069
+ this.networkRecoveryCount += 1;
1070
+ if (this.networkRecoveryCount <= hlsConfig.fatalRecoveryLimit) {
1071
+ if (authFailure) {
876
1072
  callbacks.onAuthRecoveryNeeded?.("fatal_network");
877
- this.hls?.startLoad();
878
- return;
879
1073
  }
1074
+ this.hls?.startLoad();
1075
+ return;
880
1076
  }
881
- if (data.type === Hls.ErrorTypes.MEDIA_ERROR) {
882
- this.mediaRecoveryCount += 1;
883
- if (this.mediaRecoveryCount <= hlsConfig.fatalRecoveryLimit) {
884
- this.hls?.recoverMediaError();
885
- return;
1077
+ }
1078
+ if (data.type === Hls.ErrorTypes.MEDIA_ERROR) {
1079
+ this.mediaRecoveryCount += 1;
1080
+ if (this.mediaRecoveryCount <= hlsConfig.fatalRecoveryLimit) {
1081
+ this.hls?.recoverMediaError();
1082
+ return;
1083
+ }
1084
+ }
1085
+ callbacks.onFatalError?.(data.details || "Fatal HLS error");
1086
+ }
1087
+ async applyStartPositionAndAutoplay() {
1088
+ if (this.destroyed) return;
1089
+ const { videoEl, autoplay, startPosition } = this.options;
1090
+ if (typeof startPosition === "number" && startPosition > 0) {
1091
+ const seek = () => {
1092
+ if (this.destroyed) return;
1093
+ try {
1094
+ videoEl.currentTime = startPosition;
1095
+ } catch {
886
1096
  }
1097
+ };
1098
+ if (videoEl.seekable.length > 0) {
1099
+ seek();
1100
+ } else {
1101
+ videoEl.addEventListener("loadedmetadata", seek, { once: true });
887
1102
  }
888
- callbacks.onFatalError?.(data.details || "Fatal HLS error");
889
- });
890
- this.bindMediaEvents();
1103
+ }
1104
+ if (!autoplay) return;
1105
+ try {
1106
+ await videoEl.play();
1107
+ } catch {
1108
+ if (!videoEl.muted) {
1109
+ videoEl.muted = true;
1110
+ try {
1111
+ await videoEl.play();
1112
+ } catch {
1113
+ this.options.logger.warn("Autoplay blocked");
1114
+ }
1115
+ }
1116
+ }
891
1117
  }
892
1118
  bindMediaEvents() {
893
1119
  const { videoEl, callbacks } = this.options;
894
- videoEl.addEventListener("playing", () => callbacks.onStateChange?.("playing"));
1120
+ videoEl.addEventListener(
1121
+ "playing",
1122
+ () => callbacks.onStateChange?.("playing")
1123
+ );
895
1124
  videoEl.addEventListener("pause", () => {
896
1125
  if (!videoEl.ended) callbacks.onStateChange?.("paused");
897
1126
  });
898
- videoEl.addEventListener("waiting", () => callbacks.onStateChange?.("buffering"));
1127
+ videoEl.addEventListener(
1128
+ "waiting",
1129
+ () => callbacks.onStateChange?.("buffering")
1130
+ );
899
1131
  videoEl.addEventListener("ended", () => {
900
1132
  callbacks.onStateChange?.("ended");
901
1133
  callbacks.onEnded?.();
@@ -913,6 +1145,7 @@ var HlsEngine = class {
913
1145
  }
914
1146
  }
915
1147
  resumeAfterAuthRenew() {
1148
+ this.networkRecoveryCount = 0;
916
1149
  this.hls?.startLoad();
917
1150
  this.updateAuthParams();
918
1151
  }
@@ -1055,7 +1288,13 @@ function createVideoPlayer(options) {
1055
1288
  startPosition: config.player.startPosition,
1056
1289
  volume: config.player.volume,
1057
1290
  videoId: options.videoId,
1058
- keyProxyUrlBuilder: options.streaming?.keyProxyUrlBuilder,
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.
1297
+ getKeyAccessToken: config.streaming.getKeyAccessToken,
1059
1298
  hlsConfig: config.streaming.hls,
1060
1299
  logger,
1061
1300
  callbacks: {
@@ -1116,6 +1355,14 @@ function createVideoPlayer(options) {
1116
1355
  onRenewing: () => setState("renewing"),
1117
1356
  onRenewed: (nextExpiresAt) => {
1118
1357
  engine?.resumeAfterAuthRenew();
1358
+ if (!destroyed) {
1359
+ const el2 = videoEl;
1360
+ if (el2 && !el2.paused && !el2.ended) {
1361
+ setState("playing");
1362
+ } else {
1363
+ setState("ready");
1364
+ }
1365
+ }
1119
1366
  events.emit("tokenrefresh", { expiresAt: nextExpiresAt });
1120
1367
  events.emit("sourcechange", { expiresAt: nextExpiresAt });
1121
1368
  },
@@ -1317,7 +1564,8 @@ function createVideoPlayer(options) {
1317
1564
  streaming: {
1318
1565
  engine: "hls",
1319
1566
  hls: { ...config.streaming.hls, ...partial?.streaming?.hls },
1320
- keyProxyUrlBuilder: partial?.streaming?.keyProxyUrlBuilder ?? config.streaming.keyProxyUrlBuilder
1567
+ keyProxyUrlBuilder: partial?.streaming?.keyProxyUrlBuilder ?? config.streaming.keyProxyUrlBuilder,
1568
+ getKeyAccessToken: partial?.streaming?.getKeyAccessToken ?? config.streaming.getKeyAccessToken
1321
1569
  },
1322
1570
  logging: { ...config.logging, ...partial?.logging },
1323
1571
  debug: { ...config.debug, ...partial?.debug },
@@ -1360,11 +1608,13 @@ export {
1360
1608
  createVideoPlayer,
1361
1609
  getLiveEdgeTime,
1362
1610
  isAtLiveEdge,
1611
+ isEncryptionKeyUrl,
1363
1612
  isPlainObject,
1364
1613
  mergePlayerConfig,
1365
1614
  redactObject,
1366
1615
  redactSensitive,
1367
1616
  redactValue,
1617
+ resolveBuiltinHlsLoader,
1368
1618
  rewriteHlsRequestUrl,
1369
1619
  seekToLiveEdge,
1370
1620
  themeToCssVars