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.
@@ -1278,6 +1278,12 @@ function createHlsAdPlayer(contentVideo, options) {
1278
1278
  contentVideo.style.visibility = "visible";
1279
1279
  contentVideo.style.opacity = "1";
1280
1280
  }
1281
+ },
1282
+ getLoadedAdDuration: function getLoadedAdDuration() {
1283
+ return currentAd === null || currentAd === void 0 ? void 0 : currentAd.duration;
1284
+ },
1285
+ getLoadedAdId: function getLoadedAdId() {
1286
+ return currentAd === null || currentAd === void 0 ? void 0 : currentAd.id;
1281
1287
  }
1282
1288
  };
1283
1289
  }
@@ -2690,6 +2696,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2690
2696
  this.preloadPoolLoopRunning = false;
2691
2697
  this.adDetectSentForCurrentBreak = false;
2692
2698
  this.adBreakEpoch = 0;
2699
+ this.adBreakPlayedDurationMs = 0;
2693
2700
  this.palPlaybackStarted = false;
2694
2701
  this.streamCorrelator = generateCorrelator();
2695
2702
  this.consentSignals = {};
@@ -3186,6 +3193,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3186
3193
  adIndex: _this.currentAdIndex,
3187
3194
  timestamp: /* @__PURE__ */ new Date().toISOString()
3188
3195
  }).catch(function() {});
3196
+ _this.recordPlayedAdDuration();
3189
3197
  var remaining = _this.getRemainingAdMs();
3190
3198
  _this.consecutiveFailures = 0;
3191
3199
  if (_this.config.debugAdTiming) {
@@ -3261,6 +3269,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3261
3269
  return;
3262
3270
  }
3263
3271
  _this.consecutiveFailures = 0;
3272
+ _this.recordPlayedAdDuration();
3264
3273
  var remaining = _this.getRemainingAdMs();
