stormcloud-video-player 0.8.8 → 0.8.10

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/lib/index.d.cts CHANGED
@@ -27,6 +27,7 @@ interface StormcloudVideoPlayerConfig {
27
27
  adFailsafeTimeoutMs?: number;
28
28
  adBreakCheckIntervalMs?: number;
29
29
  maxAdBreakExtensionMs?: number;
30
+ minRemainingMsToStartAd?: number;
30
31
  showCustomControls?: boolean;
31
32
  hideLoadingIndicator?: boolean;
32
33
  onVolumeToggle?: () => void;
@@ -202,6 +203,7 @@ declare class StormcloudVideoPlayer {
202
203
  private readonly maxConsecutiveFailures;
203
204
  private lastAdRequestTime;
204
205
  private readonly minAdRequestIntervalMs;
206
+ private readonly defaultMinRemainingMsToStartAd;
205
207
  private readonly backoffBaseMs;
206
208
  private readonly maxBackoffMs;
207
209
  private placeholderContainer;
@@ -217,6 +219,7 @@ declare class StormcloudVideoPlayer {
217
219
  private preloadPoolActive;
218
220
  private preloadPoolLoopRunning;
219
221
  private adDetectSentForCurrentBreak;
222
+ private adBreakEpoch;
220
223
  private palNonce;
221
224
  private palPlaybackStarted;
222
225
  private readonly streamCorrelator;
@@ -336,6 +339,8 @@ declare class StormcloudVideoPlayer {
336
339
  private selectVastTagsForBreak;
337
340
  private logAdState;
338
341
  private getRemainingAdMs;
342
+ private getMinRemainingMsToStartAd;
343
+ private hasTimeToStartAnotherAd;
339
344
  private findBreakForTime;
340
345
  toggleMute(): void;
341
346
  toggleFullscreen(): Promise<void>;
package/lib/index.d.ts CHANGED
@@ -27,6 +27,7 @@ interface StormcloudVideoPlayerConfig {
27
27
  adFailsafeTimeoutMs?: number;
28
28
  adBreakCheckIntervalMs?: number;
29
29
  maxAdBreakExtensionMs?: number;
30
+ minRemainingMsToStartAd?: number;
30
31
  showCustomControls?: boolean;
31
32
  hideLoadingIndicator?: boolean;
32
33
  onVolumeToggle?: () => void;
@@ -202,6 +203,7 @@ declare class StormcloudVideoPlayer {
202
203
  private readonly maxConsecutiveFailures;
203
204
  private lastAdRequestTime;
204
205
  private readonly minAdRequestIntervalMs;
206
+ private readonly defaultMinRemainingMsToStartAd;
205
207
  private readonly backoffBaseMs;
206
208
  private readonly maxBackoffMs;
207
209
  private placeholderContainer;
@@ -217,6 +219,7 @@ declare class StormcloudVideoPlayer {
217
219
  private preloadPoolActive;
218
220
  private preloadPoolLoopRunning;
219
221
  private adDetectSentForCurrentBreak;
222
+ private adBreakEpoch;
220
223
  private palNonce;
221
224
  private palPlaybackStarted;
222
225
  private readonly streamCorrelator;
@@ -336,6 +339,8 @@ declare class StormcloudVideoPlayer {
336
339
  private selectVastTagsForBreak;
337
340
  private logAdState;
338
341
  private getRemainingAdMs;
342
+ private getMinRemainingMsToStartAd;
343
+ private hasTimeToStartAnotherAd;
339
344
  private findBreakForTime;
340
345
  toggleMute(): void;
341
346
  toggleFullscreen(): Promise<void>;
package/lib/index.js CHANGED
@@ -2708,6 +2708,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2708
2708
  this.maxConsecutiveFailures = 5;
2709
2709
  this.lastAdRequestTime = 0;
2710
2710
  this.minAdRequestIntervalMs = 3e3;
2711
+ this.defaultMinRemainingMsToStartAd = 4e3;
2711
2712
  this.backoffBaseMs = 1e3;
2712
2713
  this.maxBackoffMs = 15e3;
2713
2714
  this.globalConsecutiveNoFills = 0;
@@ -2722,6 +2723,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2722
2723
  this.preloadPoolActive = false;
2723
2724
  this.preloadPoolLoopRunning = false;
2724
2725
  this.adDetectSentForCurrentBreak = false;
2726
+ this.adBreakEpoch = 0;
2725
2727
  this.palPlaybackStarted = false;
2726
2728
  this.streamCorrelator = generateCorrelator();
2727
2729
  this.consentSignals = {};
@@ -3226,7 +3228,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3226
3228
  } : {});
3227
3229
  }
3228
3230
  if (_this.inAdBreak) {
3229
- if (remaining > 500) {
3231
+ if (_this.hasTimeToStartAnotherAd()) {
3230
3232
  _this.tryNextAvailableAdWithRateLimit();
3231
3233
  } else {
3232
3234
  _this.handleAdPodComplete();
@@ -3299,7 +3301,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3299
3301
  queuedUrls: _to_consumable_array(_this.adRequestQueue)
3300
3302
  } : {});
3301
3303
  }
3302
- if (remaining > 500) {
3304
+ if (_this.hasTimeToStartAnotherAd()) {
3303
3305
  _this.tryNextAvailableAdWithRateLimit();
3304
3306
  } else {
3305
3307
  _this.handleAdPodComplete();
@@ -3999,7 +4001,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3999
4001
  var remainingMs1 = Math.max(0, this.expectedAdBreakDurationMs - elapsedMs1);
4000
4002
  this.scheduleAdStopCountdown(remainingMs1);
4001
4003
  }
4002
- if (!this.adPlayer.isAdPlaying() && this.activeAdRequestToken === null && this.adRequestQueue.length > 0) {
4004
+ if (!this.adPlayer.isAdPlaying() && this.activeAdRequestToken === null && this.adRequestQueue.length > 0 && this.hasTimeToStartAnotherAd()) {
4003
4005
  this.tryNextAvailableAdWithRateLimit();
4004
4006
  }
4005
4007
  return;
@@ -5269,7 +5271,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5269
5271
  key: "preloadAdIntoPool",
5270
5272
  value: function preloadAdIntoPool(vastUrl) {
5271
5273
  return _async_to_generator(function() {
5272
- var _this, loadPromise;
5274
+ var _this, epoch, loadPromise;
5273
5275
  return _ts_generator(this, function(_state) {
5274
5276
  _this = this;
5275
5277
  if (this.isGamInCooldown()) {
@@ -5295,6 +5297,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5295
5297
  if (this.config.debugAdTiming) {
5296
5298
  console.log("[PRELOAD-POOL] \uD83D\uDCE5 Preloading ad into pool: ".concat(vastUrl));
5297
5299
  }
5300
+ epoch = this.adBreakEpoch;
5298
5301
  loadPromise = function() {
5299
5302
  return _async_to_generator(function() {
5300
5303
  var _this, hasAdError, adErrorPayload, errorListenerCleanup, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, lateErrorListener, error;
@@ -5372,6 +5375,20 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5372
5375
  2
5373
5376
  ];
5374
5377
  }
5378
+ if (!this.preloadPoolActive || epoch !== this.adBreakEpoch) {
5379
+ if (this.config.debugAdTiming) {
5380
+ console.log("[PRELOAD-POOL] \uD83D\uDEAB Discarding preloaded ad - break ended during fetch");
5381
+ }
5382
+ if (errorListenerCleanup) {
5383
+ errorListenerCleanup();
5384
+ }
5385
+ try {
5386
+ preloadAd.destroy();
5387
+ } catch (unused) {}
5388
+ return [
5389
+ 2
5390
+ ];
5391
+ }
5375
5392
  poolEntry = {
5376
5393
  vastUrl: vastUrl,
5377
5394
  adController: preloadAd,
@@ -5443,7 +5460,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5443
5460
  key: "preloadPoolLoop",
5444
5461
  value: function preloadPoolLoop(baseVastUrl) {
5445
5462
  return _async_to_generator(function() {
5446
- var remaining, newUrls, newUrl;
5463
+ var epoch, newUrls, newUrl;
5447
5464
  return _ts_generator(this, function(_state) {
5448
5465
  switch(_state.label){
5449
5466
  case 0:
@@ -5453,12 +5470,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5453
5470
  ];
5454
5471
  }
5455
5472
  this.preloadPoolLoopRunning = true;
5473
+ epoch = this.adBreakEpoch;
5456
5474
  if (this.config.debugAdTiming) {
5457
5475
  console.log("[PRELOAD-POOL] \uD83D\uDD04 Starting continuous preload loop");
5458
5476
  }
5459
5477
  _state.label = 1;
5460
5478
  case 1:
5461
- if (!this.preloadPoolActive) return [
5479
+ if (!(this.preloadPoolActive && epoch === this.adBreakEpoch)) return [
5462
5480
  3,
5463
5481
  10
5464
5482
  ];
@@ -5503,20 +5521,20 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5503
5521
  3,
5504
5522
  8
5505
5523
  ];
5506
- if (!this.preloadPoolActive || this.isGamInCooldown()) {
5524
+ if (!this.preloadPoolActive || epoch !== this.adBreakEpoch || this.isGamInCooldown()) {
5507
5525
  return [
5508
5526
  3,
5509
5527
  8
5510
5528
  ];
5511
5529
  }
5512
- if (this.inAdBreak) {
5513
- remaining = this.getRemainingAdMs();
5514
- if (remaining <= 0) {
5515
- return [
5516
- 3,
5517
- 8
5518
- ];
5530
+ if (this.inAdBreak && !this.hasTimeToStartAnotherAd()) {
5531
+ if (this.config.debugAdTiming) {
5532
+ console.log("[PRELOAD-POOL] Not enough ad break time left, stopping preload");
5519
5533
  }
5534
+ return [
5535
+ 3,
5536
+ 8
5537
+ ];
5520
5538
  }
5521
5539
  newUrls = this.generateVastUrlsWithCorrelators(baseVastUrl, 1);
5522
5540
  newUrl = newUrls[0];
@@ -5936,7 +5954,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5936
5954
  key: "continuousFetchLoop",
5937
5955
  value: function continuousFetchLoop(baseVastUrl) {
5938
5956
  return _async_to_generator(function() {
5939
- var _this, _loop, _ret;
5957
+ var _this, _loop, epoch, _ret;
5940
5958
  return _ts_generator(this, function(_state) {
5941
5959
  switch(_state.label){
5942
5960
  case 0:
@@ -5946,9 +5964,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5946
5964
  switch(_state.label){
5947
5965
  case 0:
5948
5966
  remaining = _this.getRemainingAdMs();
5949
- if (remaining <= 0) {
5967
+ if (remaining <= _this.getMinRemainingMsToStartAd()) {
5950
5968
  if (_this.config.debugAdTiming) {
5951
- console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F Ad break time expired, stopping URL generation");
5969
+ console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (".concat(remaining, "ms), stopping URL generation"));
5952
5970
  }
5953
5971
  return [
5954
5972
  2,
@@ -6046,9 +6064,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6046
6064
  }
6047
6065
  });
6048
6066
  };
6067
+ epoch = this.adBreakEpoch;
6049
6068
  _state.label = 1;
6050
6069
  case 1:
6051
- if (!(this.continuousFetchingActive && this.inAdBreak)) return [
6070
+ if (!(this.continuousFetchingActive && this.inAdBreak && epoch === this.adBreakEpoch)) return [
6052
6071
  3,
6053
6072
  3
6054
6073
  ];
@@ -6097,10 +6116,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6097
6116
  key: "tryNextAvailableAdWithRateLimit",
6098
6117
  value: function tryNextAvailableAdWithRateLimit() {
6099
6118
  return _async_to_generator(function() {
6100
- var backoffMultiplier, backoffDelay, effectiveMinInterval, timeSinceLastRequest, waitTime;
6119
+ var epoch, backoffMultiplier, backoffDelay, effectiveMinInterval, timeSinceLastRequest, waitTime;
6101
6120
  return _ts_generator(this, function(_state) {
6102
6121
  switch(_state.label){
6103
6122
  case 0:
6123
+ if (!this.inAdBreak) {
6124
+ return [
6125
+ 2
6126
+ ];
6127
+ }
6128
+ epoch = this.adBreakEpoch;
6104
6129
  if (this.consecutiveFailures >= this.maxConsecutiveFailures) {
6105
6130
  if (this.config.debugAdTiming) {
6106
6131
  console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Too many consecutive failures (".concat(this.consecutiveFailures, "), ending ad break gracefully"));
@@ -6132,6 +6157,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6132
6157
  _state.sent();
6133
6158
  _state.label = 2;
6134
6159
  case 2:
6160
+ if (!this.inAdBreak || epoch !== this.adBreakEpoch) {
6161
+ return [
6162
+ 2
6163
+ ];
6164
+ }
6135
6165
  return [
6136
6166
  2,
6137
6167
  this.tryNextAvailableAd(0)
@@ -6150,10 +6180,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6150
6180
  return _ts_generator(this, function(_state) {
6151
6181
  switch(_state.label){
6152
6182
  case 0:
6183
+ if (!this.inAdBreak) {
6184
+ return [
6185
+ 2
6186
+ ];
6187
+ }
6153
6188
  remaining = this.getRemainingAdMs();
6154
- if (remaining <= 500) {
6189
+ if (!this.hasTimeToStartAnotherAd()) {
6155
6190
  if (this.config.debugAdTiming) {
6156
- console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F No time remaining, ending ad break");
6191
+ console.log("[CONTINUOUS-FETCH] ⏹️ Not enough time remaining (".concat(remaining, "ms) to start another ad, ending ad break"));
6157
6192
  }
6158
6193
  this.handleAdPodComplete();
6159
6194
  return [
@@ -6425,12 +6460,27 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6425
6460
  10
6426
6461
  ];
6427
6462
  }
6463
+ if (!this.hasTimeToStartAnotherAd()) {
6464
+ if (this.config.debugAdTiming) {
6465
+ console.log("[CONTINUOUS-FETCH] \u23F9\uFE0F Not enough time remaining during placeholder wait, ending ad break");
6466
+ }
6467
+ return [
6468
+ 3,
6469
+ 10
6470
+ ];
6471
+ }
6428
6472
  _state.label = 3;
6429
6473
  case 3:
6430
6474
  if (!(this.adRequestQueue.length > 0)) return [
6431
6475
  3,
6432
6476
  9
6433
6477
  ];
6478
+ if (!this.hasTimeToStartAnotherAd()) {
6479
+ return [
6480
+ 3,
6481
+ 9
6482
+ ];
6483
+ }
6434
6484
  nextAdUrl = this.adRequestQueue.shift();
6435
6485
  if (!nextAdUrl) {
6436
6486
  return [
@@ -6713,21 +6763,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6713
6763
  if (!this.inAdBreak) return;
6714
6764
  this.adStopTimerId = void 0;
6715
6765
  var adPlaying = this.adPlayer.isAdPlaying();
6716
- var pendingAds = this.adPodQueue.length > 0;
6717
6766
  var checkIntervalMs = Math.max(250, Math.floor((_this_config_adBreakCheckIntervalMs = this.config.adBreakCheckIntervalMs) !== null && _this_config_adBreakCheckIntervalMs !== void 0 ? _this_config_adBreakCheckIntervalMs : 1e3));
6718
6767
  var maxExtensionMsConfig = this.config.maxAdBreakExtensionMs;
6719
- var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 6e4;
6768
+ var maxExtensionMs = typeof maxExtensionMsConfig === "number" && maxExtensionMsConfig > 0 ? maxExtensionMsConfig : 0;
6720
6769
  var elapsedSinceStartMs = 0;
6721
6770
  if (this.currentAdBreakStartWallClockMs != null) {
6722
6771
  elapsedSinceStartMs = Date.now() - this.currentAdBreakStartWallClockMs;
6723
6772
  }
6724
6773
  var expectedDurationMs = (_this_expectedAdBreakDurationMs = this.expectedAdBreakDurationMs) !== null && _this_expectedAdBreakDurationMs !== void 0 ? _this_expectedAdBreakDurationMs : 0;
6725
6774
  var overrunMs = Math.max(0, elapsedSinceStartMs - expectedDurationMs);
6726
- var shouldExtendAdBreak = (adPlaying || pendingAds || this.showAds) && overrunMs < maxExtensionMs;
6775
+ var shouldExtendAdBreak = adPlaying && overrunMs < maxExtensionMs;
6727
6776
  if (shouldExtendAdBreak) {
6728
6777
  this.scheduleAdStopCountdown(checkIntervalMs);
6729
6778
  return;
6730
6779
  }
6780
+ if (this.config.debugAdTiming) {
6781
+ console.log("[StormcloudVideoPlayer] ⏱️ Ad break boundary reached (overrun=".concat(overrunMs, "ms) - hard-stopping ads and resuming stream"));
6782
+ }
6731
6783
  if (adPlaying) {
6732
6784
  this.adPlayer.stop().catch(function() {});
6733
6785
  }
@@ -7162,6 +7214,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7162
7214
  if (this.config.debugAdTiming) {
7163
7215
  console.log("[StormcloudVideoPlayer] \uD83C\uDFC1 Ad pod complete - cleaning up");
7164
7216
  }
7217
+ this.adBreakEpoch++;
7165
7218
  this.clearAdRequestWatchdog();
7166
7219
  this.clearAdFailsafeTimer();
7167
7220
  this.activeAdRequestToken = null;
@@ -7232,11 +7285,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7232
7285
  this.handleAdPodComplete();
7233
7286
  return;
7234
7287
  }
7235
- if (remaining > 500) {
7288
+ if (this.hasTimeToStartAnotherAd()) {
7236
7289
  this.tryNextAvailableAdWithRateLimit();
7237
7290
  } else {
7238
7291
  if (this.config.debugAdTiming) {
7239
- console.log("[AD-ERROR] Ad failed and no time remaining. Failed URLs:", this.failedVastUrls.size);
7292
+ console.log("[AD-ERROR] Ad failed and not enough time remaining for another ad. Failed URLs:", this.failedVastUrls.size);
7240
7293
  }
7241
7294
  this.handleAdPodComplete();
7242
7295
  }
@@ -7371,6 +7424,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7371
7424
  return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
7372
7425
  }
7373
7426
  },
7427
+ {
7428
+ key: "getMinRemainingMsToStartAd",
7429
+ value: function getMinRemainingMsToStartAd() {
7430
+ var configured = this.config.minRemainingMsToStartAd;
7431
+ if (typeof configured === "number" && configured >= 0) {
7432
+ return configured;
7433
+ }
7434
+ return this.defaultMinRemainingMsToStartAd;
7435
+ }
7436
+ },
7437
+ {
7438
+ key: "hasTimeToStartAnotherAd",
7439
+ value: function hasTimeToStartAnotherAd() {
7440
+ return this.getRemainingAdMs() > this.getMinRemainingMsToStartAd();
7441
+ }
7442
+ },
7374
7443
  {
7375
7444
  key: "findBreakForTime",
7376
7445
  value: function findBreakForTime(nowMs) {