stormcloud-video-player 0.8.10 → 0.8.12

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.cjs CHANGED
@@ -1476,6 +1476,12 @@ function createHlsAdPlayer(contentVideo, options) {
1476
1476
  contentVideo.style.visibility = "visible";
1477
1477
  contentVideo.style.opacity = "1";
1478
1478
  }
1479
+ },
1480
+ getLoadedAdDuration: function getLoadedAdDuration() {
1481
+ return currentAd === null || currentAd === void 0 ? void 0 : currentAd.duration;
1482
+ },
1483
+ getLoadedAdId: function getLoadedAdId() {
1484
+ return currentAd === null || currentAd === void 0 ? void 0 : currentAd.id;
1479
1485
  }
1480
1486
  };
1481
1487
  }
@@ -2935,6 +2941,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2935
2941
  this.preloadPoolLoopRunning = false;
2936
2942
  this.adDetectSentForCurrentBreak = false;
2937
2943
  this.adBreakEpoch = 0;
2944
+ this.adBreakPlayedDurationMs = 0;
2938
2945
  this.palPlaybackStarted = false;
2939
2946
  this.streamCorrelator = generateCorrelator();
2940
2947
  this.consentSignals = {};
@@ -3431,6 +3438,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3431
3438
  adIndex: _this.currentAdIndex,
3432
3439
  timestamp: /* @__PURE__ */ new Date().toISOString()
3433
3440
  }).catch(function() {});
3441
+ _this.recordPlayedAdDuration();
3434
3442
  var remaining = _this.getRemainingAdMs();
3435
3443
  _this.consecutiveFailures = 0;
3436
3444
  if (_this.config.debugAdTiming) {
@@ -3506,6 +3514,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3506
3514
  return;
3507
3515
  }
3508
3516
  _this.consecutiveFailures = 0;
3517
+ _this.recordPlayedAdDuration();
3509
3518
  var remaining = _this.getRemainingAdMs();