3265
3274
  if (_this.config.debugAdTiming) {
3266
3275
  console.log("[CONTINUOUS-FETCH] content_resume event: remaining=".concat(remaining, "ms, queued ads=").concat(_this.adRequestQueue.length), _this.adRequestQueue.length > 0 ? {
@@ -5154,7 +5163,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5154
5163
  }
5155
5164
  this.beginNewAdPod();
5156
5165
  this.podAssignedByPrefetch = true;
5157
- var urlsToPregenerate = 5;
5166
+ var urlsToPregenerate = 1;
5158
5167
  var generatedUrls = this.generateVastUrlsWithCorrelators(baseVastUrl, urlsToPregenerate);
5159
5168
  this.pendingAdBreak = _object_spread_props(_object_spread({
5160
5169
  marker: marker
@@ -5179,7 +5188,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5179
5188
  console.log("[PREFETCH] \uD83D\uDD04 Starting ad prefetch for upcoming ad break");
5180
5189
  console.log("[PREFETCH] \uD83D\uDCCB Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
5181
5190
  }
5182
- this.startPreloadPool(baseVastUrl, generatedUrls.slice(0, this.maxPreloadPoolSize));
5191
+ this.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
5183
5192
  }
5184
5193
  },
5185
5194
  {
@@ -5197,6 +5206,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5197
5206
  {
5198
5207
  key: "startPreloadPool",
5199
5208
  value: function startPreloadPool(baseVastUrl, initialUrls) {
5209
+ var startLoopImmediately = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : true;
5200
5210
  if (this.preloadPoolActive) {
5201
5211
  if (this.config.debugAdTiming) {
5202
5212
  console.log("[PRELOAD-POOL] Already active, skipping duplicate start");
@@ -5230,7 +5240,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5230
5240
  }
5231
5241
  }
5232
5242
  }
5233
- this.preloadPoolLoop(baseVastUrl);
5243
+ if (startLoopImmediately) {
5244
+ this.preloadPoolLoop(baseVastUrl);
5245
+ } else if (this.config.debugAdTiming) {
5246
+ console.log("[PRELOAD-POOL] Pre-break mode: preloaded once, waiting for SCTE start to continue fetching");
5247
+ }
5234
5248
  }
5235
5249
  },
5236
5250
  {
@@ -5266,7 +5280,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5266
5280
  epoch = this.adBreakEpoch;
5267
5281
  loadPromise = function() {
5268
5282
  return _async_to_generator(function() {
5269
- var _this, hasAdError, adErrorPayload, errorListenerCleanup, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, lateErrorListener, error;
5283
+ var _this, hasAdError, adErrorPayload, errorListenerCleanup, _preloadAd_getLoadedAdDuration, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, loadedDuration, lateErrorListener, _poolEntry_durationSeconds, error;
5270
5284
  return _ts_generator(this, function(_state) {
5271
5285
  switch(_state.label){
5272
5286
  case 0:
@@ -5362,6 +5376,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5362
5376
  isReady: true,
5363
5377
  loadPromise: Promise.resolve()
5364
5378
  };
5379
+ loadedDuration = (_preloadAd_getLoadedAdDuration = preloadAd.getLoadedAdDuration) === null || _preloadAd_getLoadedAdDuration === void 0 ? void 0 : _preloadAd_getLoadedAdDuration.call(preloadAd);
5380
+ if (loadedDuration != null && loadedDuration > 0) {
5381
+ poolEntry.durationSeconds = loadedDuration;
5382
+ }
5365
5383
  lateErrorListener = function lateErrorListener(payload) {
5366
5384
  var index = _this.preloadPool.findIndex(function(entry) {
5367
5385
  return entry.vastUrl === vastUrl;
@@ -5379,7 +5397,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5379
5397
  preloadAd.on("ad_error", lateErrorListener);
5380
5398
  this.preloadPool.push(poolEntry);
5381
5399
  if (this.config.debugAdTiming) {
5382
- console.log("[PRELOAD-POOL] ✅ Ad preloaded (no errors detected within timeout) (pool size: ".concat(this.preloadPool.length, "/").concat(this.maxPreloadPoolSize, ")"));
5400
+ ;
5401
+ 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)"));
5383
5402
  }
5384
5403
  if (errorListenerCleanup) {
5385
5404
  errorListenerCleanup();
@@ -5660,8 +5679,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5660
5679
  }
5661
5680
  this.inAdBreak = true;
5662
5681
  this.currentAdBreakStartWallClockMs = Date.now();
5682
+ this.adBreakPlayedDurationMs = 0;
5663
5683
  this.currentAdIndex = 0;
5664
- this.totalAdsInBreak = 1;
5684
+ this.totalAdsInBreak = 0;
5665
5685
  this.adPodQueue = [];
5666
5686
  this.showAds = true;
5667
5687
  this.showPlaceholderLayer();
@@ -5715,14 +5735,30 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5715
5735
  case 1:
5716
5736
  _state.trys.push([
5717
5737
  1,
5718
- 8,
5738
+ 12,
5719
5739
  ,
5720
- 14
5740
+ 20
5721
5741
  ]);
5722
5742
  if (!(usePreloadedAd && preloadedController)) return [
5743
+ 3,
5744
+ 5
5745
+ ];
5746
+ if (!!this.ensureLoadedAdFitsBudget(preloadedController)) return [
5723
5747
  3,
5724
5748
  3
5725
5749
  ];
5750
+ this.rejectLoadedAdForDuration(preloadedController);
5751
+ this.startContinuousFetching(baseVastUrl);
5752
+ return [
5753
+ 4,
5754
+ this.tryNextAvailableAdWithRateLimit()
5755
+ ];
5756
+ case 2:
5757
+ _state.sent();
5758
+ return [
5759
+ 2
5760
+ ];
5761
+ case 3:
5726
5762
  preservedMuted = this.adPlayer.getOriginalMutedState();
5727
5763
  preservedVolume = this.adPlayer.getOriginalVolume();
5728
5764
  this.adPlayer.destroy();
@@ -5735,18 +5771,20 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5735
5771
  }
5736
5772
  this.adPlayer.resume();
5737
5773
  this.consecutiveFailures = 0;
5738
- this.currentAdIndex++;
5739
5774
  this.startContinuousFetching(baseVastUrl);
5740
5775
  if (!this.preloadPoolActive) {
5741
5776
  this.preloadPoolActive = true;
5777
+ }
5778
+ if (!this.preloadPoolLoopRunning) {
5742
5779
  this.preloadPoolLoop(baseVastUrl);
5743
5780
  }
5744
5781
  return [
5745
5782
  4,
5746
5783
  this.adPlayer.play()
5747
5784
  ];
5748
- case 2:
5785
+ case 4:
5749
5786
  _state.sent();
5787
+ this.markAdStarted();
5750
5788
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5751
5789
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5752
5790
  }
@@ -5754,21 +5792,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5754
5792
  this.adPlayer.setAdVolume(adVolume);
5755
5793
  return [
5756
5794
  3,
5757
- 7
5795
+ 11
5758
5796
  ];
5759
- case 3:
5797
+ case 5:
5760
5798
  return [
5761
5799
  4,
5762
5800
  this.enforceGlobalRateLimit()
5763
5801
  ];
5764
- case 4:
5802
+ case 6:
5765
5803
  _state.sent();
5766
5804
  this.lastAdRequestTime = Date.now();
5767
5805
  return [
5768
5806
  4,
5769
5807
  this.adPlayer.requestAds(firstAdUrl)
5770
5808
  ];
5771
- case 5:
5809
+ case 7:
5772
5810
  _state.sent();
5773
5811
  sendAdLoadedTracking(this.config.licenseKey, {
5774
5812
  source: "hls",
@@ -5778,8 +5816,24 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5778
5816
  if (this.config.debugAdTiming) {
5779
5817
  console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
5780
5818
  }
5819
+ if (!!this.ensureLoadedAdFitsBudget()) return [
5820
+ 3,
5821
+ 9
5822
+ ];
5823
+ this.rejectLoadedAdForDuration();
5824
+ this.recreateAdController();
5825
+ this.startContinuousFetching(baseVastUrl);
5826
+ return [
5827
+ 4,
5828
+ this.tryNextAvailableAdWithRateLimit()
5829
+ ];
5830
+ case 8:
5831
+ _state.sent();
5832
+ return [
5833
+ 2
5834
+ ];
5835
+ case 9:
5781
5836
  this.consecutiveFailures = 0;
5782
- this.currentAdIndex++;
5783
5837
  this.startContinuousFetching(baseVastUrl);
5784
5838
  if (!this.preloadPoolActive) {
5785
5839
  this.startPreloadPool(baseVastUrl, []);
@@ -5788,44 +5842,61 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5788
5842
  4,
5789
5843
  this.adPlayer.play()
5790
5844
  ];
5791
- case 6:
5845
+ case 10:
5792
5846
  _state.sent();
5847
+ this.markAdStarted();
5793
5848
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5794
5849
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5795
5850
  }
5796
5851
  adVolume1 = currentMuted ? 0 : currentVolume;
5797
5852
  this.adPlayer.setAdVolume(adVolume1);
5798
- _state.label = 7;
5799
- case 7:
5853
+ _state.label = 11;
5854
+ case 11:
5800
5855
  return [
5801
5856
  3,
5802
- 14
5857
+ 20
5803
5858
  ];
5804
- case 8:
5859
+ case 12:
5805
5860
  error = _state.sent();
5806
5861
  if (this.config.debugAdTiming) {
5807
5862
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F First ad request failed:", error);
5808
5863
  }
5809
5864
  if (!!usePreloadedAd) return [
5810
5865
  3,
5811
- 12
5866
+ 18
5812
5867
  ];
5813
5868
  fallbackPreloaded = this.getPreloadedAd();
5814
5869
  if (!fallbackPreloaded) return [
5815
5870
  3,
5816
- 12
5871
+ 18
5817
5872
  ];
5818
5873
  if (this.config.debugAdTiming) {
5819
5874
  console.log("[CONTINUOUS-FETCH] \uD83D\uDD04 First ad failed, using preloaded fallback");
5820
5875
  }
5821
- _state.label = 9;
5822
- case 9:
5876
+ _state.label = 13;
5877
+ case 13:
5823
5878
  _state.trys.push([
5824
- 9,
5825
- 11,
5879
+ 13,
5880
+ 17,
5826
5881
  ,
5827
- 12
5882
+ 18
5828
5883
  ]);
5884
+ if (!!this.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
5885
+ 3,
5886
+ 15
5887
+ ];
5888
+ this.rejectLoadedAdForDuration(fallbackPreloaded.adController);
5889
+ this.startContinuousFetching(baseVastUrl);
5890
+ return [
5891
+ 4,
5892
+ this.tryNextAvailableAdWithRateLimit()
5893
+ ];
5894
+ case 14:
5895
+ _state.sent();
5896
+ return [
5897
+ 2
5898
+ ];
5899
+ case 15:
5829
5900
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
5830
5901
  preservedVolume1 = this.adPlayer.getOriginalVolume();
5831
5902
  this.adPlayer.destroy();
@@ -5835,7 +5906,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5835
5906
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
5836
5907
  this.adPlayer.resume();
5837
5908
  this.consecutiveFailures = 0;
5838
- this.currentAdIndex++;
5839
5909
  this.startContinuousFetching(baseVastUrl);
5840
5910
  if (!this.preloadPoolActive) {
5841
5911
  this.preloadPoolActive = true;
@@ -5845,8 +5915,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5845
5915
  4,
5846
5916
  this.adPlayer.play()
5847
5917
  ];
5848
- case 10:
5918
+ case 16:
5849
5919
  _state.sent();
5920
+ this.markAdStarted();
5850
5921
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5851
5922
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5852
5923
  }
@@ -5855,16 +5926,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5855
5926
  return [
5856
5927
  2
5857
5928
  ];
5858
- case 11:
5929
+ case 17:
5859
5930
  fallbackError = _state.sent();
5860
5931
  if (this.config.debugAdTiming) {
5861
5932
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded fallback also failed:", fallbackError);
5862
5933
  }
5863
5934
  return [
5864
5935
  3,
5865
- 12
5936
+ 18
5866
5937
  ];
5867
- case 12:
5938
+ case 18:
5868
5939
  if (this.isTemporaryAdError(error)) {
5869
5940
  this.temporaryFailureUrls.set(firstAdUrl, Date.now());
5870
5941
  if (this.config.debugAdTiming) {
@@ -5882,13 +5953,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5882
5953
  4,
5883
5954
  this.tryNextAvailableAdWithRateLimit()
5884
5955
  ];
5885
- case 13:
5956
+ case 19:
5886
5957
  _state.sent();
5887
5958
  return [
5888
5959
  3,
5889
- 14
5960
+ 20
5890
5961
  ];
5891
- case 14:
5962
+ case 20:
5892
5963
  return [
5893
5964
  2
5894
5965
  ];
@@ -5929,10 +6000,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5929
6000
  return _ts_generator(this, function(_state) {
5930
6001
  switch(_state.label){
5931
6002
  case 0:
5932
- remaining = _this.getRemainingAdMs();
6003
+ remaining = Math.min(_this.getWallClockRemainingAdMs(), _this.getDurationBudgetRemainingMs());
5933
6004
  if (remaining <= _this.getMinRemainingMsToStartAd()) {
5934
6005
  if (_this.config.debugAdTiming) {
5935
- console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (".concat(remaining, "ms), stopping URL generation"));
6006
+ console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (wall=".concat(_this.getWallClockRemainingAdMs(), "ms, budget=").concat(_this.getDurationBudgetRemainingMs(), "ms), stopping URL generation"));
5936
6007
  }
5937
6008
  return [
5938
6009
  2,
@@ -6014,7 +6085,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6014
6085
  });
6015
6086
  }
6016
6087
  _this.adRequestQueue.push(newAdUrl);
6017
- _this.totalAdsInBreak++;
6018
6088
  generationDelay = _this.consecutiveFailures > 0 ? Math.min(1e3 * Math.pow(2, _this.consecutiveFailures), 5e3) : 500;
6019
6089
  return [
6020
6090
  4,
@@ -6183,7 +6253,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6183
6253
  preloaded = this.getPreloadedAd();
6184
6254
  if (!preloaded) return [
6185
6255
  3,
6186
- 4
6256
+ 6
6187
6257
  ];
6188
6258
  if (this.config.debugAdTiming) {
6189
6259
  console.log("[CONTINUOUS-FETCH] \uD83C\uDFAF Using preloaded ad from pool (".concat(this.preloadPool.length, " remaining in pool)"));
@@ -6197,10 +6267,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6197
6267
  case 1:
6198
6268
  _state.trys.push([
6199
6269
  1,
6200
- 3,
6270
+ 5,
6201
6271
  ,
6202
- 4
6272
+ 6
6203
6273
  ]);
6274
+ if (!!this.ensureLoadedAdFitsBudget(preloaded.adController)) return [
6275
+ 3,
6276
+ 3
6277
+ ];
6278
+ this.rejectLoadedAdForDuration(preloaded.adController);
6279
+ return [
6280
+ 4,
6281
+ this.tryNextAvailableAdWithRateLimit()
6282
+ ];
6283
+ case 2:
6284
+ _state.sent();
6285
+ return [
6286
+ 2
6287
+ ];
6288
+ case 3:
6204
6289
  preservedMuted = this.adPlayer.getOriginalMutedState();
6205
6290
  preservedVolume = this.adPlayer.getOriginalVolume();
6206
6291
  this.adPlayer.destroy();
@@ -6209,15 +6294,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6209
6294
  this.attachImaEventListeners();
6210
6295
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
6211
6296
  this.adPlayer.resume();
6212
- this.currentAdIndex++;
6213
- this.totalAdRequestsInBreak++;
6214
- this.lastAdRequestTime = Date.now();
6215
6297
  return [
6216
6298
  4,
6217
6299
  this.adPlayer.play()
6218
6300
  ];
6219
- case 2:
6301
+ case 4:
6220
6302
  _state.sent();
6303
+ this.markAdStarted();
6221
6304
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6222
6305
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
6223
6306
  }
@@ -6226,25 +6309,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6226
6309
  return [
6227
6310
  2
6228
6311
  ];
6229
- case 3:
6312
+ case 5:
6230
6313
  error = _state.sent();
6231
6314
  if (this.config.debugAdTiming) {
6232
6315
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded ad failed to play:", error);
6233
6316
  }
6234
6317
  return [
6235
6318
  3,
6236
- 4
6319
+ 6
6237
6320
  ];
6238
- case 4:
6321
+ case 6:
6239
6322
  if (!(this.adRequestQueue.length > 0)) return [
6240
6323
  3,
6241
- 10
6324
+ 14
6242
6325
  ];
6243
6326
  nextAdUrl = this.adRequestQueue.shift();
6244
6327
  if (!nextAdUrl) {
6245
6328
  return [
6246
6329
  3,
6247
- 4
6330
+ 6
6248
6331
  ];
6249
6332
  }
6250
6333
  if (this.failedVastUrls.has(nextAdUrl)) {
@@ -6253,7 +6336,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6253
6336
  }
6254
6337
  return [
6255
6338
  3,
6256
- 4
6339
+ 6
6257
6340
  ];
6258
6341
  }
6259
6342
  if (this.isUrlInCooldown(nextAdUrl)) {
@@ -6262,40 +6345,51 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6262
6345
  }
6263
6346
  return [
6264
6347
  3,
6265
- 4
6348
+ 6
6266
6349
  ];
6267
6350
  }
6268
6351
  if (this.config.debugAdTiming) {
6269
- 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)"));
6352
+ console.log("[CONTINUOUS-FETCH] \uD83C\uDFAC Requesting next ad via HLS ad player (".concat(this.currentAdIndex + 1, ", ").concat(this.adRequestQueue.length, " remaining in queue)"));
6270
6353
  }
6271
- this.currentAdIndex++;
6272
- this.totalAdRequestsInBreak++;
6273
- this.lastAdRequestTime = Date.now();
6274
- _state.label = 5;
6275
- case 5:
6354
+ _state.label = 7;
6355
+ case 7:
6276
6356
  _state.trys.push([
6277
- 5,
6278
6357
  7,
6358
+ 9,
6279
6359
  ,
6280
- 9
6360
+ 13
6281
6361
  ]);
6282
6362
  return [
6283
6363
  4,
6284
6364
  this.playSingleAd(nextAdUrl)
6285
6365
  ];
6286
- case 6:
6366
+ case 8:
6287
6367
  _state.sent();
6288
6368
  this.consecutiveFailures = 0;
6289
6369
  return [
6290
6370
  3,
6291
- 9
6371
+ 13
6292
6372
  ];
6293
- case 7:
6373
+ case 9:
6294
6374
  error1 = _state.sent();
6295
6375
  errorMessage = error1.message;
6296
6376
  if (this.config.debugAdTiming) {
6297
6377
  console.log("[CONTINUOUS-FETCH] \u274C Ad request failed:", errorMessage);
6298
6378
  }
6379
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6380
+ 3,
6381
+ 11
6382
+ ];
6383
+ return [
6384
+ 4,
6385
+ this.tryNextAvailableAdWithRateLimit()
6386
+ ];
6387
+ case 10:
6388
+ _state.sent();
6389
+ return [
6390
+ 2
6391
+ ];
6392
+ case 11:
6299
6393
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6300
6394
  this.consecutiveFailures++;
6301
6395
  }
@@ -6303,21 +6397,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6303
6397
  4,
6304
6398
  this.tryNextAvailableAdWithRateLimit()
6305
6399
  ];
6306
- case 8:
6400
+ case 12:
6307
6401
  _state.sent();
6308
6402
  return [
6309
6403
  3,
6310
- 9
6404
+ 13
6311
6405
  ];
6312
- case 9:
6406
+ case 13:
6313
6407
  return [
6314
6408
  2
6315
6409
  ];
6316
- case 10:
6410
+ case 14:
6317
6411
  maxRetries = 3;
6318
6412
  if (!(this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3)) return [
6319
6413
  3,
6320
- 13
6414
+ 17
6321
6415
  ];
6322
6416
  if (this.config.debugAdTiming) {
6323
6417
  console.log("[CONTINUOUS-FETCH] ⏳ Queue empty, waiting for URLs... (".concat(retryCount + 1, "/").concat(maxRetries, ")"));
@@ -6328,18 +6422,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6328
6422
  return setTimeout(resolve, 500);
6329
6423
  })
6330
6424
  ];
6331
- case 11:
6425
+ case 15:
6332
6426
  _state.sent();
6333
6427
  return [
6334
6428
  4,
6335
6429
  this.tryNextAvailableAd(retryCount + 1)
6336
6430
  ];
6337
- case 12:
6431
+ case 16:
6338
6432
  _state.sent();
6339
6433
  return [
6340
6434
  2
6341
6435
  ];
6342
- case 13:
6436
+ case 17:
6343
6437
  if (!this.isShowingPlaceholder && remaining > 1e3) {
6344
6438
  this.showPlaceholderAndWaitForAds();
6345
6439
  } else {
@@ -6402,7 +6496,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6402
6496
  case 1:
6403
6497
  if (!(i < maxChecks)) return [
6404
6498
  3,
6405
- 10
6499
+ 12
6406
6500
  ];
6407
6501
  return [
6408
6502
  4,
@@ -6423,7 +6517,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6423
6517
  }
6424
6518
  return [
6425
6519
  3,
6426
- 10
6520
+ 12
6427
6521
  ];
6428
6522
  }
6429
6523
  if (!this.hasTimeToStartAnotherAd()) {
@@ -6432,19 +6526,19 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6432
6526
  }
6433
6527
  return [
6434
6528
  3,
6435
- 10
6529
+ 12
6436
6530
  ];
6437
6531
  }
6438
6532
  _state.label = 3;
6439
6533
  case 3:
6440
6534
  if (!(this.adRequestQueue.length > 0)) return [
6441
6535
  3,
6442
- 9
6536
+ 11
6443
6537
  ];
6444
6538
  if (!this.hasTimeToStartAnotherAd()) {
6445
6539
  return [
6446
6540
  3,
6447
- 9
6541
+ 11
6448
6542
  ];
6449
6543
  }
6450
6544
  nextAdUrl = this.adRequestQueue.shift();
@@ -6471,16 +6565,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6471
6565
  }
6472
6566
  this.isShowingPlaceholder = false;
6473
6567
  this.adPlayer.hidePlaceholder();
6474
- this.currentAdIndex++;
6475
- this.totalAdRequestsInBreak++;
6476
- this.lastAdRequestTime = Date.now();
6477
6568
  _state.label = 4;
6478
6569
  case 4:
6479
6570
  _state.trys.push([
6480
6571
  4,
6481
6572
  6,
6482
6573
  ,
6483
- 8
6574
+ 10
6484
6575
  ]);
6485
6576
  return [
6486
6577
  4,
@@ -6491,11 +6582,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6491
6582
  this.consecutiveFailures = 0;
6492
6583
  return [
6493
6584
  3,
6494
- 8
6585
+ 10
6495
6586
  ];
6496
6587
  case 6:
6497
6588
  error = _state.sent();
6498
6589
  errorMessage = error.message;
6590
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6591
+ 3,
6592
+ 8
6593
+ ];
6594
+ return [
6595
+ 4,
6596
+ this.tryNextAvailableAdWithRateLimit()
6597
+ ];
6598
+ case 7:
6599
+ _state.sent();
6600
+ return [
6601
+ 2
6602
+ ];
6603
+ case 8:
6499
6604
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6500
6605
  this.consecutiveFailures++;
6501
6606
  }
@@ -6503,23 +6608,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6503
6608
  4,
6504
6609
  this.tryNextAvailableAdWithRateLimit()
6505
6610
  ];
6506
- case 7:
6611
+ case 9:
6507
6612
  _state.sent();
6508
6613
  return [
6509
6614
  3,
6510
- 8
6615
+ 10
6511
6616
  ];
6512
- case 8:
6617
+ case 10:
6513
6618
  return [
6514
6619
  2
6515
6620
  ];
6516
- case 9:
6621
+ case 11:
6517
6622
  i++;
6518
6623
  return [
6519
6624
  3,
6520
6625
  1
6521
6626
  ];
6522
- case 10:
6627
+ case 12:
6523
6628
  if (this.config.debugAdTiming) {
6524
6629
  console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout, ending ad break");
6525
6630
  }
@@ -6991,6 +7096,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6991
7096
  2
6992
7097
  ];
