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.
@@ -1240,6 +1240,12 @@ function createHlsAdPlayer(contentVideo, options) {
1240
1240
  contentVideo.style.visibility = "visible";
1241
1241
  contentVideo.style.opacity = "1";
1242
1242
  }
1243
+ },
1244
+ getLoadedAdDuration: function getLoadedAdDuration() {
1245
+ return currentAd === null || currentAd === void 0 ? void 0 : currentAd.duration;
1246
+ },
1247
+ getLoadedAdId: function getLoadedAdId() {
1248
+ return currentAd === null || currentAd === void 0 ? void 0 : currentAd.id;
1243
1249
  }
1244
1250
  };
1245
1251
  }
@@ -2652,6 +2658,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2652
2658
  this.preloadPoolLoopRunning = false;
2653
2659
  this.adDetectSentForCurrentBreak = false;
2654
2660
  this.adBreakEpoch = 0;
2661
+ this.adBreakPlayedDurationMs = 0;
2655
2662
  this.palPlaybackStarted = false;
2656
2663
  this.streamCorrelator = generateCorrelator();
2657
2664
  this.consentSignals = {};
@@ -3148,6 +3155,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3148
3155
  adIndex: _this.currentAdIndex,
3149
3156
  timestamp: /* @__PURE__ */ new Date().toISOString()
3150
3157
  }).catch(function() {});
3158
+ _this.recordPlayedAdDuration();
3151
3159
  var remaining = _this.getRemainingAdMs();
3152
3160
  _this.consecutiveFailures = 0;
3153
3161
  if (_this.config.debugAdTiming) {
@@ -3223,6 +3231,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3223
3231
  return;
3224
3232
  }
3225
3233
  _this.consecutiveFailures = 0;
3234
+ _this.recordPlayedAdDuration();
3226
3235
  var remaining = _this.getRemainingAdMs();
