eb-player 2.0.21 → 2.1.1
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 +217 -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 +130 -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.
|
|
7
|
+
var __EB_PLAYER_VERSION__ = "2.1.1";
|
|
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,175 @@
|
|
|
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 constructor extending `BaseLoader`: construct the base,
|
|
5856
|
+
* bind the inherited `load`, then assign an own `load` property that reads
|
|
5857
|
+
* `context.frag?.baseurl`.
|
|
5858
|
+
*
|
|
5859
|
+
* NOT a native `class`: this constructor is registered in the base
|
|
5860
|
+
* `config.loader` slot, which the EasyBroadcast P2P SDK's hls adapter
|
|
5861
|
+
* subclasses with transpiled ES5 inheritance — its wrapper constructor
|
|
5862
|
+
* invokes the parent as `Parent.apply(this, arguments)`, which throws
|
|
5863
|
+
* "Class constructor ... cannot be invoked without 'new'" on a native
|
|
5864
|
+
* class (hls.js's own dist loaders are function-based for the same
|
|
5865
|
+
* reason). The function below therefore supports both invocation modes:
|
|
5866
|
+
* under `new` (hls.js pLoader/fLoader/loader slots) it defers to
|
|
5867
|
+
* `Reflect.construct` so a native-class base still works; under an
|
|
5868
|
+
* `.apply()`-style super call it initializes the wrapper's `this` in
|
|
5869
|
+
* place, which requires the base to be function-based — true of every
|
|
5870
|
+
* hls.js dist build this player loads.
|
|
5871
|
+
*
|
|
5872
|
+
* `tokenSource` is captured by closure — never stored on `this` and never
|
|
5873
|
+
* used to acquire or refresh a token.
|
|
5874
|
+
*/
|
|
5875
|
+
function createSegmentTokenLoaderClass(BaseLoader, tokenSource) {
|
|
5876
|
+
// WR-02: the "unsupported token type" diagnostic is logged to the console
|
|
5877
|
+
// at most once per loader CLASS — i.e. once per engine session that calls
|
|
5878
|
+
// createSegmentTokenLoaderClass, since hls.js constructs a fresh loader
|
|
5879
|
+
// instance per fragment load and an instance-level flag would reset every
|
|
5880
|
+
// load. `callbacks.onError` still fires on every load below, unguarded —
|
|
5881
|
+
// hls.js needs the error signal each time to drive its own retry/fatal
|
|
5882
|
+
// escalation; only the console spam is deduplicated.
|
|
5883
|
+
let unsupportedTokenTypeLogged = false;
|
|
5884
|
+
function attachSigningLoad(instance) {
|
|
5885
|
+
const originalLoad = instance.load.bind(instance);
|
|
5886
|
+
instance.load = function (context, loadConfig, callbacks) {
|
|
5887
|
+
// D-03/D-04: a token type outside the query-param family (akamai, or
|
|
5888
|
+
// none configured) must fail visibly rather than emit an unsigned
|
|
5889
|
+
// request. A silent passthrough here is the same undiagnosable 403
|
|
5890
|
+
// from the edge that D-04 forbids, so it gets the same treatment as
|
|
5891
|
+
// the akamai case: no delegation to the base loader, and a named
|
|
5892
|
+
// error surfaced through hls.js's own error callback.
|
|
5893
|
+
const tokenType = tokenSource.tokenType;
|
|
5894
|
+
if (!tokenType || !SEGMENT_SIGNING_TOKEN_TYPES.includes(tokenType)) {
|
|
5895
|
+
const message = segmentSigningUnsupportedMessage(tokenType);
|
|
5896
|
+
if (!unsupportedTokenTypeLogged) {
|
|
5897
|
+
unsupportedTokenTypeLogged = true;
|
|
5898
|
+
console.error(message);
|
|
5899
|
+
}
|
|
5900
|
+
callbacks.onError({ code: 0, text: message }, context, null, createEmptySegmentLoaderStats());
|
|
5901
|
+
return;
|
|
5902
|
+
}
|
|
5903
|
+
const baseUrl = context.frag?.baseurl;
|
|
5904
|
+
if (!baseUrl || !context.url) {
|
|
5905
|
+
originalLoad(context, loadConfig, callbacks);
|
|
5906
|
+
return;
|
|
5907
|
+
}
|
|
5908
|
+
context.url = signSegmentUrlFromPlaylist(context.url, baseUrl);
|
|
5909
|
+
originalLoad(context, loadConfig, callbacks);
|
|
5910
|
+
};
|
|
5911
|
+
}
|
|
5912
|
+
function SegmentTokenLoader(loaderConfig) {
|
|
5913
|
+
if (new.target) {
|
|
5914
|
+
const instance = Reflect.construct(BaseLoader, [loaderConfig], new.target);
|
|
5915
|
+
attachSigningLoad(instance);
|
|
5916
|
+
return instance;
|
|
5917
|
+
}
|
|
5918
|
+
BaseLoader.call(this, loaderConfig);
|
|
5919
|
+
attachSigningLoad(this);
|
|
5920
|
+
return undefined;
|
|
5921
|
+
}
|
|
5922
|
+
SegmentTokenLoader.prototype = Object.create(BaseLoader.prototype);
|
|
5923
|
+
SegmentTokenLoader.prototype.constructor = SegmentTokenLoader;
|
|
5924
|
+
return SegmentTokenLoader;
|
|
5925
|
+
}
|
|
5926
|
+
|
|
5750
5927
|
/**
|
|
5751
5928
|
* HlsEngine — HLS playback engine.
|
|
5752
5929
|
*
|
|
@@ -5930,6 +6107,7 @@
|
|
|
5930
6107
|
const hlsSafeSettings = { ...hlsEngineSettings };
|
|
5931
6108
|
delete hlsSafeSettings['extraParamsCallback'];
|
|
5932
6109
|
delete hlsSafeSettings['onCDNTokenError'];
|
|
6110
|
+
delete hlsSafeSettings['signSegmentsWithCdnToken'];
|
|
5933
6111
|
const driverConfig = {
|
|
5934
6112
|
autoStartLoad: !config.startAt,
|
|
5935
6113
|
enableWorker: true,
|
|
@@ -5973,6 +6151,24 @@
|
|
|
5973
6151
|
}
|
|
5974
6152
|
}
|
|
5975
6153
|
driverConfig.pLoader = PLoader;
|
|
6154
|
+
// Opt-in (D-06): sign segment requests with the grant carried by their
|
|
6155
|
+
// own parent playlist. Off by default so segment request shapes stay
|
|
6156
|
+
// byte-identical to today's when the flag is unset.
|
|
6157
|
+
if (config.engineSettings.signSegmentsWithCdnToken === true) {
|
|
6158
|
+
const SegmentTokenLoader = createSegmentTokenLoaderClass(Hls.DefaultConfig.loader, tm);
|
|
6159
|
+
driverConfig.fLoader = SegmentTokenLoader;
|
|
6160
|
+
// The base `loader` slot must sign too: the EasyBroadcast P2P SDK's
|
|
6161
|
+
// hls adapter replaces `config.fLoader` at integrate() time with a
|
|
6162
|
+
// wrapper class that extends `config.loader` and overrides only
|
|
6163
|
+
// loadInternal(), routing segment fetches through its own
|
|
6164
|
+
// resource manager (V2V + CDN-fallback fetch of context.url
|
|
6165
|
+
// verbatim). Signing lives in load(), which the wrapper inherits,
|
|
6166
|
+
// so every URL entering that path — and hls.js's key loader, which
|
|
6167
|
+
// also instantiates `config.loader` — is signed before any request
|
|
6168
|
+
// is issued. Without this, V2V CDN fallbacks go out unsigned (403
|
|
6169
|
+
// on token-protected origins).
|
|
6170
|
+
driverConfig.loader = SegmentTokenLoader;
|
|
6171
|
+
}
|
|
5976
6172
|
}
|
|
5977
6173
|
// Inject custom ABR controller unless disabled
|
|
5978
6174
|
if (!config.disableCustomAbr) {
|
|
@@ -6426,7 +6622,12 @@
|
|
|
6426
6622
|
* Ported from component/engines/dash.js
|
|
6427
6623
|
*
|
|
6428
6624
|
* Extends BaseEngine to bridge dash.js MediaPlayer events to PlayerState.
|
|
6429
|
-
* Uses CDN loader
|
|
6625
|
+
* Uses the CDN loader and StallWatchdog, plus DASH retry settings and ABR rules.
|
|
6626
|
+
*
|
|
6627
|
+
* CDN token signing (manifests and segments alike) is HLS-only by design —
|
|
6628
|
+
* this engine has no `CDNTokenManager` integration at all (Phase 08.1 / D-05).
|
|
6629
|
+
* DASH segments are `.m4s`, so nothing written for HLS's fragment-loader
|
|
6630
|
+
* token signing should be assumed to generalise here.
|
|
6430
6631
|
*
|
|
6431
6632
|
* CRITICAL: driver instance is a private class field — NEVER stored in PlayerState.
|
|
6432
6633
|
*/
|
|
@@ -6867,18 +7068,30 @@
|
|
|
6867
7068
|
}
|
|
6868
7069
|
PLoader = SnapshotPLoader;
|
|
6869
7070
|
}
|
|
7071
|
+
// Build fLoader class with segment CDN token signing — opt-in (D-06), off by
|
|
7072
|
+
// default so segment request shapes stay byte-identical to today's when the
|
|
7073
|
+
// flag is unset. Reuses the shared factory built for the main engine's
|
|
7074
|
+
// fragment loader (`segment-token-loader.ts`) rather than re-implementing the
|
|
7075
|
+
// signing logic here. tokenManager is the same closure-captured reference
|
|
7076
|
+
// used above for pLoader (Pitfall 6) — never read off `this`.
|
|
7077
|
+
let FLoader;
|
|
7078
|
+
if (tokenManager && this.config.engineSettings?.signSegmentsWithCdnToken === true) {
|
|
7079
|
+
FLoader = createSegmentTokenLoaderClass(HlsConstructor.DefaultConfig.loader, tokenManager);
|
|
7080
|
+
}
|
|
6870
7081
|
// Strip player-specific keys that are NOT hls.js config options — they contain
|
|
6871
7082
|
// non-serializable functions that cause DataCloneError when hls.js posts config to its worker.
|
|
6872
7083
|
const rawSettings = { ...(this.config.engineSettings ?? {}) };
|
|
6873
7084
|
delete rawSettings['extraParamsCallback'];
|
|
6874
7085
|
delete rawSettings['onCDNTokenError'];
|
|
7086
|
+
delete rawSettings['signSegmentsWithCdnToken'];
|
|
6875
7087
|
const driverConfig = {
|
|
6876
7088
|
startLevel: 0,
|
|
6877
7089
|
enableWebVTT: false,
|
|
6878
7090
|
enableWorker: false,
|
|
6879
7091
|
maxBufferLength: 1,
|
|
6880
7092
|
...rawSettings,
|
|
6881
|
-
...(PLoader ? { pLoader: PLoader } : {})
|
|
7093
|
+
...(PLoader ? { pLoader: PLoader } : {}),
|
|
7094
|
+
...(FLoader ? { fLoader: FLoader } : {})
|
|
6882
7095
|
};
|
|
6883
7096
|
const driver = new HlsConstructor(driverConfig);
|
|
6884
7097
|
this.driver = driver;
|