6993
7098
  }
7099
+ if (!this.ensureLoadedAdFitsBudget()) {
7100
+ this.rejectLoadedAdForDuration();
7101
+ this.recreateAdController();
7102
+ throw new Error("ad_duration_exceeds_budget");
7103
+ }
6994
7104
  _state.label = 4;
6995
7105
  case 4:
6996
7106
  _state.trys.push([
@@ -7006,6 +7116,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7006
7116
  ];
7007
7117
  case 5:
7008
7118
  _state.sent();
7119
+ this.markAdStarted();
7009
7120
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7010
7121
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7011
7122
  }
@@ -7037,6 +7148,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7037
7148
  ,
7038
7149
  10
7039
7150
  ]);
7151
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback.adController)) {
7152
+ this.rejectLoadedAdForDuration(preloadedFallback.adController);
7153
+ throw playError;
7154
+ }
7040
7155
  this.clearAdFailsafeTimer();
7041
7156
  preservedMuted = this.adPlayer.getOriginalMutedState();
7042
7157
  preservedVolume = this.adPlayer.getOriginalVolume();
@@ -7047,14 +7162,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7047
7162
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
7048
7163
  this.adPlayer.resume();
7049
7164
  this.consecutiveFailures = 0;
7050
- this.currentAdIndex++;
7051
- this.totalAdRequestsInBreak++;
7052
7165
  return [
7053
7166
  4,
7054
7167
  this.adPlayer.play()
7055
7168
  ];