3510
3519
  if (_this.config.debugAdTiming) {
3511
3520
  console.log("[CONTINUOUS-FETCH] content_resume event: remaining=".concat(remaining, "ms, queued ads=").concat(_this.adRequestQueue.length), _this.adRequestQueue.length > 0 ? {
@@ -5399,7 +5408,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5399
5408
  }
5400
5409
  this.beginNewAdPod();
5401
5410
  this.podAssignedByPrefetch = true;
5402
- var urlsToPregenerate = 5;
5411
+ var urlsToPregenerate = 1;
5403
5412
  var generatedUrls = this.generateVastUrlsWithCorrelators(baseVastUrl, urlsToPregenerate);
5404
5413
  this.pendingAdBreak = _object_spread_props(_object_spread({
5405
5414
  marker: marker
@@ -5424,7 +5433,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5424
5433
  console.log("[PREFETCH] \uD83D\uDD04 Starting ad prefetch for upcoming ad break");
5425
5434
  console.log("[PREFETCH] \uD83D\uDCCB Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
5426
5435
  }
5427
- this.startPreloadPool(baseVastUrl, generatedUrls.slice(0, this.maxPreloadPoolSize));
5436
+ this.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
5428
5437
  }
5429
5438
  },
5430
5439
  {
@@ -5442,6 +5451,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5442
5451
  {
5443
5452
  key: "startPreloadPool",
5444
5453
  value: function startPreloadPool(baseVastUrl, initialUrls) {
5454
+ var startLoopImmediately = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : true;
5445
5455
  if (this.preloadPoolActive) {
5446
5456
  if (this.config.debugAdTiming) {
5447
5457
  console.log("[PRELOAD-POOL] Already active, skipping duplicate start");
@@ -5475,7 +5485,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5475
5485
  }
5476
5486
  }
5477
5487
  }
5478
- this.preloadPoolLoop(baseVastUrl);
5488
+ if (startLoopImmediately) {
5489
+ this.preloadPoolLoop(baseVastUrl);
5490
+ } else if (this.config.debugAdTiming) {
5491
+ console.log("[PRELOAD-POOL] Pre-break mode: preloaded once, waiting for SCTE start to continue fetching");
5492
+ }
5479
5493
  }
5480
5494
  },
5481
5495
  {
@@ -5511,7 +5525,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5511
5525
  epoch = this.adBreakEpoch;
5512
5526
  loadPromise = function() {
5513
5527
  return _async_to_generator(function() {
5514
- var _this, hasAdError, adErrorPayload, errorListenerCleanup, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, lateErrorListener, error;
5528
+ var _this, hasAdError, adErrorPayload, errorListenerCleanup, _preloadAd_getLoadedAdDuration, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, loadedDuration, lateErrorListener, _poolEntry_durationSeconds, error;
5515
5529
  return _ts_generator(this, function(_state) {
5516
5530
  switch(_state.label){
5517
5531
  case 0:
@@ -5607,6 +5621,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5607
5621
  isReady: true,
5608
5622
  loadPromise: Promise.resolve()
5609
5623
  };
5624
+ loadedDuration = (_preloadAd_getLoadedAdDuration = preloadAd.getLoadedAdDuration) === null || _preloadAd_getLoadedAdDuration === void 0 ? void 0 : _preloadAd_getLoadedAdDuration.call(preloadAd);
5625
+ if (loadedDuration != null && loadedDuration > 0) {
5626
+ poolEntry.durationSeconds = loadedDuration;
5627
+ }
5610
5628
  lateErrorListener = function lateErrorListener(payload) {
5611
5629
  var index = _this.preloadPool.findIndex(function(entry) {
5612
5630
  return entry.vastUrl === vastUrl;
@@ -5624,7 +5642,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5624
5642
  preloadAd.on("ad_error", lateErrorListener);
5625
5643
  this.preloadPool.push(poolEntry);
5626
5644
  if (this.config.debugAdTiming) {
5627
- console.log("[PRELOAD-POOL] ✅ Ad preloaded (no errors detected within timeout) (pool size: ".concat(this.preloadPool.length, "/").concat(this.maxPreloadPoolSize, ")"));
5645
+ ;
5646
+ console.log("[PRELOAD-POOL] ✅ Ad preloaded (no errors detected within timeout) (pool size: ".concat(this.preloadPool.length, "/").concat(this.maxPreloadPoolSize, ", duration=").concat((_poolEntry_durationSeconds = poolEntry.durationSeconds) !== null && _poolEntry_durationSeconds !== void 0 ? _poolEntry_durationSeconds : "?", "s)"));
5628
5647
  }
5629
5648
  if (errorListenerCleanup) {
5630
5649
  errorListenerCleanup();
@@ -5905,8 +5924,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5905
5924
  }
5906
5925
  this.inAdBreak = true;
5907
5926
  this.currentAdBreakStartWallClockMs = Date.now();
5927
+ this.adBreakPlayedDurationMs = 0;
5908
5928
  this.currentAdIndex = 0;
5909
- this.totalAdsInBreak = 1;
5929
+ this.totalAdsInBreak = 0;
5910
5930
  this.adPodQueue = [];
5911
5931
  this.showAds = true;
5912
5932
  this.showPlaceholderLayer();
@@ -5960,14 +5980,30 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5960
5980
  case 1:
5961
5981
  _state.trys.push([
5962
5982
  1,
5963
- 8,
5983
+ 12,
5964
5984
  ,
5965
- 14
5985
+ 20
5966
5986
  ]);
5967
5987
  if (!(usePreloadedAd && preloadedController)) return [
5988
+ 3,
5989
+ 5
5990
+ ];
5991
+ if (!!this.ensureLoadedAdFitsBudget(preloadedController)) return [
5968
5992
  3,
5969
5993
  3
5970
5994
  ];
5995
+ this.rejectLoadedAdForDuration(preloadedController);
5996
+ this.startContinuousFetching(baseVastUrl);
5997
+ return [
5998
+ 4,
5999
+ this.tryNextAvailableAdWithRateLimit()
6000
+ ];
6001
+ case 2:
6002
+ _state.sent();
6003
+ return [
6004
+ 2
6005
+ ];
6006
+ case 3:
5971
6007
  preservedMuted = this.adPlayer.getOriginalMutedState();
5972
6008
  preservedVolume = this.adPlayer.getOriginalVolume();
5973
6009
  this.adPlayer.destroy();
@@ -5980,18 +6016,20 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5980
6016
  }
5981
6017
  this.adPlayer.resume();
5982
6018
  this.consecutiveFailures = 0;
5983
- this.currentAdIndex++;
5984
6019
  this.startContinuousFetching(baseVastUrl);
5985
6020
  if (!this.preloadPoolActive) {
5986
6021
  this.preloadPoolActive = true;
6022
+ }
6023
+ if (!this.preloadPoolLoopRunning) {
5987
6024
  this.preloadPoolLoop(baseVastUrl);
5988
6025
  }
5989
6026
  return [
5990
6027
  4,
5991
6028
  this.adPlayer.play()
5992
6029
  ];
5993
- case 2:
6030
+ case 4:
5994
6031
  _state.sent();
6032
+ this.markAdStarted();
5995
6033
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5996
6034
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5997
6035
  }
@@ -5999,21 +6037,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5999
6037
  this.adPlayer.setAdVolume(adVolume);
6000
6038
  return [
6001
6039
  3,
6002
- 7
6040
+ 11
6003
6041
  ];
6004
- case 3:
6042
+ case 5:
6005
6043
  return [
6006
6044
  4,
6007
6045
  this.enforceGlobalRateLimit()
6008
6046
  ];
6009
- case 4:
6047
+ case 6:
6010
6048
  _state.sent();
6011
6049
  this.lastAdRequestTime = Date.now();
6012
6050
  return [
6013
6051
  4,
6014
6052
  this.adPlayer.requestAds(firstAdUrl)
6015
6053
  ];
6016
- case 5:
6054
+ case 7:
6017
6055
  _state.sent();
6018
6056
  sendAdLoadedTracking(this.config.licenseKey, {
6019
6057
  source: "hls",
@@ -6023,8 +6061,24 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6023
6061
  if (this.config.debugAdTiming) {
6024
6062
  console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
6025
6063
  }
6064
+ if (!!this.ensureLoadedAdFitsBudget()) return [
6065
+ 3,
6066
+ 9
6067
+ ];
6068
+ this.rejectLoadedAdForDuration();
6069
+ this.recreateAdController();
6070
+ this.startContinuousFetching(baseVastUrl);
6071
+ return [
6072
+ 4,
6073
+ this.tryNextAvailableAdWithRateLimit()
6074
+ ];
6075
+ case 8:
6076
+ _state.sent();
6077
+ return [
6078
+ 2
6079
+ ];
6080
+ case 9:
6026
6081
  this.consecutiveFailures = 0;
6027
- this.currentAdIndex++;
6028
6082
  this.startContinuousFetching(baseVastUrl);
6029
6083
  if (!this.preloadPoolActive) {
6030
6084
  this.startPreloadPool(baseVastUrl, []);
@@ -6033,44 +6087,61 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6033
6087
  4,
6034
6088
  this.adPlayer.play()
6035
6089
  ];
6036
- case 6:
6090
+ case 10:
6037
6091
  _state.sent();
6092
+ this.markAdStarted();
6038
6093
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6039
6094
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
6040
6095
  }
6041
6096
  adVolume1 = currentMuted ? 0 : currentVolume;
6042
6097
  this.adPlayer.setAdVolume(adVolume1);
6043
- _state.label = 7;
6044
- case 7:
6098
+ _state.label = 11;
6099
+ case 11:
6045
6100
  return [
6046
6101
  3,
6047
- 14
6102
+ 20
6048
6103
  ];
6049
- case 8:
6104
+ case 12:
6050
6105
  error = _state.sent();
6051
6106
  if (this.config.debugAdTiming) {
6052
6107
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F First ad request failed:", error);
6053
6108
  }
6054
6109
  if (!!usePreloadedAd) return [
6055
6110
  3,
6056
- 12
6111
+ 18
6057
6112
  ];
6058
6113
  fallbackPreloaded = this.getPreloadedAd();
6059
6114
  if (!fallbackPreloaded) return [
6060
6115
  3,
6061
- 12
6116
+ 18
6062
6117
  ];
6063
6118
  if (this.config.debugAdTiming) {
6064
6119
  console.log("[CONTINUOUS-FETCH] \uD83D\uDD04 First ad failed, using preloaded fallback");
6065
6120
  }
6066
- _state.label = 9;
6067
- case 9:
6121
+ _state.label = 13;
6122
+ case 13:
6068
6123
  _state.trys.push([
6069
- 9,
6070
- 11,
6124
+ 13,
6125
+ 17,
6071
6126
  ,
6072
- 12
6127
+ 18
6073
6128
  ]);
6129
+ if (!!this.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
6130
+ 3,
6131
+ 15
6132
+ ];
6133
+ this.rejectLoadedAdForDuration(fallbackPreloaded.adController);
6134
+ this.startContinuousFetching(baseVastUrl);
6135
+ return [
6136
+ 4,
6137
+ this.tryNextAvailableAdWithRateLimit()
6138
+ ];
6139
+ case 14:
6140
+ _state.sent();
6141
+ return [
6142
+ 2
6143
+ ];
6144
+ case 15:
6074
6145
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
6075
6146
  preservedVolume1 = this.adPlayer.getOriginalVolume();
6076
6147
  this.adPlayer.destroy();
@@ -6080,7 +6151,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6080
6151
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
6081
6152
  this.adPlayer.resume();
6082
6153
  this.consecutiveFailures = 0;
6083
- this.currentAdIndex++;
6084
6154
  this.startContinuousFetching(baseVastUrl);
6085
6155
  if (!this.preloadPoolActive) {
6086
6156
  this.preloadPoolActive = true;
@@ -6090,8 +6160,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6090
6160
  4,
6091
6161
  this.adPlayer.play()
6092
6162
  ];
6093
- case 10:
6163
+ case 16:
6094
6164
  _state.sent();
6165
+ this.markAdStarted();
6095
6166
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6096
6167
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
6097
6168
  }
@@ -6100,16 +6171,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6100
6171
  return [
6101
6172
  2
6102
6173
  ];
6103
- case 11:
6174
+ case 17:
6104
6175
  fallbackError = _state.sent();
6105
6176
  if (this.config.debugAdTiming) {
6106
6177
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded fallback also failed:", fallbackError);
6107
6178
  }
6108
6179
  return [
6109
6180
  3,
6110
- 12
6181
+ 18
6111
6182
  ];
6112
- case 12:
6183
+ case 18:
6113
6184
  if (this.isTemporaryAdError(error)) {
6114
6185
  this.temporaryFailureUrls.set(firstAdUrl, Date.now());
6115
6186
  if (this.config.debugAdTiming) {
@@ -6127,13 +6198,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6127
6198
  4,
6128
6199
  this.tryNextAvailableAdWithRateLimit()
6129
6200
  ];
6130
- case 13:
6201
+ case 19:
6131
6202
  _state.sent();
6132
6203
  return [
6133
6204
  3,
6134
- 14
6205
+ 20
6135
6206
  ];
6136
- case 14:
6207
+ case 20:
6137
6208
  return [
6138
6209
  2
6139
6210
  ];
@@ -6174,10 +6245,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6174
6245
  return _ts_generator(this, function(_state) {
6175
6246
  switch(_state.label){
6176
6247
  case 0:
6177
- remaining = _this.getRemainingAdMs();
6248
+ remaining = Math.min(_this.getWallClockRemainingAdMs(), _this.getDurationBudgetRemainingMs());
6178
6249
  if (remaining <= _this.getMinRemainingMsToStartAd()) {
6179
6250
  if (_this.config.debugAdTiming) {
6180
- console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (".concat(remaining, "ms), stopping URL generation"));
6251
+ console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (wall=".concat(_this.getWallClockRemainingAdMs(), "ms, budget=").concat(_this.getDurationBudgetRemainingMs(), "ms), stopping URL generation"));
6181
6252
  }
6182
6253
  return [
6183
6254
  2,
@@ -6259,7 +6330,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6259
6330
  });
6260
6331
  }
6261
6332
  _this.adRequestQueue.push(newAdUrl);
6262
- _this.totalAdsInBreak++;
6263
6333
  generationDelay = _this.consecutiveFailures > 0 ? Math.min(1e3 * Math.pow(2, _this.consecutiveFailures), 5e3) : 500;
6264
6334
  return [
6265
6335
  4,
@@ -6428,7 +6498,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6428
6498
  preloaded = this.getPreloadedAd();
6429
6499
  if (!preloaded) return [
6430
6500
  3,
6431
- 4
6501
+ 6
6432
6502
  ];
6433
6503
  if (this.config.debugAdTiming) {
6434
6504
  console.log("[CONTINUOUS-FETCH] \uD83C\uDFAF Using preloaded ad from pool (".concat(this.preloadPool.length, " remaining in pool)"));
@@ -6442,10 +6512,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6442
6512
  case 1:
6443
6513
  _state.trys.push([
6444
6514
  1,
6445
- 3,
6515
+ 5,
6446
6516
  ,
6447
- 4
6517
+ 6
6448
6518
  ]);
6519
+ if (!!this.ensureLoadedAdFitsBudget(preloaded.adController)) return [
6520
+ 3,
6521
+ 3
6522
+ ];
6523
+ this.rejectLoadedAdForDuration(preloaded.adController);
6524
+ return [
6525
+ 4,
6526
+ this.tryNextAvailableAdWithRateLimit()
6527
+ ];
6528
+ case 2:
6529
+ _state.sent();
6530
+ return [
6531
+ 2
6532
+ ];
6533
+ case 3:
6449
6534
  preservedMuted = this.adPlayer.getOriginalMutedState();
6450
6535
  preservedVolume = this.adPlayer.getOriginalVolume();
6451
6536
  this.adPlayer.destroy();
@@ -6454,15 +6539,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6454
6539
  this.attachImaEventListeners();
6455
6540
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
6456
6541
  this.adPlayer.resume();
6457
- this.currentAdIndex++;
6458
- this.totalAdRequestsInBreak++;
6459
- this.lastAdRequestTime = Date.now();
6460
6542
  return [
6461
6543
  4,
6462
6544
  this.adPlayer.play()
6463
6545
  ];
6464
- case 2:
6546
+ case 4:
6465
6547
  _state.sent();
6548
+ this.markAdStarted();
6466
6549
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6467
6550
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
6468
6551
  }
@@ -6471,25 +6554,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6471
6554
  return [
6472
6555
  2
6473
6556
  ];
6474
- case 3:
6557
+ case 5:
6475
6558
  error = _state.sent();
6476
6559
  if (this.config.debugAdTiming) {
6477
6560
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded ad failed to play:", error);
6478
6561
  }
6479
6562
  return [
6480
6563
  3,
6481
- 4
6564
+ 6
6482
6565
  ];
6483
- case 4:
6566
+ case 6:
6484
6567
  if (!(this.adRequestQueue.length > 0)) return [
6485
6568
  3,
6486
- 10
6569
+ 14
6487
6570
  ];
6488
6571
  nextAdUrl = this.adRequestQueue.shift();
6489
6572
  if (!nextAdUrl) {
6490
6573
  return [
6491
6574
  3,
6492
- 4
6575
+ 6
6493
6576
  ];
6494
6577
  }
6495
6578
  if (this.failedVastUrls.has(nextAdUrl)) {
@@ -6498,7 +6581,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6498
6581
  }
6499
6582
  return [
6500
6583
  3,
6501
- 4
6584
+ 6
6502
6585
  ];
6503
6586
  }
6504
6587
  if (this.isUrlInCooldown(nextAdUrl)) {
@@ -6507,40 +6590,51 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6507
6590
  }
6508
6591
  return [
6509
6592
  3,
6510
- 4
6593
+ 6
6511
6594
  ];
6512
6595
  }
6513
6596
  if (this.config.debugAdTiming) {
6514
- console.log("[CONTINUOUS-FETCH] \uD83C\uDFAC Requesting next ad via HLS ad player (".concat(this.currentAdIndex + 1, "/").concat(this.totalAdsInBreak, ", ").concat(this.adRequestQueue.length, " remaining in queue)"));
6597
+ console.log("[CONTINUOUS-FETCH] \uD83C\uDFAC Requesting next ad via HLS ad player (".concat(this.currentAdIndex + 1, ", ").concat(this.adRequestQueue.length, " remaining in queue)"));
6515
6598
  }
6516
- this.currentAdIndex++;
6517
- this.totalAdRequestsInBreak++;
6518
- this.lastAdRequestTime = Date.now();
6519
- _state.label = 5;
6520
- case 5:
6599
+ _state.label = 7;
6600
+ case 7:
6521
6601
  _state.trys.push([
6522
- 5,
6523
6602
  7,
6603
+ 9,
6524
6604
  ,
6525
- 9
6605
+ 13
6526
6606
  ]);
6527
6607
  return [
6528
6608
  4,
6529
6609
  this.playSingleAd(nextAdUrl)
6530
6610
  ];
6531
- case 6:
6611
+ case 8:
6532
6612
  _state.sent();
6533
6613
  this.consecutiveFailures = 0;
6534
6614
  return [
6535
6615
  3,
6536
- 9
6616
+ 13
6537
6617
  ];
6538
- case 7:
6618
+ case 9:
6539
6619
  error1 = _state.sent();
6540
6620
  errorMessage = error1.message;
6541
6621
  if (this.config.debugAdTiming) {
6542
6622
  console.log("[CONTINUOUS-FETCH] \u274C Ad request failed:", errorMessage);
6543
6623
  }
6624
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6625
+ 3,
6626
+ 11
6627
+ ];
6628
+ return [
6629
+ 4,
6630
+ this.tryNextAvailableAdWithRateLimit()
6631
+ ];
6632
+ case 10:
6633
+ _state.sent();
6634
+ return [
6635
+ 2
6636
+ ];
6637
+ case 11:
6544
6638
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6545
6639
  this.consecutiveFailures++;
6546
6640
  }
@@ -6548,21 +6642,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6548
6642
  4,
6549
6643
  this.tryNextAvailableAdWithRateLimit()
6550
6644
  ];
6551
- case 8:
6645
+ case 12:
6552
6646
  _state.sent();
6553
6647
  return [
6554
6648
  3,
6555
- 9
6649
+ 13
6556
6650
  ];
6557
- case 9:
6651
+ case 13:
6558
6652
  return [
6559
6653
  2
6560
6654
  ];
6561
- case 10:
6655
+ case 14:
6562
6656
  maxRetries = 3;
6563
6657
  if (!(this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3)) return [
6564
6658
  3,
6565
- 13
6659
+ 17
6566
6660
  ];
6567
6661
  if (this.config.debugAdTiming) {
6568
6662
  console.log("[CONTINUOUS-FETCH] ⏳ Queue empty, waiting for URLs... (".concat(retryCount + 1, "/").concat(maxRetries, ")"));
@@ -6573,18 +6667,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6573
6667
  return setTimeout(resolve, 500);
6574
6668
  })
6575
6669
  ];
6576
- case 11:
6670
+ case 15:
6577
6671
  _state.sent();
6578
6672
  return [
6579
6673
  4,
6580
6674
  this.tryNextAvailableAd(retryCount + 1)
6581
6675
  ];
6582
- case 12:
6676
+ case 16:
6583
6677
  _state.sent();
6584
6678
  return [
6585
6679
  2
6586
6680
  ];
6587
- case 13:
6681
+ case 17:
6588
6682
  if (!this.isShowingPlaceholder && remaining > 1e3) {
6589
6683
  this.showPlaceholderAndWaitForAds();
6590
6684
  } else {
@@ -6647,7 +6741,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6647
6741
  case 1:
6648
6742
  if (!(i < maxChecks)) return [
6649
6743
  3,
6650
- 10
6744
+ 12
6651
6745
  ];
6652
6746
  return [
6653
6747
  4,
@@ -6668,7 +6762,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6668
6762
  }
6669
6763
  return [
6670
6764
  3,
6671
- 10
6765
+ 12
6672
6766
  ];
6673
6767
  }
6674
6768
  if (!this.hasTimeToStartAnotherAd()) {
@@ -6677,19 +6771,19 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6677
6771
  }
6678
6772
  return [
6679
6773
  3,
6680
- 10
6774
+ 12
6681
6775
  ];
6682
6776
  }
6683
6777
  _state.label = 3;
6684
6778
  case 3:
6685
6779
  if (!(this.adRequestQueue.length > 0)) return [
6686
6780
  3,
6687
- 9
6781
+ 11
6688
6782
  ];
6689
6783
  if (!this.hasTimeToStartAnotherAd()) {
6690
6784
  return [
6691
6785
  3,
6692
- 9
6786
+ 11
6693
6787
  ];
6694
6788
  }
6695
6789
  nextAdUrl = this.adRequestQueue.shift();
@@ -6716,16 +6810,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6716
6810
  }
6717
6811
  this.isShowingPlaceholder = false;
6718
6812
  this.adPlayer.hidePlaceholder();
6719
- this.currentAdIndex++;
6720
- this.totalAdRequestsInBreak++;
6721
- this.lastAdRequestTime = Date.now();
6722
6813
  _state.label = 4;
6723
6814
  case 4:
6724
6815
  _state.trys.push([
6725
6816
  4,
6726
6817
  6,
6727
6818
  ,
6728
- 8
6819
+ 10
6729
6820
  ]);
6730
6821
  return [
6731
6822
  4,
@@ -6736,11 +6827,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6736
6827
  this.consecutiveFailures = 0;
6737
6828
  return [
6738
6829
  3,
6739
- 8
6830
+ 10
6740
6831
  ];
6741
6832
  case 6:
6742
6833
  error = _state.sent();
6743
6834
  errorMessage = error.message;
6835
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6836
+ 3,
6837
+ 8
6838
+ ];
6839
+ return [
6840
+ 4,
6841
+ this.tryNextAvailableAdWithRateLimit()
6842
+ ];
6843
+ case 7:
6844
+ _state.sent();
6845
+ return [
6846
+ 2
6847
+ ];
6848
+ case 8:
6744
6849
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6745
6850
  this.consecutiveFailures++;
6746
6851
  }
@@ -6748,23 +6853,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6748
6853
  4,
6749
6854
  this.tryNextAvailableAdWithRateLimit()
6750
6855
  ];
6751
- case 7:
6856
+ case 9:
6752
6857
  _state.sent();
6753
6858
  return [
6754
6859
  3,
6755
- 8
6860
+ 10
6756
6861
  ];
6757
- case 8:
6862
+ case 10:
6758
6863
  return [
6759
6864
  2
6760
6865
  ];
6761
- case 9:
6866
+ case 11:
6762
6867
  i++;
6763
6868
  return [
6764
6869
  3,
6765
6870
  1
6766
6871
  ];
6767
- case 10:
6872
+ case 12:
6768
6873
  if (this.config.debugAdTiming) {
6769
6874
  console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout, ending ad break");
6770
6875
  }
@@ -7236,6 +7341,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7236
7341
  2
7237
7342
  ];
7238
7343
  }
7344
+ if (!this.ensureLoadedAdFitsBudget()) {
7345
+ this.rejectLoadedAdForDuration();
7346
+ this.recreateAdController();
7347
+ throw new Error("ad_duration_exceeds_budget");
7348
+ }
7239
7349
  _state.label = 4;
7240
7350
  case 4:
7241
7351
  _state.trys.push([
@@ -7251,6 +7361,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7251
7361
  ];
7252
7362
  case 5:
7253
7363
  _state.sent();
7364
+ this.markAdStarted();
7254
7365
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7255
7366
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7256
7367
  }
@@ -7282,6 +7393,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7282
7393
  ,
7283
7394
  10
7284
7395
  ]);
