eb-player 2.0.21 → 2.1.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/build/ebplayer.bundle.js +183 -4
- package/dist/build/ebplayer.bundle.js.map +1 -1
- package/dist/build/types/src/core/config.d.ts +8 -0
- package/dist/build/types/src/core/config.d.ts.map +1 -1
- package/dist/build/types/src/engines/cdn-token-manager.d.ts +1 -1
- package/dist/build/types/src/engines/cdn-token-manager.d.ts.map +1 -1
- package/dist/build/types/src/engines/dash.d.ts +6 -1
- package/dist/build/types/src/engines/dash.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 +117 -0
- package/dist/build/types/src/engines/segment-token-loader.d.ts.map +1 -0
- package/dist/build/types/src/engines/snapshot/hls.d.ts +4 -16
- package/dist/build/types/src/engines/snapshot/hls.d.ts.map +1 -1
- package/dist/build/types/src/integrations/ads-manager.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.0
|
|
7
|
+
var __EB_PLAYER_VERSION__ = "2.1.0";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Finite State Machine for player playback state transitions.
|
|
@@ -3992,7 +3992,15 @@
|
|
|
3992
3992
|
// sequential-await interaction: fixing G-07-9 alone would otherwise
|
|
3993
3993
|
// install this listener only after the intro, behind the very click
|
|
3994
3994
|
// that started it).
|
|
3995
|
-
|
|
3995
|
+
//
|
|
3996
|
+
// WR-01: wired to localController.signal, not the outer signal. The outer
|
|
3997
|
+
// abort already propagates into localController (above), so this still
|
|
3998
|
+
// covers player disposal — but it additionally lets completeAdLeg() settle
|
|
3999
|
+
// a still-pending gesture wait. Without it, an ad leg completed by the
|
|
4000
|
+
// watchdog, an AD_ERROR or ALL_ADS_COMPLETED before any click leaves this
|
|
4001
|
+
// promise unsettled forever: init() never resolves and the capture-phase
|
|
4002
|
+
// click listener stays attached to .eb-player.
|
|
4003
|
+
const gesturePromise = config.autoplay ? null : this.waitForUserGesture(video, localController.signal);
|
|
3996
4004
|
// G-07-9: the decision is derived from configuration, not from the
|
|
3997
4005
|
// PlayerState intro-playing flag. An async function body runs
|
|
3998
4006
|
// synchronously up to its first await, so reading that flag here would
|
|
@@ -5747,6 +5755,153 @@
|
|
|
5747
5755
|
};
|
|
5748
5756
|
}
|
|
5749
5757
|
|
|
5758
|
+
/**
|
|
5759
|
+
* Segment (fragment) CDN token signing — shared primitive.
|
|
5760
|
+
*
|
|
5761
|
+
* Extends CDN token signing from manifests to segments, behind an opt-in flag
|
|
5762
|
+
* (`EngineSettings.signSegmentsWithCdnToken`). Each segment is signed with the
|
|
5763
|
+
* grant carried by its OWN parent playlist — read from `context.frag.baseurl`,
|
|
5764
|
+
* the object hls.js itself parsed that fragment from — never from any store,
|
|
5765
|
+
* cache, or shared manager state. This makes cross-rendition mis-signing
|
|
5766
|
+
* structurally impossible rather than merely unlikely.
|
|
5767
|
+
*
|
|
5768
|
+
* CRITICAL: this module must never acquire or refresh a token. It only reads
|
|
5769
|
+
* data already present on the fragment context handed to it by hls.js. Any
|
|
5770
|
+
* acquisition call here would reintroduce the single-slot cache reuse in
|
|
5771
|
+
* `CDNTokenManager.lastTokenResponse`, which hands one rendition's grant to
|
|
5772
|
+
* another — the exact failure this design forbids.
|
|
5773
|
+
*
|
|
5774
|
+
* Token expiry model (CDN-04, D-07): a segment carries whatever grant its
|
|
5775
|
+
* parent playlist carried when hls.js parsed it. Live playlists reload on
|
|
5776
|
+
* their own cadence, so their fragments pick up refreshed grants with no
|
|
5777
|
+
* refresh machinery here — reload IS the refresh path. VOD playlists are
|
|
5778
|
+
* parsed exactly once by hls.js 1.6.10 (its level controller only
|
|
5779
|
+
* (re)requests a playlist it has none of, or one that is live), so a VOD
|
|
5780
|
+
* asset's segment grants are frozen at first parse for the asset's entire
|
|
5781
|
+
* duration. If a token's lifetime is shorter than the asset's duration, later
|
|
5782
|
+
* segments will be rejected by the edge and surface through the existing
|
|
5783
|
+
* error/retry path. This is an accepted operational constraint — the token
|
|
5784
|
+
* service's lifetime must exceed the longest VOD asset — not an oversight;
|
|
5785
|
+
* D-07 chose reload-cadence refresh and CONTEXT.md defers a proactive
|
|
5786
|
+
* near-expiry refresh path with a named revisit condition (a real asset shown
|
|
5787
|
+
* to outlive its grant). No timer, interval, or scheduled re-fetch belongs in
|
|
5788
|
+
* this module.
|
|
5789
|
+
*/
|
|
5790
|
+
// ---------------------------------------------------------------------------
|
|
5791
|
+
// Supported token-type family (D-03) — query-param tokens only. Akamai's
|
|
5792
|
+
// path-segment mechanic (`appendAkamaiTokenParams`) is a different shape and
|
|
5793
|
+
// deliberately not extended here.
|
|
5794
|
+
// ---------------------------------------------------------------------------
|
|
5795
|
+
const SEGMENT_SIGNING_TOKEN_TYPES = Object.freeze([
|
|
5796
|
+
TOKEN_TYPES.EASY_B,
|
|
5797
|
+
TOKEN_TYPES.VENOM,
|
|
5798
|
+
TOKEN_TYPES.BUNNY
|
|
5799
|
+
]);
|
|
5800
|
+
/**
|
|
5801
|
+
* Builds the single diagnostic string used for every unsupported-token-type
|
|
5802
|
+
* case. Names the flag and the offending token type (or says none is
|
|
5803
|
+
* configured) and lists the supported ones. Never includes a token value.
|
|
5804
|
+
*/
|
|
5805
|
+
function segmentSigningUnsupportedMessage(tokenType) {
|
|
5806
|
+
const supported = SEGMENT_SIGNING_TOKEN_TYPES.join(', ');
|
|
5807
|
+
const described = tokenType === undefined ? 'no token type configured' : `token type "${tokenType}"`;
|
|
5808
|
+
return `signSegmentsWithCdnToken: segment signing is not supported for ${described}. Supported token types: ${supported}.`;
|
|
5809
|
+
}
|
|
5810
|
+
/**
|
|
5811
|
+
* A fresh, all-zero `LoaderStats`-shaped object for the fail-loud path's
|
|
5812
|
+
* `onError` call. hls.js's own error-handling code reads fields off this
|
|
5813
|
+
* object (e.g. `stats.loaded`); passing `undefined` risks a `TypeError`
|
|
5814
|
+
* inside hls.js itself instead of the clean error this module intends to
|
|
5815
|
+
* produce. Returns a new object per call — never shared/mutated state — so
|
|
5816
|
+
* nothing downstream can observe stale values from a previous failed load.
|
|
5817
|
+
*/
|
|
5818
|
+
function createEmptySegmentLoaderStats() {
|
|
5819
|
+
return {
|
|
5820
|
+
aborted: false,
|
|
5821
|
+
loaded: 0,
|
|
5822
|
+
total: 0,
|
|
5823
|
+
retry: 0,
|
|
5824
|
+
chunkCount: 0,
|
|
5825
|
+
bwEstimate: 0,
|
|
5826
|
+
loading: { start: 0, first: 0, end: 0 },
|
|
5827
|
+
parsing: { start: 0, end: 0 },
|
|
5828
|
+
buffering: { start: 0, first: 0, end: 0 }
|
|
5829
|
+
};
|
|
5830
|
+
}
|
|
5831
|
+
/**
|
|
5832
|
+
* Pure transfer: reads `token`, `expires` and `token_path` off the parent
|
|
5833
|
+
* playlist URL's query string and reapplies them to the segment URL via the
|
|
5834
|
+
* same static primitive that signs manifests, so a signed segment URL is
|
|
5835
|
+
* shaped exactly like a signed manifest URL. Returns the segment URL
|
|
5836
|
+
* unchanged when the playlist carries no token, or when either URL cannot be
|
|
5837
|
+
* parsed (a thrown exception inside a loader is a dead player, not a signing
|
|
5838
|
+
* failure).
|
|
5839
|
+
*/
|
|
5840
|
+
function signSegmentUrlFromPlaylist(segmentUrl, parentPlaylistUrl) {
|
|
5841
|
+
try {
|
|
5842
|
+
const playlistUrl = new URL(parentPlaylistUrl);
|
|
5843
|
+
const token = playlistUrl.searchParams.get('token');
|
|
5844
|
+
if (!token)
|
|
5845
|
+
return segmentUrl;
|
|
5846
|
+
const expires = playlistUrl.searchParams.get('expires');
|
|
5847
|
+
const tokenPath = playlistUrl.searchParams.get('token_path');
|
|
5848
|
+
return CDNTokenManager.appendTokenParams(segmentUrl, token, expires, tokenPath);
|
|
5849
|
+
}
|
|
5850
|
+
catch {
|
|
5851
|
+
return segmentUrl;
|
|
5852
|
+
}
|
|
5853
|
+
}
|
|
5854
|
+
/**
|
|
5855
|
+
* Returns a loader class extending `BaseLoader`, built exactly like the
|
|
5856
|
+
* existing playlist loader: call `super()`, bind the inherited `load`, then
|
|
5857
|
+
* assign an own `load` property that reads `context.frag?.baseurl`.
|
|
5858
|
+
*
|
|
5859
|
+
* `tokenSource` is captured by closure — never stored on `this` and never
|
|
5860
|
+
* used to acquire or refresh a token.
|
|
5861
|
+
*/
|
|
5862
|
+
function createSegmentTokenLoaderClass(BaseLoader, tokenSource) {
|
|
5863
|
+
// WR-02: the "unsupported token type" diagnostic is logged to the console
|
|
5864
|
+
// at most once per loader CLASS — i.e. once per engine session that calls
|
|
5865
|
+
// createSegmentTokenLoaderClass, since hls.js constructs a fresh loader
|
|
5866
|
+
// instance per fragment load and an instance-level flag would reset every
|
|
5867
|
+
// load. `callbacks.onError` still fires on every load below, unguarded —
|
|
5868
|
+
// hls.js needs the error signal each time to drive its own retry/fatal
|
|
5869
|
+
// escalation; only the console spam is deduplicated.
|
|
5870
|
+
let unsupportedTokenTypeLogged = false;
|
|
5871
|
+
class SegmentTokenLoader extends BaseLoader {
|
|
5872
|
+
constructor(loaderConfig) {
|
|
5873
|
+
super(loaderConfig);
|
|
5874
|
+
const originalLoad = this.load.bind(this);
|
|
5875
|
+
this.load = function (context, loadConfig, callbacks) {
|
|
5876
|
+
// D-03/D-04: a token type outside the query-param family (akamai, or
|
|
5877
|
+
// none configured) must fail visibly rather than emit an unsigned
|
|
5878
|
+
// request. A silent passthrough here is the same undiagnosable 403
|
|
5879
|
+
// from the edge that D-04 forbids, so it gets the same treatment as
|
|
5880
|
+
// the akamai case: no delegation to the base loader, and a named
|
|
5881
|
+
// error surfaced through hls.js's own error callback.
|
|
5882
|
+
const tokenType = tokenSource.tokenType;
|
|
5883
|
+
if (!tokenType || !SEGMENT_SIGNING_TOKEN_TYPES.includes(tokenType)) {
|
|
5884
|
+
const message = segmentSigningUnsupportedMessage(tokenType);
|
|
5885
|
+
if (!unsupportedTokenTypeLogged) {
|
|
5886
|
+
unsupportedTokenTypeLogged = true;
|
|
5887
|
+
console.error(message);
|
|
5888
|
+
}
|
|
5889
|
+
callbacks.onError({ code: 0, text: message }, context, null, createEmptySegmentLoaderStats());
|
|
5890
|
+
return;
|
|
5891
|
+
}
|
|
5892
|
+
const baseUrl = context.frag?.baseurl;
|
|
5893
|
+
if (!baseUrl || !context.url) {
|
|
5894
|
+
originalLoad(context, loadConfig, callbacks);
|
|
5895
|
+
return;
|
|
5896
|
+
}
|
|
5897
|
+
context.url = signSegmentUrlFromPlaylist(context.url, baseUrl);
|
|
5898
|
+
originalLoad(context, loadConfig, callbacks);
|
|
5899
|
+
};
|
|
5900
|
+
}
|
|
5901
|
+
}
|
|
5902
|
+
return SegmentTokenLoader;
|
|
5903
|
+
}
|
|
5904
|
+
|
|
5750
5905
|
/**
|
|
5751
5906
|
* HlsEngine — HLS playback engine.
|
|
5752
5907
|
*
|
|
@@ -5930,6 +6085,7 @@
|
|
|
5930
6085
|
const hlsSafeSettings = { ...hlsEngineSettings };
|
|
5931
6086
|
delete hlsSafeSettings['extraParamsCallback'];
|
|
5932
6087
|
delete hlsSafeSettings['onCDNTokenError'];
|
|
6088
|
+
delete hlsSafeSettings['signSegmentsWithCdnToken'];
|
|
5933
6089
|
const driverConfig = {
|
|
5934
6090
|
autoStartLoad: !config.startAt,
|
|
5935
6091
|
enableWorker: true,
|
|
@@ -5973,6 +6129,12 @@
|
|
|
5973
6129
|
}
|
|
5974
6130
|
}
|
|
5975
6131
|
driverConfig.pLoader = PLoader;
|
|
6132
|
+
// Opt-in (D-06): sign segment requests with the grant carried by their
|
|
6133
|
+
// own parent playlist. Off by default so segment request shapes stay
|
|
6134
|
+
// byte-identical to today's when the flag is unset.
|
|
6135
|
+
if (config.engineSettings.signSegmentsWithCdnToken === true) {
|
|
6136
|
+
driverConfig.fLoader = createSegmentTokenLoaderClass(Hls.DefaultConfig.loader, tm);
|
|
6137
|
+
}
|
|
5976
6138
|
}
|
|
5977
6139
|
// Inject custom ABR controller unless disabled
|
|
5978
6140
|
if (!config.disableCustomAbr) {
|
|
@@ -6426,7 +6588,12 @@
|
|
|
6426
6588
|
* Ported from component/engines/dash.js
|
|
6427
6589
|
*
|
|
6428
6590
|
* Extends BaseEngine to bridge dash.js MediaPlayer events to PlayerState.
|
|
6429
|
-
* Uses CDN loader
|
|
6591
|
+
* Uses the CDN loader and StallWatchdog, plus DASH retry settings and ABR rules.
|
|
6592
|
+
*
|
|
6593
|
+
* CDN token signing (manifests and segments alike) is HLS-only by design —
|
|
6594
|
+
* this engine has no `CDNTokenManager` integration at all (Phase 08.1 / D-05).
|
|
6595
|
+
* DASH segments are `.m4s`, so nothing written for HLS's fragment-loader
|
|
6596
|
+
* token signing should be assumed to generalise here.
|
|
6430
6597
|
*
|
|
6431
6598
|
* CRITICAL: driver instance is a private class field — NEVER stored in PlayerState.
|
|
6432
6599
|
*/
|
|
@@ -6867,18 +7034,30 @@
|
|
|
6867
7034
|
}
|
|
6868
7035
|
PLoader = SnapshotPLoader;
|
|
6869
7036
|
}
|
|
7037
|
+
// Build fLoader class with segment CDN token signing — opt-in (D-06), off by
|
|
7038
|
+
// default so segment request shapes stay byte-identical to today's when the
|
|
7039
|
+
// flag is unset. Reuses the shared factory built for the main engine's
|
|
7040
|
+
// fragment loader (`segment-token-loader.ts`) rather than re-implementing the
|
|
7041
|
+
// signing logic here. tokenManager is the same closure-captured reference
|
|
7042
|
+
// used above for pLoader (Pitfall 6) — never read off `this`.
|
|
7043
|
+
let FLoader;
|
|
7044
|
+
if (tokenManager && this.config.engineSettings?.signSegmentsWithCdnToken === true) {
|
|
7045
|
+
FLoader = createSegmentTokenLoaderClass(HlsConstructor.DefaultConfig.loader, tokenManager);
|
|
7046
|
+
}
|
|
6870
7047
|
// Strip player-specific keys that are NOT hls.js config options — they contain
|
|
6871
7048
|
// non-serializable functions that cause DataCloneError when hls.js posts config to its worker.
|
|
6872
7049
|
const rawSettings = { ...(this.config.engineSettings ?? {}) };
|
|
6873
7050
|
delete rawSettings['extraParamsCallback'];
|
|
6874
7051
|
delete rawSettings['onCDNTokenError'];
|
|
7052
|
+
delete rawSettings['signSegmentsWithCdnToken'];
|
|
6875
7053
|
const driverConfig = {
|
|
6876
7054
|
startLevel: 0,
|
|
6877
7055
|
enableWebVTT: false,
|
|
6878
7056
|
enableWorker: false,
|
|
6879
7057
|
maxBufferLength: 1,
|
|
6880
7058
|
...rawSettings,
|
|
6881
|
-
...(PLoader ? { pLoader: PLoader } : {})
|
|
7059
|
+
...(PLoader ? { pLoader: PLoader } : {}),
|
|
7060
|
+
...(FLoader ? { fLoader: FLoader } : {})
|
|
6882
7061
|
};
|
|
6883
7062
|
const driver = new HlsConstructor(driverConfig);
|
|
6884
7063
|
this.driver = driver;
|