3227
3236
  if (_this.config.debugAdTiming) {
3228
3237
  console.log("[CONTINUOUS-FETCH] content_resume event: remaining=".concat(remaining, "ms, queued ads=").concat(_this.adRequestQueue.length), _this.adRequestQueue.length > 0 ? {
@@ -5116,7 +5125,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5116
5125
  }
5117
5126
  this.beginNewAdPod();
5118
5127
  this.podAssignedByPrefetch = true;
5119
- var urlsToPregenerate = 5;
5128
+ var urlsToPregenerate = 1;
5120
5129
  var generatedUrls = this.generateVastUrlsWithCorrelators(baseVastUrl, urlsToPregenerate);
5121
5130
  this.pendingAdBreak = _object_spread_props(_object_spread({
5122
5131
  marker: marker
@@ -5141,7 +5150,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5141
5150
  console.log("[PREFETCH] \uD83D\uDD04 Starting ad prefetch for upcoming ad break");
5142
5151
  console.log("[PREFETCH] \uD83D\uDCCB Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
5143
5152
  }
5144
- this.startPreloadPool(baseVastUrl, generatedUrls.slice(0, this.maxPreloadPoolSize));
5153
+ this.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
5145
5154
  }
5146
5155
  },
5147
5156
  {
@@ -5159,6 +5168,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5159
5168
  {
5160
5169
  key: "startPreloadPool",
5161
5170
  value: function startPreloadPool(baseVastUrl, initialUrls) {
5171
+ var startLoopImmediately = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : true;
5162
5172
  if (this.preloadPoolActive) {
5163
5173
  if (this.config.debugAdTiming) {
5164
5174
  console.log("[PRELOAD-POOL] Already active, skipping duplicate start");
@@ -5192,7 +5202,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5192
5202
  }
5193
5203
  }
5194
5204
  }
5195
- this.preloadPoolLoop(baseVastUrl);
5205
+ if (startLoopImmediately) {
5206
+ this.preloadPoolLoop(baseVastUrl);
5207
+ } else if (this.config.debugAdTiming) {
5208
+ console.log("[PRELOAD-POOL] Pre-break mode: preloaded once, waiting for SCTE start to continue fetching");
5209
+ }
5196
5210
  }
5197
5211
  },
5198
5212
  {
@@ -5228,7 +5242,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5228
5242
  epoch = this.adBreakEpoch;
5229
5243
  loadPromise = function() {
5230
5244
  return _async_to_generator(function() {
5231
- var _this, hasAdError, adErrorPayload, errorListenerCleanup, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, lateErrorListener, error;
5245
+ var _this, hasAdError, adErrorPayload, errorListenerCleanup, _preloadAd_getLoadedAdDuration, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, loadedDuration, lateErrorListener, _poolEntry_durationSeconds, error;
5232
5246
  return _ts_generator(this, function(_state) {
5233
5247
  switch(_state.label){
5234
5248
  case 0:
@@ -5324,6 +5338,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5324
5338
  isReady: true,
5325
5339
  loadPromise: Promise.resolve()
5326
5340
  };
5341
+ loadedDuration = (_preloadAd_getLoadedAdDuration = preloadAd.getLoadedAdDuration) === null || _preloadAd_getLoadedAdDuration === void 0 ? void 0 : _preloadAd_getLoadedAdDuration.call(preloadAd);
5342
+ if (loadedDuration != null && loadedDuration > 0) {
5343
+ poolEntry.durationSeconds = loadedDuration;
5344
+ }
5327
5345
  lateErrorListener = function lateErrorListener(payload) {
5328
5346
  var index = _this.preloadPool.findIndex(function(entry) {
5329
5347
  return entry.vastUrl === vastUrl;
@@ -5341,7 +5359,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5341
5359
  preloadAd.on("ad_error", lateErrorListener);
5342
5360
  this.preloadPool.push(poolEntry);
5343
5361
  if (this.config.debugAdTiming) {
5344
- console.log("[PRELOAD-POOL] ✅ Ad preloaded (no errors detected within timeout) (pool size: ".concat(this.preloadPool.length, "/").concat(this.maxPreloadPoolSize, ")"));
5362
+ ;
5363
+ 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)"));
5345
5364
  }
5346
5365
  if (errorListenerCleanup) {
5347
5366
  errorListenerCleanup();
@@ -5622,8 +5641,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5622
5641
  }
5623
5642
  this.inAdBreak = true;
5624
5643
  this.currentAdBreakStartWallClockMs = Date.now();
5644
+ this.adBreakPlayedDurationMs = 0;
5625
5645
  this.currentAdIndex = 0;
5626
- this.totalAdsInBreak = 1;
5646
+ this.totalAdsInBreak = 0;
5627
5647
  this.adPodQueue = [];
5628
5648
  this.showAds = true;
5629
5649
  this.showPlaceholderLayer();
@@ -5677,14 +5697,30 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5677
5697
  case 1:
5678
5698
  _state.trys.push([
5679
5699
  1,
5680
- 8,
5700
+ 12,
5681
5701
  ,
5682
- 14
5702
+ 20
5683
5703
  ]);
5684
5704
  if (!(usePreloadedAd && preloadedController)) return [
5705
+ 3,
5706
+ 5
5707
+ ];
5708
+ if (!!this.ensureLoadedAdFitsBudget(preloadedController)) return [
5685
5709
  3,
5686
5710
  3
5687
5711
  ];
5712
+ this.rejectLoadedAdForDuration(preloadedController);
5713
+ this.startContinuousFetching(baseVastUrl);
5714
+ return [
5715
+ 4,
5716
+ this.tryNextAvailableAdWithRateLimit()
5717
+ ];
5718
+ case 2:
5719
+ _state.sent();
5720
+ return [
5721
+ 2
5722
+ ];
5723
+ case 3:
5688
5724
  preservedMuted = this.adPlayer.getOriginalMutedState();
5689
5725
  preservedVolume = this.adPlayer.getOriginalVolume();
5690
5726
  this.adPlayer.destroy();
@@ -5697,18 +5733,20 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5697
5733
  }
5698
5734
  this.adPlayer.resume();
5699
5735
  this.consecutiveFailures = 0;
5700
- this.currentAdIndex++;
5701
5736
  this.startContinuousFetching(baseVastUrl);
5702
5737
  if (!this.preloadPoolActive) {
5703
5738
  this.preloadPoolActive = true;
5739
+ }
5740
+ if (!this.preloadPoolLoopRunning) {
5704
5741
  this.preloadPoolLoop(baseVastUrl);
5705
5742
  }
5706
5743
  return [
5707
5744
  4,
5708
5745
  this.adPlayer.play()
5709
5746
  ];
5710
- case 2:
5747
+ case 4:
5711
5748
  _state.sent();
5749
+ this.markAdStarted();
5712
5750
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5713
5751
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5714
5752
  }
@@ -5716,21 +5754,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5716
5754
  this.adPlayer.setAdVolume(adVolume);
5717
5755
  return [
5718
5756
  3,
5719
- 7
5757
+ 11
5720
5758
  ];
5721
- case 3:
5759
+ case 5:
5722
5760
  return [
5723
5761
  4,
5724
5762
  this.enforceGlobalRateLimit()
5725
5763
  ];
5726
- case 4:
5764
+ case 6:
5727
5765
  _state.sent();
5728
5766
  this.lastAdRequestTime = Date.now();
5729
5767
  return [
5730
5768
  4,
5731
5769
  this.adPlayer.requestAds(firstAdUrl)
5732
5770
  ];
5733
- case 5:
5771
+ case 7:
5734
5772
  _state.sent();
5735
5773
  sendAdLoadedTracking(this.config.licenseKey, {
5736
5774
  source: "hls",
@@ -5740,8 +5778,24 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5740
5778
  if (this.config.debugAdTiming) {
5741
5779
  console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
5742
5780
  }
5781
+ if (!!this.ensureLoadedAdFitsBudget()) return [
5782
+ 3,
5783
+ 9
5784
+ ];
5785
+ this.rejectLoadedAdForDuration();
5786
+ this.recreateAdController();
5787
+ this.startContinuousFetching(baseVastUrl);
5788
+ return [
5789
+ 4,
5790
+ this.tryNextAvailableAdWithRateLimit()
5791
+ ];
5792
+ case 8:
5793
+ _state.sent();
5794
+ return [
5795
+ 2
5796
+ ];
5797
+ case 9:
5743
5798
  this.consecutiveFailures = 0;
5744
- this.currentAdIndex++;
5745
5799
  this.startContinuousFetching(baseVastUrl);
5746
5800
  if (!this.preloadPoolActive) {
5747
5801
  this.startPreloadPool(baseVastUrl, []);
@@ -5750,44 +5804,61 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5750
5804
  4,
5751
5805
  this.adPlayer.play()
5752
5806
  ];
5753
- case 6:
5807
+ case 10:
5754
5808
  _state.sent();
5809
+ this.markAdStarted();
5755
5810
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5756
5811
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5757
5812
  }
5758
5813
  adVolume1 = currentMuted ? 0 : currentVolume;
5759
5814
  this.adPlayer.setAdVolume(adVolume1);
5760
- _state.label = 7;
5761
- case 7:
5815
+ _state.label = 11;
5816
+ case 11:
5762
5817
  return [
5763
5818
  3,
5764
- 14
5819
+ 20
5765
5820
  ];
5766
- case 8:
5821
+ case 12:
5767
5822
  error = _state.sent();
5768
5823
  if (this.config.debugAdTiming) {
5769
5824
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F First ad request failed:", error);
5770
5825
  }
5771
5826
  if (!!usePreloadedAd) return [
5772
5827
  3,
5773
- 12
5828
+ 18
5774
5829
  ];
5775
5830
  fallbackPreloaded = this.getPreloadedAd();
5776
5831
  if (!fallbackPreloaded) return [
5777
5832
  3,
5778
- 12
5833
+ 18
5779
5834
  ];
5780
5835
  if (this.config.debugAdTiming) {
5781
5836
  console.log("[CONTINUOUS-FETCH] \uD83D\uDD04 First ad failed, using preloaded fallback");
5782
5837
  }
5783
- _state.label = 9;
5784
- case 9:
5838
+ _state.label = 13;
5839
+ case 13:
5785
5840
  _state.trys.push([
5786
- 9,
5787
- 11,
5841
+ 13,
5842
+ 17,
5788
5843
  ,
5789
- 12
5844
+ 18
5790
5845
  ]);
5846
+ if (!!this.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
5847
+ 3,
5848
+ 15
5849
+ ];
5850
+ this.rejectLoadedAdForDuration(fallbackPreloaded.adController);
5851
+ this.startContinuousFetching(baseVastUrl);
5852
+ return [
5853
+ 4,
5854
+ this.tryNextAvailableAdWithRateLimit()
5855
+ ];
5856
+ case 14:
5857
+ _state.sent();
5858
+ return [
5859
+ 2
5860
+ ];
5861
+ case 15:
5791
5862
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
5792
5863
  preservedVolume1 = this.adPlayer.getOriginalVolume();
5793
5864
  this.adPlayer.destroy();
@@ -5797,7 +5868,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5797
5868
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
5798
5869
  this.adPlayer.resume();
5799
5870
  this.consecutiveFailures = 0;
5800
- this.currentAdIndex++;
5801
5871
  this.startContinuousFetching(baseVastUrl);
5802
5872
  if (!this.preloadPoolActive) {
5803
5873
  this.preloadPoolActive = true;
@@ -5807,8 +5877,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5807
5877
  4,
5808
5878
  this.adPlayer.play()
5809
5879
  ];
5810
- case 10:
5880
+ case 16:
5811
5881
  _state.sent();
5882
+ this.markAdStarted();
5812
5883
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5813
5884
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5814
5885
  }
@@ -5817,16 +5888,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5817
5888
  return [
5818
5889
  2
5819
5890
  ];
5820
- case 11:
5891
+ case 17:
5821
5892
  fallbackError = _state.sent();
5822
5893
  if (this.config.debugAdTiming) {
5823
5894
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded fallback also failed:", fallbackError);
5824
5895
  }
5825
5896
  return [
5826
5897
  3,
5827
- 12
5898
+ 18
5828
5899
  ];
5829
- case 12:
5900
+ case 18:
5830
5901
  if (this.isTemporaryAdError(error)) {
5831
5902
  this.temporaryFailureUrls.set(firstAdUrl, Date.now());
5832
5903
  if (this.config.debugAdTiming) {
@@ -5844,13 +5915,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5844
5915
  4,
5845
5916
  this.tryNextAvailableAdWithRateLimit()
5846
5917
  ];
5847
- case 13:
5918
+ case 19:
5848
5919
  _state.sent();
5849
5920
  return [
5850
5921
  3,
5851
- 14
5922
+ 20
5852
5923
  ];
5853
- case 14:
5924
+ case 20:
5854
5925
  return [
5855
5926
  2
5856
5927
  ];
@@ -5891,10 +5962,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5891
5962
  return _ts_generator(this, function(_state) {
5892
5963
  switch(_state.label){
5893
5964
  case 0:
5894
- remaining = _this.getRemainingAdMs();
5965
+ remaining = Math.min(_this.getWallClockRemainingAdMs(), _this.getDurationBudgetRemainingMs());
5895
5966
  if (remaining <= _this.getMinRemainingMsToStartAd()) {
5896
5967
  if (_this.config.debugAdTiming) {
5897
- console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (".concat(remaining, "ms), stopping URL generation"));
5968
+ console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (wall=".concat(_this.getWallClockRemainingAdMs(), "ms, budget=").concat(_this.getDurationBudgetRemainingMs(), "ms), stopping URL generation"));
5898
5969
  }
5899
5970
  return [
5900
5971
  2,
@@ -5976,7 +6047,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5976
6047
  });
5977
6048
  }
5978
6049
  _this.adRequestQueue.push(newAdUrl);
5979
- _this.totalAdsInBreak++;
5980
6050
  generationDelay = _this.consecutiveFailures > 0 ? Math.min(1e3 * Math.pow(2, _this.consecutiveFailures), 5e3) : 500;
5981
6051
  return [
5982
6052
  4,
@@ -6145,7 +6215,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6145
6215
  preloaded = this.getPreloadedAd();
6146
6216
  if (!preloaded) return [
6147
6217
  3,
6148
- 4
6218
+ 6
6149
6219
  ];
6150
6220
  if (this.config.debugAdTiming) {
6151
6221
  console.log("[CONTINUOUS-FETCH] \uD83C\uDFAF Using preloaded ad from pool (".concat(this.preloadPool.length, " remaining in pool)"));
@@ -6159,10 +6229,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6159
6229
  case 1:
6160
6230
  _state.trys.push([
6161
6231
  1,
6162
- 3,
6232
+ 5,
6163
6233
  ,
6164
- 4
6234
+ 6
6165
6235
  ]);
6236
+ if (!!this.ensureLoadedAdFitsBudget(preloaded.adController)) return [
6237
+ 3,
6238
+ 3
6239
+ ];
6240
+ this.rejectLoadedAdForDuration(preloaded.adController);
6241
+ return [
6242
+ 4,
6243
+ this.tryNextAvailableAdWithRateLimit()
6244
+ ];
6245
+ case 2:
6246
+ _state.sent();
6247
+ return [
6248
+ 2
6249
+ ];
6250
+ case 3:
6166
6251
  preservedMuted = this.adPlayer.getOriginalMutedState();
6167
6252
  preservedVolume = this.adPlayer.getOriginalVolume();
6168
6253
  this.adPlayer.destroy();
@@ -6171,15 +6256,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6171
6256
  this.attachImaEventListeners();
6172
6257
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
6173
6258
  this.adPlayer.resume();
6174
- this.currentAdIndex++;
6175
- this.totalAdRequestsInBreak++;
6176
- this.lastAdRequestTime = Date.now();
6177
6259
  return [
6178
6260
  4,
6179
6261
  this.adPlayer.play()
6180
6262
  ];
6181
- case 2:
6263
+ case 4:
6182
6264
  _state.sent();
6265
+ this.markAdStarted();
6183
6266
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6184
6267
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
6185
6268
  }
@@ -6188,25 +6271,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6188
6271
  return [
6189
6272
  2
6190
6273
  ];
6191
- case 3:
6274
+ case 5:
6192
6275
  error = _state.sent();
6193
6276
  if (this.config.debugAdTiming) {
6194
6277
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded ad failed to play:", error);
6195
6278
  }
6196
6279
  return [
6197
6280
  3,
6198
- 4
6281
+ 6
6199
6282
  ];
6200
- case 4:
6283
+ case 6:
6201
6284
  if (!(this.adRequestQueue.length > 0)) return [
6202
6285
  3,
6203
- 10
6286
+ 14
6204
6287
  ];
6205
6288
  nextAdUrl = this.adRequestQueue.shift();
6206
6289
  if (!nextAdUrl) {
6207
6290
  return [
6208
6291
  3,
6209
- 4
6292
+ 6
6210
6293
  ];
6211
6294
  }
6212
6295
  if (this.failedVastUrls.has(nextAdUrl)) {
@@ -6215,7 +6298,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6215
6298
  }
6216
6299
  return [
6217
6300
  3,
6218
- 4
6301
+ 6
6219
6302
  ];
6220
6303
  }
6221
6304
  if (this.isUrlInCooldown(nextAdUrl)) {
@@ -6224,40 +6307,51 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6224
6307
  }
6225
6308
  return [
6226
6309
  3,
6227
- 4
6310
+ 6
6228
6311
  ];
6229
6312
  }
6230
6313
  if (this.config.debugAdTiming) {
6231
- 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)"));
6314
+ console.log("[CONTINUOUS-FETCH] \uD83C\uDFAC Requesting next ad via HLS ad player (".concat(this.currentAdIndex + 1, ", ").concat(this.adRequestQueue.length, " remaining in queue)"));
6232
6315
  }
6233
- this.currentAdIndex++;
6234
- this.totalAdRequestsInBreak++;
6235
- this.lastAdRequestTime = Date.now();
6236
- _state.label = 5;
6237
- case 5:
6316
+ _state.label = 7;
6317
+ case 7:
6238
6318
  _state.trys.push([
6239
- 5,
6240
6319
  7,
6320
+ 9,
6241
6321
  ,
6242
- 9
6322
+ 13
6243
6323
  ]);
6244
6324
  return [
6245
6325
  4,
6246
6326
  this.playSingleAd(nextAdUrl)
6247
6327
  ];
6248
- case 6:
6328
+ case 8:
6249
6329
  _state.sent();
6250
6330
  this.consecutiveFailures = 0;
6251
6331
  return [
6252
6332
  3,
6253
- 9
6333
+ 13
6254
6334
  ];
6255
- case 7:
6335
+ case 9:
6256
6336
  error1 = _state.sent();
6257
6337
  errorMessage = error1.message;
6258
6338
  if (this.config.debugAdTiming) {
6259
6339
  console.log("[CONTINUOUS-FETCH] \u274C Ad request failed:", errorMessage);
6260
6340
  }
6341
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6342
+ 3,
6343
+ 11
6344
+ ];
6345
+ return [
6346
+ 4,
6347
+ this.tryNextAvailableAdWithRateLimit()
6348
+ ];
6349
+ case 10:
6350
+ _state.sent();
6351
+ return [
6352
+ 2
6353
+ ];
6354
+ case 11:
6261
6355
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6262
6356
  this.consecutiveFailures++;
6263
6357
  }
@@ -6265,21 +6359,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6265
6359
  4,
6266
6360
  this.tryNextAvailableAdWithRateLimit()
6267
6361
  ];
6268
- case 8:
6362
+ case 12:
6269
6363
  _state.sent();
6270
6364
  return [
6271
6365
  3,
6272
- 9
6366
+ 13
6273
6367
  ];
6274
- case 9:
6368
+ case 13:
6275
6369
  return [
6276
6370
  2
6277
6371
  ];
6278
- case 10:
6372
+ case 14:
6279
6373
  maxRetries = 3;
6280
6374
  if (!(this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3)) return [
6281
6375
  3,
6282
- 13
6376
+ 17
6283
6377
  ];
6284
6378
  if (this.config.debugAdTiming) {
6285
6379
  console.log("[CONTINUOUS-FETCH] ⏳ Queue empty, waiting for URLs... (".concat(retryCount + 1, "/").concat(maxRetries, ")"));
@@ -6290,18 +6384,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6290
6384
  return setTimeout(resolve, 500);
6291
6385
  })
6292
6386
  ];
6293
- case 11:
6387
+ case 15:
6294
6388
  _state.sent();
6295
6389
  return [
6296
6390
  4,
6297
6391
  this.tryNextAvailableAd(retryCount + 1)
6298
6392
  ];
6299
- case 12:
6393
+ case 16:
6300
6394
  _state.sent();
6301
6395
  return [
6302
6396
  2
6303
6397
  ];
6304
- case 13:
6398
+ case 17:
6305
6399
  if (!this.isShowingPlaceholder && remaining > 1e3) {
6306
6400
  this.showPlaceholderAndWaitForAds();
6307
6401
  } else {
@@ -6364,7 +6458,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6364
6458
  case 1:
6365
6459
  if (!(i < maxChecks)) return [
6366
6460
  3,
6367
- 10
6461
+ 12
6368
6462
  ];
6369
6463
  return [
6370
6464
  4,
@@ -6385,7 +6479,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6385
6479
  }
6386
6480
  return [
6387
6481
  3,
6388
- 10
6482
+ 12
6389
6483
  ];
6390
6484
  }
6391
6485
  if (!this.hasTimeToStartAnotherAd()) {
@@ -6394,19 +6488,19 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6394
6488
  }
6395
6489
  return [
6396
6490
  3,
6397
- 10
6491
+ 12
6398
6492
  ];
6399
6493
  }
6400
6494
  _state.label = 3;
6401
6495
  case 3:
6402
6496
  if (!(this.adRequestQueue.length > 0)) return [
6403
6497
  3,
6404
- 9
6498
+ 11
6405
6499
  ];
6406
6500
  if (!this.hasTimeToStartAnotherAd()) {
6407
6501
  return [
6408
6502
  3,
6409
- 9
6503
+ 11
6410
6504
  ];
6411
6505
  }
6412
6506
  nextAdUrl = this.adRequestQueue.shift();
@@ -6433,16 +6527,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6433
6527
  }
6434
6528
  this.isShowingPlaceholder = false;
6435
6529
  this.adPlayer.hidePlaceholder();
6436
- this.currentAdIndex++;
6437
- this.totalAdRequestsInBreak++;
6438
- this.lastAdRequestTime = Date.now();
6439
6530
  _state.label = 4;
6440
6531
  case 4:
6441
6532
  _state.trys.push([
6442
6533
  4,
6443
6534
  6,
6444
6535
  ,
6445
- 8
6536
+ 10
6446
6537
  ]);
6447
6538
  return [
6448
6539
  4,
@@ -6453,11 +6544,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6453
6544
  this.consecutiveFailures = 0;
6454
6545
  return [
6455
6546
  3,
6456
- 8
6547
+ 10
6457
6548
  ];
6458
6549
  case 6:
6459
6550
  error = _state.sent();
6460
6551
  errorMessage = error.message;
6552
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6553
+ 3,
6554
+ 8
6555
+ ];
6556
+ return [
6557
+ 4,
6558
+ this.tryNextAvailableAdWithRateLimit()
6559
+ ];
6560
+ case 7:
6561
+ _state.sent();
6562
+ return [
6563
+ 2
6564
+ ];
6565
+ case 8:
6461
6566
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6462
6567
  this.consecutiveFailures++;
6463
6568
  }
@@ -6465,23 +6570,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6465
6570
  4,
6466
6571
  this.tryNextAvailableAdWithRateLimit()
6467
6572
  ];
6468
- case 7:
6573
+ case 9:
6469
6574
  _state.sent();
6470
6575
  return [
6471
6576
  3,
6472
- 8
6577
+ 10
6473
6578
  ];
6474
- case 8:
6579
+ case 10:
6475
6580
  return [
6476
6581
  2
6477
6582
  ];
6478
- case 9:
6583
+ case 11:
6479
6584
  i++;
6480
6585
  return [
6481
6586
  3,
6482
6587
  1
6483
6588
  ];
6484
- case 10:
6589
+ case 12:
6485
6590
  if (this.config.debugAdTiming) {
6486
6591
  console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout, ending ad break");
6487
6592
  }
@@ -6953,6 +7058,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6953
7058
  2
6954
7059
  ];
