eb-player 2.1.2 → 2.1.3
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/build/ebplayer.bundle.js +97 -31
- package/dist/build/ebplayer.bundle.js.map +1 -1
- package/dist/build/types/src/engines/cdn-token-manager.d.ts +18 -0
- package/dist/build/types/src/engines/cdn-token-manager.d.ts.map +1 -1
- package/dist/build/types/src/engines/hls.d.ts.map +1 -1
- package/dist/build/types/src/engines/segment-token-loader.d.ts +46 -25
- package/dist/build/types/src/engines/segment-token-loader.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.EBPlayer = {}));
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
|
-
var __EB_PLAYER_VERSION__ = "2.1.
|
|
7
|
+
var __EB_PLAYER_VERSION__ = "2.1.3";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Finite State Machine for player playback state transitions.
|
|
@@ -5119,6 +5119,7 @@
|
|
|
5119
5119
|
this.retryInterval = retryInterval;
|
|
5120
5120
|
this.expirationMarginInSeconds = expirationMarginInSeconds;
|
|
5121
5121
|
this.lastTokenResponse = null;
|
|
5122
|
+
this.sourceUrl = null;
|
|
5122
5123
|
this.resetAttemptCounterTimeout = null;
|
|
5123
5124
|
this.inFlightFetch = null;
|
|
5124
5125
|
}
|
|
@@ -5437,8 +5438,17 @@
|
|
|
5437
5438
|
}
|
|
5438
5439
|
break;
|
|
5439
5440
|
}
|
|
5441
|
+
// Query-param tokens (easy_b/venom/bunny) carry a prefix-scoped `token_path`
|
|
5442
|
+
// and share one grant across manifests and segments, so the grant must be
|
|
5443
|
+
// minted against the asset-level source URL rather than whichever URL
|
|
5444
|
+
// triggered this refresh. Akamai's path-segment mechanic is scoped
|
|
5445
|
+
// differently and is deliberately left on the requested URL.
|
|
5446
|
+
const sharesPrefixScopedGrant = this.tokenType === TOKEN_TYPES.EASY_B
|
|
5447
|
+
|| this.tokenType === TOKEN_TYPES.VENOM
|
|
5448
|
+
|| this.tokenType === TOKEN_TYPES.BUNNY;
|
|
5449
|
+
const fetchSrc = (sharesPrefixScopedGrant && this.sourceUrl) ? this.sourceUrl : srcUrl.toString();
|
|
5440
5450
|
try {
|
|
5441
|
-
const tokenResponse = await this.fetchToken({ src:
|
|
5451
|
+
const tokenResponse = await this.fetchToken({ src: fetchSrc });
|
|
5442
5452
|
if (tokenResponse === null) {
|
|
5443
5453
|
return url;
|
|
5444
5454
|
}
|
|
@@ -5759,33 +5769,39 @@
|
|
|
5759
5769
|
* Segment (fragment) CDN token signing — shared primitive.
|
|
5760
5770
|
*
|
|
5761
5771
|
* Extends CDN token signing from manifests to segments, behind an opt-in flag
|
|
5762
|
-
* (`EngineSettings.signSegmentsWithCdnToken`).
|
|
5763
|
-
* grant carried by its OWN parent playlist — read from
|
|
5764
|
-
* the object hls.js itself parsed that fragment from
|
|
5765
|
-
*
|
|
5766
|
-
*
|
|
5767
|
-
*
|
|
5768
|
-
*
|
|
5769
|
-
*
|
|
5770
|
-
*
|
|
5771
|
-
*
|
|
5772
|
-
*
|
|
5773
|
-
*
|
|
5774
|
-
*
|
|
5775
|
-
*
|
|
5776
|
-
*
|
|
5777
|
-
*
|
|
5778
|
-
*
|
|
5779
|
-
*
|
|
5780
|
-
*
|
|
5781
|
-
*
|
|
5782
|
-
*
|
|
5783
|
-
*
|
|
5784
|
-
*
|
|
5785
|
-
*
|
|
5786
|
-
*
|
|
5787
|
-
*
|
|
5788
|
-
*
|
|
5772
|
+
* (`EngineSettings.signSegmentsWithCdnToken`). The fast path signs each segment
|
|
5773
|
+
* with the grant carried by its OWN parent playlist — read from
|
|
5774
|
+
* `context.frag.baseurl`, the object hls.js itself parsed that fragment from.
|
|
5775
|
+
*
|
|
5776
|
+
* Token expiry model (CDN-04, D-07, revised):
|
|
5777
|
+
*
|
|
5778
|
+
* `frag.baseurl` is a string frozen at the moment hls.js parsed the level
|
|
5779
|
+
* playlist. Live playlists reload on their own cadence, so their fragments pick
|
|
5780
|
+
* up refreshed grants for free — reload IS the refresh path. VOD playlists are
|
|
5781
|
+
* parsed exactly once by hls.js 1.6.10 (its level controller only (re)requests a
|
|
5782
|
+
* playlist it has none of, or one that is live), so for VOD that string never
|
|
5783
|
+
* changes. Pure transfer alone therefore pins a VOD asset's segments to the
|
|
5784
|
+
* first grant for the asset's entire duration, and every segment 403s once that
|
|
5785
|
+
* grant lapses — the condition CONTEXT.md named as D-07's revisit trigger, since
|
|
5786
|
+
* observed in production (a ~294s grant against a ~26min asset).
|
|
5787
|
+
*
|
|
5788
|
+
* So when the inherited grant is inside the manager's expiry margin, this module
|
|
5789
|
+
* now defers to the shared grant via `updateUrlWithTokenParams` rather than
|
|
5790
|
+
* copying a dead string.
|
|
5791
|
+
*
|
|
5792
|
+
* Why sharing a grant across renditions is safe (supersedes this module's
|
|
5793
|
+
* previous prohibition): the token service derives `token_path` from the
|
|
5794
|
+
* directory of the URL it signs, and `token_path` is a PREFIX scope. An
|
|
5795
|
+
* asset-level grant returns 200 on every rendition beneath it — verified live
|
|
5796
|
+
* across all four renditions of a real asset. The hazard this module previously
|
|
5797
|
+
* guarded against — one rendition's grant handed to another — is real ONLY for a
|
|
5798
|
+
* rendition-scoped grant, and that is prevented at its source by
|
|
5799
|
+
* `CDNTokenManager.sourceUrl`, which pins acquisition of the shared grant to the
|
|
5800
|
+
* asset-level source URL. The invariant is therefore "the shared grant must be
|
|
5801
|
+
* asset-level", not "segments must never refresh".
|
|
5802
|
+
*
|
|
5803
|
+
* Still true: no timer, interval, or scheduled re-fetch belongs in this module.
|
|
5804
|
+
* Refresh here is demand-driven, on the load that needs it.
|
|
5789
5805
|
*/
|
|
5790
5806
|
// ---------------------------------------------------------------------------
|
|
5791
5807
|
// Supported token-type family (D-03) — query-param tokens only. Akamai's
|
|
@@ -5851,6 +5867,23 @@
|
|
|
5851
5867
|
return segmentUrl;
|
|
5852
5868
|
}
|
|
5853
5869
|
}
|
|
5870
|
+
/**
|
|
5871
|
+
* Reads the `expires` value off a parent playlist URL, or null when the URL
|
|
5872
|
+
* carries no token at all or cannot be parsed. Used to decide between the pure
|
|
5873
|
+
* transfer fast path and a shared-grant refresh.
|
|
5874
|
+
*/
|
|
5875
|
+
function readPlaylistGrantExpiry(parentPlaylistUrl) {
|
|
5876
|
+
try {
|
|
5877
|
+
const playlistUrl = new URL(parentPlaylistUrl);
|
|
5878
|
+
const token = playlistUrl.searchParams.get('token');
|
|
5879
|
+
if (!token)
|
|
5880
|
+
return { hasToken: false, expires: null };
|
|
5881
|
+
return { hasToken: true, expires: playlistUrl.searchParams.get('expires') };
|
|
5882
|
+
}
|
|
5883
|
+
catch {
|
|
5884
|
+
return { hasToken: false, expires: null };
|
|
5885
|
+
}
|
|
5886
|
+
}
|
|
5854
5887
|
/**
|
|
5855
5888
|
* Returns a loader constructor extending `BaseLoader`: construct the base,
|
|
5856
5889
|
* bind the inherited `load`, then assign an own `load` property that reads
|
|
@@ -5905,8 +5938,36 @@
|
|
|
5905
5938
|
originalLoad(context, loadConfig, callbacks);
|
|
5906
5939
|
return;
|
|
5907
5940
|
}
|
|
5908
|
-
|
|
5909
|
-
|
|
5941
|
+
const { hasToken, expires } = readPlaylistGrantExpiry(baseUrl);
|
|
5942
|
+
// Parent playlist carried no grant: nothing to transfer, and nothing to
|
|
5943
|
+
// refresh — an unsigned playlist means this path is not token-protected.
|
|
5944
|
+
if (!hasToken) {
|
|
5945
|
+
originalLoad(context, loadConfig, callbacks);
|
|
5946
|
+
return;
|
|
5947
|
+
}
|
|
5948
|
+
// Fast path: the inherited grant is still good. Pure synchronous transfer,
|
|
5949
|
+
// no manager involvement, no network.
|
|
5950
|
+
if (!tokenSource.isTokenExpired(expires)) {
|
|
5951
|
+
context.url = signSegmentUrlFromPlaylist(context.url, baseUrl);
|
|
5952
|
+
originalLoad(context, loadConfig, callbacks);
|
|
5953
|
+
return;
|
|
5954
|
+
}
|
|
5955
|
+
// Inherited grant is expired or inside the expiry margin. For VOD this is
|
|
5956
|
+
// terminal — hls.js will never re-parse the playlist, so the frozen
|
|
5957
|
+
// baseurl can never recover on its own. Defer to the shared asset-level
|
|
5958
|
+
// grant, which `CDNTokenManager.sourceUrl` keeps broad enough to cover
|
|
5959
|
+
// this segment.
|
|
5960
|
+
tokenSource.updateUrlWithTokenParams({ url: context.url })
|
|
5961
|
+
.then((updatedUrl) => {
|
|
5962
|
+
context.url = updatedUrl;
|
|
5963
|
+
originalLoad(context, loadConfig, callbacks);
|
|
5964
|
+
})
|
|
5965
|
+
.catch(() => {
|
|
5966
|
+
// Token error: fall back to the inherited grant rather than emitting an
|
|
5967
|
+
// unsigned request, and let the edge's 403 drive hls.js's own retry.
|
|
5968
|
+
context.url = signSegmentUrlFromPlaylist(context.url, baseUrl);
|
|
5969
|
+
originalLoad(context, loadConfig, callbacks);
|
|
5970
|
+
});
|
|
5910
5971
|
};
|
|
5911
5972
|
}
|
|
5912
5973
|
function SegmentTokenLoader(loaderConfig) {
|
|
@@ -6279,6 +6340,11 @@
|
|
|
6279
6340
|
// stream URL is loaded rather than config.src which always points at the main stream.
|
|
6280
6341
|
let src = this.loadSourceUrl || config.src || '';
|
|
6281
6342
|
if (this.tokenManager && src) {
|
|
6343
|
+
// Pin shared-grant acquisition to the asset-level source URL before the
|
|
6344
|
+
// first fetch. The token service scopes `token_path` to the directory of
|
|
6345
|
+
// the URL it signs, so a grant minted against a level playlist (as an ABR
|
|
6346
|
+
// switch would trigger) is rendition-scoped and 403s on every sibling.
|
|
6347
|
+
this.tokenManager.sourceUrl = src;
|
|
6282
6348
|
src = await this.tokenManager.updateUrlWithTokenParams({ url: src });
|
|
6283
6349
|
// Guard: abort if detached during token URL update
|
|
6284
6350
|
if (this.detached) {
|