7396
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback.adController)) {
7397
+ this.rejectLoadedAdForDuration(preloadedFallback.adController);
7398
+ throw playError;
7399
+ }
7285
7400
  this.clearAdFailsafeTimer();
7286
7401
  preservedMuted = this.adPlayer.getOriginalMutedState();
7287
7402
  preservedVolume = this.adPlayer.getOriginalVolume();
@@ -7292,14 +7407,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7292
7407
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
7293
7408
  this.adPlayer.resume();
7294
7409
  this.consecutiveFailures = 0;
7295
- this.currentAdIndex++;
7296
- this.totalAdRequestsInBreak++;
7297
7410
  return [
7298
7411
  4,
7299
7412
  this.adPlayer.play()
7300
7413
  ];
7301
7414
  case 8:
7302
7415
  _state.sent();
7416
+ this.markAdStarted();
7303
7417
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7304
7418
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7305
7419
  }
@@ -7360,6 +7474,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7360
7474
  ,
7361
7475
  16
7362
7476
  ]);
7477
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback1.adController)) {
7478
+ this.rejectLoadedAdForDuration(preloadedFallback1.adController);
7479
+ throw error;
7480
+ }
7363
7481
  this.clearAdRequestWatchdog();
7364
7482
  this.clearAdFailsafeTimer();