6955
7060
  }
7061
+ if (!this.ensureLoadedAdFitsBudget()) {
7062
+ this.rejectLoadedAdForDuration();
7063
+ this.recreateAdController();
7064
+ throw new Error("ad_duration_exceeds_budget");
7065
+ }
6956
7066
  _state.label = 4;
6957
7067
  case 4:
6958
7068
  _state.trys.push([
@@ -6968,6 +7078,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6968
7078
  ];
6969
7079
  case 5:
6970
7080
  _state.sent();
7081
+ this.markAdStarted();
6971
7082
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6972
7083
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
6973
7084
  }
@@ -6999,6 +7110,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6999
7110
  ,
7000
7111
  10
7001
7112
  ]);
7113
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback.adController)) {
7114
+ this.rejectLoadedAdForDuration(preloadedFallback.adController);
7115
+ throw playError;
7116
+ }
7002
7117
  this.clearAdFailsafeTimer();
7003
7118
  preservedMuted = this.adPlayer.getOriginalMutedState();
7004
7119
  preservedVolume = this.adPlayer.getOriginalVolume();
@@ -7009,14 +7124,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7009
7124
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
7010
7125
  this.adPlayer.resume();
7011
7126
  this.consecutiveFailures = 0;
7012
- this.currentAdIndex++;
7013
- this.totalAdRequestsInBreak++;
7014
7127
  return [
7015
7128
  4,
7016
7129
  this.adPlayer.play()
7017
7130
  ];