7056
7169
  case 8:
7057
7170
  _state.sent();
7171
+ this.markAdStarted();
7058
7172
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7059
7173
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7060
7174
  }
@@ -7115,6 +7229,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7115
7229
  ,
7116
7230
  16
7117
7231
  ]);
7232
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback1.adController)) {
7233
+ this.rejectLoadedAdForDuration(preloadedFallback1.adController);
7234
+ throw error;
7235
+ }
7118
7236
  this.clearAdRequestWatchdog();
7119
7237
  this.clearAdFailsafeTimer();
7120
7238
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
@@ -7126,14 +7244,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7126
7244
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
7127
7245
  this.adPlayer.resume();
7128
7246
  this.consecutiveFailures = 0;
7129
- this.currentAdIndex++;
7130
- this.totalAdRequestsInBreak++;
7131
7247
  return [
7132
7248
  4,
7133
7249
  this.adPlayer.play()
7134
7250
  ];
7135
7251
  case 14:
7136
7252
  _state.sent();
7253
+ this.markAdStarted();
7137
7254
  this.adPlayer.setAdVolume(this.getAdAudioVolume());
7138
7255
  return [
7139
7256
  2
@@ -7194,6 +7311,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7194
7311
  this.adRequestQueue = [];
7195
7312
  this.inAdBreak = false;
7196
7313
  this.adDetectSentForCurrentBreak = false;
7314
+ this.adBreakPlayedDurationMs = 0;
7197
7315
  this.activeScte35BreakKey = void 0;
7198
7316
  this.scheduledScte35BreakKey = void 0;
7199
7317
  this.expectedAdBreakDurationMs = void 0;
@@ -7383,13 +7501,117 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7383
7501
  }