7365
7483
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
@@ -7371,14 +7489,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7371
7489
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
7372
7490
  this.adPlayer.resume();
7373
7491
  this.consecutiveFailures = 0;
7374
- this.currentAdIndex++;
7375
- this.totalAdRequestsInBreak++;
7376
7492
  return [
7377
7493
  4,
7378
7494
  this.adPlayer.play()
7379
7495
  ];
7380
7496
  case 14:
7381
7497
  _state.sent();
7498
+ this.markAdStarted();
7382
7499
  this.adPlayer.setAdVolume(this.getAdAudioVolume());
7383
7500
  return [
7384
7501
  2
@@ -7439,6 +7556,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7439
7556
  this.adRequestQueue = [];
7440
7557
  this.inAdBreak = false;
7441
7558
  this.adDetectSentForCurrentBreak = false;
7559
+ this.adBreakPlayedDurationMs = 0;
7442
7560
  this.activeScte35BreakKey = void 0;
7443
7561
  this.scheduledScte35BreakKey = void 0;
7444
7562
  this.expectedAdBreakDurationMs = void 0;
@@ -7628,13 +7746,117 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7628
7746
  }
7629
7747
  },
7630
7748
  {
7631
- key: "getRemainingAdMs",
7632
- value: function getRemainingAdMs() {
7633
- if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) return 0;
7749
+ key: "getWallClockRemainingAdMs",
7750
+ value: function getWallClockRemainingAdMs() {
7751
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
7752
+ return 0;
7753
+ }
7634
7754
  var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
7635
7755
  return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
7636
7756
  }
