@guardvideo/player-sdk 3.5.0 → 3.7.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/core/player.d.ts +6 -0
- package/dist/core/player.d.ts.map +1 -1
- package/dist/core/types.d.ts +1 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.esm.js +116 -13
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +116 -13
- package/dist/index.js.map +1 -1
- package/dist/react/GuardVideoPlayer.d.ts.map +1 -1
- package/dist/ui/PlayerUI.d.ts +5 -0
- package/dist/ui/PlayerUI.d.ts.map +1 -1
- package/dist/vanilla/guardvideo-player.js +115 -13
- package/dist/vanilla/guardvideo-player.js.map +1 -1
- package/dist/vanilla/guardvideo-player.min.js +1 -1
- package/dist/vanilla/guardvideo-player.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -37077,6 +37077,10 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37077
37077
|
this.MAX_BACKOFF = 30000;
|
|
37078
37078
|
this.rateLimitCooldownUntil = 0;
|
|
37079
37079
|
this.nonceRefreshPromise = null;
|
|
37080
|
+
this.destroyed = false;
|
|
37081
|
+
this.networkRetryCount = 0;
|
|
37082
|
+
this.MAX_NETWORK_RETRIES = 6;
|
|
37083
|
+
this.pendingRetryTimer = null;
|
|
37080
37084
|
this._onRateChange = this.enforceMaxRate.bind(this);
|
|
37081
37085
|
this.videoElement = videoElement;
|
|
37082
37086
|
this.config = {
|
|
@@ -37353,6 +37357,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37353
37357
|
const data = await resp.json();
|
|
37354
37358
|
this.log('Playlist nonce acquired, expires in ' + data.expiresIn + 's');
|
|
37355
37359
|
this.retryBackoff = 1000;
|
|
37360
|
+
this.networkRetryCount = 0;
|
|
37356
37361
|
this.scheduleNonceRefresh(data.expiresIn);
|
|
37357
37362
|
return data.nonce;
|
|
37358
37363
|
}
|
|
@@ -37366,6 +37371,8 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37366
37371
|
clearTimeout(this.nonceRefreshTimer);
|
|
37367
37372
|
const refreshMs = Math.max(5000, expiresInSeconds * 750);
|
|
37368
37373
|
this.nonceRefreshTimer = setTimeout(() => {
|
|
37374
|
+
if (this.destroyed)
|
|
37375
|
+
return;
|
|
37369
37376
|
this.refreshNonce().catch(() => {
|
|
37370
37377
|
this.log('Proactive nonce refresh failed');
|
|
37371
37378
|
});
|
|
@@ -37394,6 +37401,24 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37394
37401
|
this.rateLimitCooldownUntil = Date.now() + this.retryBackoff;
|
|
37395
37402
|
this.retryBackoff = Math.min(this.retryBackoff * 2, this.MAX_BACKOFF);
|
|
37396
37403
|
}
|
|
37404
|
+
scheduleRetry(fn, delayMs) {
|
|
37405
|
+
if (this.pendingRetryTimer)
|
|
37406
|
+
clearTimeout(this.pendingRetryTimer);
|
|
37407
|
+
this.pendingRetryTimer = setTimeout(() => {
|
|
37408
|
+
this.pendingRetryTimer = null;
|
|
37409
|
+
if (!this.destroyed && this.hls)
|
|
37410
|
+
fn();
|
|
37411
|
+
}, delayMs);
|
|
37412
|
+
}
|
|
37413
|
+
stopLoading() {
|
|
37414
|
+
if (this.pendingRetryTimer) {
|
|
37415
|
+
clearTimeout(this.pendingRetryTimer);
|
|
37416
|
+
this.pendingRetryTimer = null;
|
|
37417
|
+
}
|
|
37418
|
+
this.hls?.stopLoad();
|
|
37419
|
+
this.networkRetryCount = 0;
|
|
37420
|
+
this.retryBackoff = 1000;
|
|
37421
|
+
}
|
|
37397
37422
|
async initializePlayer() {
|
|
37398
37423
|
if (!this.embedToken) {
|
|
37399
37424
|
throw new Error('No embed token available');
|
|
@@ -37457,6 +37482,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37457
37482
|
this.setState(exports.PlayerState.READY);
|
|
37458
37483
|
this.config.onReady();
|
|
37459
37484
|
this.retryBackoff = 1000;
|
|
37485
|
+
this.networkRetryCount = 0;
|
|
37460
37486
|
if (this.config.autoplay)
|
|
37461
37487
|
this.play();
|
|
37462
37488
|
});
|
|
@@ -37485,12 +37511,14 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37485
37511
|
details: data.details,
|
|
37486
37512
|
fatal: data.fatal,
|
|
37487
37513
|
});
|
|
37514
|
+
if (this.destroyed)
|
|
37515
|
+
return;
|
|
37488
37516
|
if (Date.now() < this.rateLimitCooldownUntil) {
|
|
37489
37517
|
this.log('Suppressing retry — rate limit cooldown active (' +
|
|
37490
37518
|
Math.ceil((this.rateLimitCooldownUntil - Date.now()) / 1000) + 's remaining)');
|
|
37491
37519
|
if (data.fatal) {
|
|
37492
37520
|
const delay = this.rateLimitCooldownUntil - Date.now() + 500;
|
|
37493
|
-
|
|
37521
|
+
this.scheduleRetry(() => this.hls?.startLoad(), delay);
|
|
37494
37522
|
}
|
|
37495
37523
|
return;
|
|
37496
37524
|
}
|
|
@@ -37498,7 +37526,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37498
37526
|
if (httpStatus === 429) {
|
|
37499
37527
|
this.enterRateLimitCooldown();
|
|
37500
37528
|
this.log('429 detected — entering cooldown for ' + this.retryBackoff + 'ms');
|
|
37501
|
-
|
|
37529
|
+
this.scheduleRetry(() => {
|
|
37502
37530
|
if (this.embedToken?.forensicWatermark) {
|
|
37503
37531
|
this.refreshNonce().then(() => this.hls?.startLoad()).catch(() => this.hls?.startLoad());
|
|
37504
37532
|
}
|
|
@@ -37511,11 +37539,17 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37511
37539
|
if (data.type === Hls.ErrorTypes.NETWORK_ERROR &&
|
|
37512
37540
|
data.details === Hls.ErrorDetails.LEVEL_LOAD_ERROR &&
|
|
37513
37541
|
this.embedToken?.forensicWatermark) {
|
|
37514
|
-
this.
|
|
37542
|
+
this.networkRetryCount++;
|
|
37543
|
+
if (this.networkRetryCount > this.MAX_NETWORK_RETRIES) {
|
|
37544
|
+
this.error('Max network retries exceeded for playlist load');
|
|
37545
|
+
this.handleError({ code: 'NETWORK_ERROR', message: 'Playlist load failed after multiple retries', fatal: true, details: data });
|
|
37546
|
+
return;
|
|
37547
|
+
}
|
|
37548
|
+
this.log('Playlist load failed — refreshing nonce before retry (' + this.networkRetryCount + '/' + this.MAX_NETWORK_RETRIES + ')');
|
|
37515
37549
|
this.refreshNonce().then(() => {
|
|
37516
|
-
|
|
37550
|
+
this.scheduleRetry(() => this.hls?.startLoad(), this.retryBackoff);
|
|
37517
37551
|
}).catch(() => {
|
|
37518
|
-
|
|
37552
|
+
this.scheduleRetry(() => this.hls?.startLoad(), this.retryBackoff);
|
|
37519
37553
|
});
|
|
37520
37554
|
this.retryBackoff = Math.min(this.retryBackoff * 1.5, this.MAX_BACKOFF);
|
|
37521
37555
|
return;
|
|
@@ -37523,11 +37557,17 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37523
37557
|
if (data.fatal) {
|
|
37524
37558
|
switch (data.type) {
|
|
37525
37559
|
case Hls.ErrorTypes.NETWORK_ERROR:
|
|
37526
|
-
this.
|
|
37560
|
+
this.networkRetryCount++;
|
|
37561
|
+
if (this.networkRetryCount > this.MAX_NETWORK_RETRIES) {
|
|
37562
|
+
this.error('Max network retries exceeded — giving up');
|
|
37563
|
+
this.handleError({ code: 'NETWORK_ERROR', message: 'Network error after multiple retries. Please check your connection.', fatal: true, details: data });
|
|
37564
|
+
return;
|
|
37565
|
+
}
|
|
37566
|
+
this.error('Network error, attempting recovery (' + this.networkRetryCount + '/' + this.MAX_NETWORK_RETRIES + ')...');
|
|
37527
37567
|
if (this.embedToken?.forensicWatermark) {
|
|
37528
37568
|
this.refreshNonce().catch(() => { });
|
|
37529
37569
|
}
|
|
37530
|
-
|
|
37570
|
+
this.scheduleRetry(() => this.hls?.startLoad(), this.retryBackoff);
|
|
37531
37571
|
this.retryBackoff = Math.min(this.retryBackoff * 1.5, this.MAX_BACKOFF);
|
|
37532
37572
|
break;
|
|
37533
37573
|
case Hls.ErrorTypes.MEDIA_ERROR:
|
|
@@ -37542,8 +37582,21 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37542
37582
|
this.setupVideoEventListeners();
|
|
37543
37583
|
}
|
|
37544
37584
|
setupVideoEventListeners() {
|
|
37545
|
-
this.videoElement.addEventListener('playing', () =>
|
|
37546
|
-
|
|
37585
|
+
this.videoElement.addEventListener('playing', () => {
|
|
37586
|
+
this.setState(exports.PlayerState.PLAYING);
|
|
37587
|
+
if (this.hls)
|
|
37588
|
+
this.hls.startLoad(-1);
|
|
37589
|
+
});
|
|
37590
|
+
this.videoElement.addEventListener('pause', () => {
|
|
37591
|
+
this.setState(exports.PlayerState.PAUSED);
|
|
37592
|
+
if (this.hls && !this.videoElement.seeking) {
|
|
37593
|
+
this.hls.stopLoad();
|
|
37594
|
+
if (this.pendingRetryTimer) {
|
|
37595
|
+
clearTimeout(this.pendingRetryTimer);
|
|
37596
|
+
this.pendingRetryTimer = null;
|
|
37597
|
+
}
|
|
37598
|
+
}
|
|
37599
|
+
});
|
|
37547
37600
|
this.videoElement.addEventListener('waiting', () => this.setState(exports.PlayerState.BUFFERING));
|
|
37548
37601
|
this.videoElement.addEventListener('error', () => {
|
|
37549
37602
|
const error = this.videoElement.error;
|
|
@@ -37570,6 +37623,8 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37570
37623
|
}
|
|
37571
37624
|
async play() {
|
|
37572
37625
|
try {
|
|
37626
|
+
this.networkRetryCount = 0;
|
|
37627
|
+
this.retryBackoff = 1000;
|
|
37573
37628
|
await this.videoElement.play();
|
|
37574
37629
|
}
|
|
37575
37630
|
catch (err) {
|
|
@@ -37606,6 +37661,11 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
37606
37661
|
getState() { return this.state; }
|
|
37607
37662
|
destroy() {
|
|
37608
37663
|
this.log('Destroying player');
|
|
37664
|
+
this.destroyed = true;
|
|
37665
|
+
if (this.pendingRetryTimer) {
|
|
37666
|
+
clearTimeout(this.pendingRetryTimer);
|
|
37667
|
+
this.pendingRetryTimer = null;
|
|
37668
|
+
}
|
|
37609
37669
|
if (this.nonceRefreshTimer) {
|
|
37610
37670
|
clearTimeout(this.nonceRefreshTimer);
|
|
37611
37671
|
this.nonceRefreshTimer = null;
|
|
@@ -38439,6 +38499,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
38439
38499
|
this._ctxTargetBound = () => { };
|
|
38440
38500
|
this._ctxKeyDownBound = () => { };
|
|
38441
38501
|
this._watermarkObserver = null;
|
|
38502
|
+
this._watermarkStylePollTimer = null;
|
|
38442
38503
|
this._watermarkText = '';
|
|
38443
38504
|
this._watermarkDriftTimer = null;
|
|
38444
38505
|
const accent = config.branding?.accentColor ?? '#00e5a0';
|
|
@@ -39116,6 +39177,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
39116
39177
|
tampered = true;
|
|
39117
39178
|
}
|
|
39118
39179
|
if (m.type === 'attributes' && m.target === this.watermarkCanvas) {
|
|
39180
|
+
this.watermarkCanvas.removeAttribute('style');
|
|
39119
39181
|
this.watermarkCanvas.className = 'gvp-watermark-canvas';
|
|
39120
39182
|
this.watermarkCanvas.setAttribute('aria-hidden', 'true');
|
|
39121
39183
|
this._renderCanvasWatermark(this._watermarkText);
|
|
@@ -39129,14 +39191,51 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
39129
39191
|
}
|
|
39130
39192
|
}
|
|
39131
39193
|
if (tampered) {
|
|
39132
|
-
this.
|
|
39133
|
-
bubbles: true,
|
|
39134
|
-
detail: { type: 'watermark_tamper', timestamp: Date.now() },
|
|
39135
|
-
}));
|
|
39194
|
+
this._onWatermarkTamper();
|
|
39136
39195
|
}
|
|
39137
39196
|
});
|
|
39138
39197
|
this._watermarkObserver.observe(this.root, { childList: true });
|
|
39139
39198
|
this._watermarkObserver.observe(this.watermarkDiv, { attributes: true, childList: true });
|
|
39199
|
+
this._watermarkObserver.observe(this.watermarkCanvas, { attributes: true });
|
|
39200
|
+
if (this._watermarkStylePollTimer)
|
|
39201
|
+
clearInterval(this._watermarkStylePollTimer);
|
|
39202
|
+
this._watermarkStylePollTimer = setInterval(() => {
|
|
39203
|
+
if (!this._watermarkText)
|
|
39204
|
+
return;
|
|
39205
|
+
const divHidden = this._isElementHidden(this.watermarkDiv);
|
|
39206
|
+
const canvasHidden = this._isElementHidden(this.watermarkCanvas);
|
|
39207
|
+
if (divHidden || canvasHidden) {
|
|
39208
|
+
this.watermarkDiv.removeAttribute('style');
|
|
39209
|
+
this.watermarkDiv.className = 'gvp-watermark';
|
|
39210
|
+
this.watermarkCanvas.removeAttribute('style');
|
|
39211
|
+
this.watermarkCanvas.className = 'gvp-watermark-canvas';
|
|
39212
|
+
this._purgeWatermarkOverrideStyles();
|
|
39213
|
+
this._onWatermarkTamper();
|
|
39214
|
+
}
|
|
39215
|
+
}, 500);
|
|
39216
|
+
}
|
|
39217
|
+
_isElementHidden(el) {
|
|
39218
|
+
const cs = getComputedStyle(el);
|
|
39219
|
+
return (cs.display === 'none' ||
|
|
39220
|
+
cs.visibility === 'hidden' ||
|
|
39221
|
+
parseFloat(cs.opacity) < 0.01 ||
|
|
39222
|
+
(el.offsetWidth === 0 && el.offsetHeight === 0));
|
|
39223
|
+
}
|
|
39224
|
+
_purgeWatermarkOverrideStyles() {
|
|
39225
|
+
const styles = this.root.querySelectorAll('style');
|
|
39226
|
+
styles.forEach((s) => {
|
|
39227
|
+
const text = s.textContent ?? '';
|
|
39228
|
+
if (/gvp-watermark/i.test(text)) {
|
|
39229
|
+
s.remove();
|
|
39230
|
+
}
|
|
39231
|
+
});
|
|
39232
|
+
}
|
|
39233
|
+
_onWatermarkTamper() {
|
|
39234
|
+
this.corePlayer.pause();
|
|
39235
|
+
this.root.dispatchEvent(new CustomEvent('gv:security', {
|
|
39236
|
+
bubbles: true,
|
|
39237
|
+
detail: { type: 'watermark_tamper', timestamp: Date.now() },
|
|
39238
|
+
}));
|
|
39140
39239
|
}
|
|
39141
39240
|
_refillWatermark() {
|
|
39142
39241
|
const seed = this._hashCode(this._watermarkText);
|
|
@@ -39320,6 +39419,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
39320
39419
|
}
|
|
39321
39420
|
play() { return this.corePlayer.play(); }
|
|
39322
39421
|
pause() { return this.corePlayer.pause(); }
|
|
39422
|
+
stopLoading() { return this.corePlayer.stopLoading(); }
|
|
39323
39423
|
seek(t) { return this.corePlayer.seek(t); }
|
|
39324
39424
|
getCurrentTime() { return this.corePlayer.getCurrentTime(); }
|
|
39325
39425
|
getDuration() { return this.corePlayer.getDuration(); }
|
|
@@ -39347,6 +39447,8 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
|
|
|
39347
39447
|
this._watermarkObserver?.disconnect();
|
|
39348
39448
|
if (this._watermarkDriftTimer)
|
|
39349
39449
|
clearInterval(this._watermarkDriftTimer);
|
|
39450
|
+
if (this._watermarkStylePollTimer)
|
|
39451
|
+
clearInterval(this._watermarkStylePollTimer);
|
|
39350
39452
|
this.corePlayer.destroy();
|
|
39351
39453
|
this.root.remove();
|
|
39352
39454
|
}
|