7384
7502
  },
7385
7503
  {
7386
- key: "getRemainingAdMs",
7387
- value: function getRemainingAdMs() {
7388
- if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) return 0;
7504
+ key: "getWallClockRemainingAdMs",
7505
+ value: function getWallClockRemainingAdMs() {
7506
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
7507
+ return 0;
7508
+ }
7389
7509
  var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
7390
7510
  return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
7391
7511
  }
7392
7512
  },
7513
+ {
7514
+ key: "getDurationBudgetRemainingMs",
7515
+ value: function getDurationBudgetRemainingMs() {
7516
+ if (this.expectedAdBreakDurationMs == null) {
7517
+ return 0;
7518
+ }
7519
+ return Math.max(0, this.expectedAdBreakDurationMs - this.adBreakPlayedDurationMs);
7520
+ }
7521
+ },
7522
+ {
7523
+ key: "getLoadedAdDurationMs",
7524
+ value: function getLoadedAdDurationMs() {
7525
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7526
+ var _adController_getLoadedAdDuration;
7527
+ var durationSec = (_adController_getLoadedAdDuration = adController.getLoadedAdDuration) === null || _adController_getLoadedAdDuration === void 0 ? void 0 : _adController_getLoadedAdDuration.call(adController);
7528
+ if (durationSec == null || durationSec <= 0) {
7529
+ return 0;
7530
+ }
7531
+ return Math.round(durationSec * 1e3);
7532
+ }
7533
+ },
7534
+ {
7535
+ key: "adFitsRemainingBudget",
7536
+ value: function adFitsRemainingBudget(durationMs) {
7537
+ var adController = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : this.adPlayer;
7538
+ if (durationMs <= 0) {
7539
+ return true;
7540
+ }
7541
+ return durationMs <= this.getDurationBudgetRemainingMs();
7542
+ }
7543
+ },
7544
+ {
7545
+ key: "logAdDurationDecision",
7546
+ value: function logAdDurationDecision(accepted, durationMs) {
7547
+ var adController = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : this.adPlayer;
7548
+ var _ref;
7549
+ var _adController_getLoadedAdId;
7550
+ if (!this.config.debugAdTiming) {
7551
+ return;
7552
+ }
7553
+ var remainingBudget = this.getDurationBudgetRemainingMs();
7554
+ var adId = (_ref = (_adController_getLoadedAdId = adController.getLoadedAdId) === null || _adController_getLoadedAdId === void 0 ? void 0 : _adController_getLoadedAdId.call(adController)) !== null && _ref !== void 0 ? _ref : "unknown";
7555
+ if (accepted) {
7556
+ console.log("[AD-DURATION] Accepted ad id=".concat(adId, ", duration=").concat(durationMs, "ms, budget remaining after play=").concat(Math.max(0, remainingBudget - durationMs), "ms"));
7557
+ } else {
7558
+ console.log("[AD-DURATION] Rejected ad id=".concat(adId, ", duration=").concat(durationMs, "ms exceeds remaining budget=").concat(remainingBudget, "ms — requesting another ad"));
7559
+ }
7560
+ }
7561
+ },
7562
+ {
7563
+ key: "recordPlayedAdDuration",
7564
+ value: function recordPlayedAdDuration() {
7565
+ var durationMs = this.getLoadedAdDurationMs();
7566
+ if (durationMs <= 0) {
7567
+ return;
7568
+ }
7569
+ this.adBreakPlayedDurationMs += durationMs;
7570
+ if (this.config.debugAdTiming) {
7571
+ var _this_expectedAdBreakDurationMs;
7572
+ 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"));
7573
+ }
7574
+ }
7575
+ },
7576
+ {
7577
+ key: "ensureLoadedAdFitsBudget",
7578
+ value: function ensureLoadedAdFitsBudget() {
7579
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7580
+ var durationMs = this.getLoadedAdDurationMs(adController);
7581
+ var fits = this.adFitsRemainingBudget(durationMs, adController);
7582
+ this.logAdDurationDecision(fits, durationMs, adController);
7583
+ return fits;
7584
+ }
7585
+ },
7586
+ {
7587
+ key: "rejectLoadedAdForDuration",
7588
+ value: function rejectLoadedAdForDuration() {
7589
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7590
+ try {
7591
+ adController.destroy();
7592
+ } catch (unused) {}
7593
+ }
7594
+ },
7595
+ {
7596
+ key: "markAdStarted",
7597
+ value: function markAdStarted() {
7598
+ this.currentAdIndex++;
7599
+ this.totalAdsInBreak = this.currentAdIndex;
7600
+ this.totalAdRequestsInBreak++;
7601
+ this.lastAdRequestTime = Date.now();
7602
+ }
7603
+ },
7604
+ {
7605
+ key: "getRemainingAdMs",
7606
+ value: function getRemainingAdMs() {
7607
+ var wallClockRemaining = this.getWallClockRemainingAdMs();
7608
+ var budgetRemaining = this.getDurationBudgetRemainingMs();
7609
+ if (this.expectedAdBreakDurationMs == null) {
7610
+ return wallClockRemaining;
7611
+ }
7612
+ return Math.min(wallClockRemaining, budgetRemaining);
7613
+ }
7614
+ },
7393
7615
  {
7394
7616
  key: "getMinRemainingMsToStartAd",
7395
7617
  value: function getMinRemainingMsToStartAd() {
@@ -7403,7 +7625,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7403
7625
  {
7404
7626
  key: "hasTimeToStartAnotherAd",
7405
7627
  value: function hasTimeToStartAnotherAd() {
7406
- return this.getRemainingAdMs() > this.getMinRemainingMsToStartAd();
7628
+ var minRemaining = this.getMinRemainingMsToStartAd();
7629
+ return this.getWallClockRemainingAdMs() > minRemaining && this.getDurationBudgetRemainingMs() > minRemaining;
7407
7630
  }
7408
7631
  },
7409
7632
  {