7637
7757
  },
7758
+ {
7759
+ key: "getDurationBudgetRemainingMs",
7760
+ value: function getDurationBudgetRemainingMs() {
7761
+ if (this.expectedAdBreakDurationMs == null) {
7762
+ return 0;
7763
+ }
7764
+ return Math.max(0, this.expectedAdBreakDurationMs - this.adBreakPlayedDurationMs);
7765
+ }
7766
+ },
7767
+ {
7768
+ key: "getLoadedAdDurationMs",
7769
+ value: function getLoadedAdDurationMs() {
7770
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7771
+ var _adController_getLoadedAdDuration;
7772
+ var durationSec = (_adController_getLoadedAdDuration = adController.getLoadedAdDuration) === null || _adController_getLoadedAdDuration === void 0 ? void 0 : _adController_getLoadedAdDuration.call(adController);
7773
+ if (durationSec == null || durationSec <= 0) {
7774
+ return 0;
7775
+ }
7776
+ return Math.round(durationSec * 1e3);
7777
+ }
7778
+ },
7779
+ {
7780
+ key: "adFitsRemainingBudget",
7781
+ value: function adFitsRemainingBudget(durationMs) {
7782
+ var adController = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : this.adPlayer;
7783
+ if (durationMs <= 0) {
7784
+ return true;
7785
+ }
7786
+ return durationMs <= this.getDurationBudgetRemainingMs();
7787
+ }
7788
+ },
7789
+ {
7790
+ key: "logAdDurationDecision",
7791
+ value: function logAdDurationDecision(accepted, durationMs) {
7792
+ var adController = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : this.adPlayer;
7793
+ var _ref;
7794
+ var _adController_getLoadedAdId;
7795
+ if (!this.config.debugAdTiming) {
7796
+ return;
7797
+ }
7798
+ var remainingBudget = this.getDurationBudgetRemainingMs();
7799
+ var adId = (_ref = (_adController_getLoadedAdId = adController.getLoadedAdId) === null || _adController_getLoadedAdId === void 0 ? void 0 : _adController_getLoadedAdId.call(adController)) !== null && _ref !== void 0 ? _ref : "unknown";
7800
+ if (accepted) {
7801
+ console.log("[AD-DURATION] Accepted ad id=".concat(adId, ", duration=").concat(durationMs, "ms, budget remaining after play=").concat(Math.max(0, remainingBudget - durationMs), "ms"));
7802
+ } else {
7803
+ console.log("[AD-DURATION] Rejected ad id=".concat(adId, ", duration=").concat(durationMs, "ms exceeds remaining budget=").concat(remainingBudget, "ms — requesting another ad"));
7804
+ }
7805
+ }
7806
+ },
7807
+ {
7808
+ key: "recordPlayedAdDuration",
7809
+ value: function recordPlayedAdDuration() {
7810
+ var durationMs = this.getLoadedAdDurationMs();
7811
+ if (durationMs <= 0) {
7812
+ return;
7813
+ }
7814
+ this.adBreakPlayedDurationMs += durationMs;
7815
+ if (this.config.debugAdTiming) {
7816
+ var _this_expectedAdBreakDurationMs;
7817
+ console.log("[AD-DURATION] Played ".concat(durationMs, "ms — total played=").concat(this.adBreakPlayedDurationMs, "ms / ").concat((_this_expectedAdBreakDurationMs = this.expectedAdBreakDurationMs) !== null && _this_expectedAdBreakDurationMs !== void 0 ? _this_expectedAdBreakDurationMs : "?", "ms"));
7818
+ }
7819
+ }
7820
+ },
7821
+ {
7822
+ key: "ensureLoadedAdFitsBudget",
7823
+ value: function ensureLoadedAdFitsBudget() {
7824
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7825
+ var durationMs = this.getLoadedAdDurationMs(adController);
7826
+ var fits = this.adFitsRemainingBudget(durationMs, adController);
7827
+ this.logAdDurationDecision(fits, durationMs, adController);
7828
+ return fits;
7829
+ }
7830
+ },
7831
+ {
7832
+ key: "rejectLoadedAdForDuration",
7833
+ value: function rejectLoadedAdForDuration() {
7834
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7835
+ try {
7836
+ adController.destroy();
7837
+ } catch (unused) {}
7838
+ }
7839
+ },
7840
+ {
7841
+ key: "markAdStarted",
7842
+ value: function markAdStarted() {
7843
+ this.currentAdIndex++;
7844
+ this.totalAdsInBreak = this.currentAdIndex;
7845
+ this.totalAdRequestsInBreak++;
7846
+ this.lastAdRequestTime = Date.now();
7847
+ }
7848
+ },
7849
+ {
7850
+ key: "getRemainingAdMs",
7851
+ value: function getRemainingAdMs() {
7852
+ var wallClockRemaining = this.getWallClockRemainingAdMs();
7853
+ var budgetRemaining = this.getDurationBudgetRemainingMs();
7854
+ if (this.expectedAdBreakDurationMs == null) {
7855
+ return wallClockRemaining;
7856
+ }
7857
+ return Math.min(wallClockRemaining, budgetRemaining);
7858
+ }
7859
+ },
7638
7860
  {
7639
7861
  key: "getMinRemainingMsToStartAd",
7640
7862
  value: function getMinRemainingMsToStartAd() {
@@ -7648,7 +7870,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7648
7870
  {
7649
7871
  key: "hasTimeToStartAnotherAd",
7650
7872
  value: function hasTimeToStartAnotherAd() {
7651
- return this.getRemainingAdMs() > this.getMinRemainingMsToStartAd();
7873
+ var minRemaining = this.getMinRemainingMsToStartAd();
7874
+ return this.getWallClockRemainingAdMs() > minRemaining && this.getDurationBudgetRemainingMs() > minRemaining;
7652
7875
  }
7653
7876
  },
7654
7877
  {