7018
7131
  case 8:
7019
7132
  _state.sent();
7133
+ this.markAdStarted();
7020
7134
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7021
7135
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7022
7136
  }
@@ -7077,6 +7191,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7077
7191
  ,
7078
7192
  16
7079
7193
  ]);
7194
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback1.adController)) {
7195
+ this.rejectLoadedAdForDuration(preloadedFallback1.adController);
7196
+ throw error;
7197
+ }
7080
7198
  this.clearAdRequestWatchdog();
7081
7199
  this.clearAdFailsafeTimer();
7082
7200
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
@@ -7088,14 +7206,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7088
7206
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
7089
7207
  this.adPlayer.resume();
7090
7208
  this.consecutiveFailures = 0;
7091
- this.currentAdIndex++;
7092
- this.totalAdRequestsInBreak++;
7093
7209
  return [
7094
7210
  4,
7095
7211
  this.adPlayer.play()
7096
7212
  ];
7097
7213
  case 14:
7098
7214
  _state.sent();
7215
+ this.markAdStarted();
7099
7216
  this.adPlayer.setAdVolume(this.getAdAudioVolume());
7100
7217
  return [
7101
7218
  2
@@ -7156,6 +7273,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7156
7273
  this.adRequestQueue = [];
7157
7274
  this.inAdBreak = false;
7158
7275
  this.adDetectSentForCurrentBreak = false;
7276
+ this.adBreakPlayedDurationMs = 0;
7159
7277
  this.activeScte35BreakKey = void 0;
7160
7278
  this.scheduledScte35BreakKey = void 0;
7161
7279
  this.expectedAdBreakDurationMs = void 0;
@@ -7345,13 +7463,117 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7345
7463
  }
