@guardvideo/player-sdk 3.5.0 → 3.6.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.
@@ -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
- setTimeout(() => this.hls?.startLoad(), delay);
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
- setTimeout(() => {
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.log('Playlist load failed — refreshing nonce before retry');
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
- setTimeout(() => this.hls?.startLoad(), this.retryBackoff);
37550
+ this.scheduleRetry(() => this.hls?.startLoad(), this.retryBackoff);
37517
37551
  }).catch(() => {
37518
- setTimeout(() => this.hls?.startLoad(), this.retryBackoff);
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.error('Network error, attempting recovery...');
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
- setTimeout(() => this.hls?.startLoad(), this.retryBackoff);
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', () => this.setState(exports.PlayerState.PLAYING));
37546
- this.videoElement.addEventListener('pause', () => this.setState(exports.PlayerState.PAUSED));
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;
@@ -39320,6 +39380,7 @@ Schedule: ${scheduleItems.map(seg => segmentToString(seg))} pos: ${this.timeline
39320
39380
  }
39321
39381
  play() { return this.corePlayer.play(); }
39322
39382
  pause() { return this.corePlayer.pause(); }
39383
+ stopLoading() { return this.corePlayer.stopLoading(); }
39323
39384
  seek(t) { return this.corePlayer.seek(t); }
39324
39385
  getCurrentTime() { return this.corePlayer.getCurrentTime(); }
39325
39386
  getDuration() { return this.corePlayer.getDuration(); }