7346
7464
  },
7347
7465
  {
7348
- key: "getRemainingAdMs",
7349
- value: function getRemainingAdMs() {
7350
- if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) return 0;
7466
+ key: "getWallClockRemainingAdMs",
7467
+ value: function getWallClockRemainingAdMs() {
7468
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
7469
+ return 0;
7470
+ }
7351
7471
  var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
7352
7472
  return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
7353
7473
  }
7354
7474
  },
7475
+ {
7476
+ key: "getDurationBudgetRemainingMs",
7477
+ value: function getDurationBudgetRemainingMs() {
7478
+ if (this.expectedAdBreakDurationMs == null) {
7479
+ return 0;
7480
+ }
7481
+ return Math.max(0, this.expectedAdBreakDurationMs - this.adBreakPlayedDurationMs);
7482
+ }
7483
+ },
7484
+ {
7485
+ key: "getLoadedAdDurationMs",
7486
+ value: function getLoadedAdDurationMs() {
7487
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7488
+ var _adController_getLoadedAdDuration;
7489
+ var durationSec = (_adController_getLoadedAdDuration = adController.getLoadedAdDuration) === null || _adController_getLoadedAdDuration === void 0 ? void 0 : _adController_getLoadedAdDuration.call(adController);
7490
+ if (durationSec == null || durationSec <= 0) {
7491
+ return 0;
7492
+ }
7493
+ return Math.round(durationSec * 1e3);
7494
+ }
7495
+ },
7496
+ {
7497
+ key: "adFitsRemainingBudget",
7498
+ value: function adFitsRemainingBudget(durationMs) {
7499
+ var adController = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : this.adPlayer;
7500
+ if (durationMs <= 0) {
7501
+ return true;
7502
+ }
7503
+ return durationMs <= this.getDurationBudgetRemainingMs();
7504
+ }
7505
+ },
7506
+ {
7507
+ key: "logAdDurationDecision",
7508
+ value: function logAdDurationDecision(accepted, durationMs) {
7509
+ var adController = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : this.adPlayer;
7510
+ var _ref;
7511
+ var _adController_getLoadedAdId;
7512
+ if (!this.config.debugAdTiming) {
7513
+ return;
7514
+ }
7515
+ var remainingBudget = this.getDurationBudgetRemainingMs();
7516
+ var adId = (_ref = (_adController_getLoadedAdId = adController.getLoadedAdId) === null || _adController_getLoadedAdId === void 0 ? void 0 : _adController_getLoadedAdId.call(adController)) !== null && _ref !== void 0 ? _ref : "unknown";
7517
+ if (accepted) {
7518
+ console.log("[AD-DURATION] Accepted ad id=".concat(adId, ", duration=").concat(durationMs, "ms, budget remaining after play=").concat(Math.max(0, remainingBudget - durationMs), "ms"));
7519
+ } else {
7520
+ console.log("[AD-DURATION] Rejected ad id=".concat(adId, ", duration=").concat(durationMs, "ms exceeds remaining budget=").concat(remainingBudget, "ms — requesting another ad"));
7521
+ }
7522
+ }
7523
+ },
7524
+ {
7525
+ key: "recordPlayedAdDuration",
7526
+ value: function recordPlayedAdDuration() {
7527
+ var durationMs = this.getLoadedAdDurationMs();
7528
+ if (durationMs <= 0) {
7529
+ return;
7530
+ }
7531
+ this.adBreakPlayedDurationMs += durationMs;
7532
+ if (this.config.debugAdTiming) {
7533
+ var _this_expectedAdBreakDurationMs;
7534
+ 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"));
7535
+ }
7536
+ }
7537
+ },
7538
+ {
7539
+ key: "ensureLoadedAdFitsBudget",
7540
+ value: function ensureLoadedAdFitsBudget() {
7541
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7542
+ var durationMs = this.getLoadedAdDurationMs(adController);
7543
+ var fits = this.adFitsRemainingBudget(durationMs, adController);
7544
+ this.logAdDurationDecision(fits, durationMs, adController);
7545
+ return fits;
7546
+ }
7547
+ },
7548
+ {
7549
+ key: "rejectLoadedAdForDuration",
7550
+ value: function rejectLoadedAdForDuration() {
7551
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7552
+ try {
7553
+ adController.destroy();
7554
+ } catch (unused) {}
7555
+ }
7556
+ },
7557
+ {
7558
+ key: "markAdStarted",
7559
+ value: function markAdStarted() {
7560
+ this.currentAdIndex++;
7561
+ this.totalAdsInBreak = this.currentAdIndex;
7562
+ this.totalAdRequestsInBreak++;
7563
+ this.lastAdRequestTime = Date.now();
7564
+ }
7565
+ },
7566
+ {
7567
+ key: "getRemainingAdMs",
7568
+ value: function getRemainingAdMs() {
7569
+ var wallClockRemaining = this.getWallClockRemainingAdMs();
7570
+ var budgetRemaining = this.getDurationBudgetRemainingMs();
7571
+ if (this.expectedAdBreakDurationMs == null) {
7572
+ return wallClockRemaining;
7573
+ }
7574
+ return Math.min(wallClockRemaining, budgetRemaining);
7575
+ }
7576
+ },
7355
7577
  {
7356
7578
  key: "getMinRemainingMsToStartAd",
7357
7579
  value: function getMinRemainingMsToStartAd() {
@@ -7365,7 +7587,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7365
7587
  {
7366
7588
  key: "hasTimeToStartAnotherAd",
7367
7589
  value: function hasTimeToStartAnotherAd() {
7368
- return this.getRemainingAdMs() > this.getMinRemainingMsToStartAd();
7590
+ var minRemaining = this.getMinRemainingMsToStartAd();
7591
+ return this.getWallClockRemainingAdMs() > minRemaining && this.getDurationBudgetRemainingMs() > minRemaining;
7369
7592
  }
7370
7593
  },
7371
7594
  {