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.
@@ -1292,6 +1292,12 @@ function createHlsAdPlayer(contentVideo, options) {
1292
1292
  contentVideo.style.visibility = "visible";
1293
1293
  contentVideo.style.opacity = "1";
1294
1294
  }
1295
+ },
1296
+ getLoadedAdDuration: function getLoadedAdDuration() {
1297
+ return currentAd === null || currentAd === void 0 ? void 0 : currentAd.duration;
1298
+ },
1299
+ getLoadedAdId: function getLoadedAdId() {
1300
+ return currentAd === null || currentAd === void 0 ? void 0 : currentAd.id;
1295
1301
  }
1296
1302
  };
1297
1303
  }
@@ -2704,6 +2710,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2704
2710
  this.preloadPoolLoopRunning = false;
2705
2711
  this.adDetectSentForCurrentBreak = false;
2706
2712
  this.adBreakEpoch = 0;
2713
+ this.adBreakPlayedDurationMs = 0;
2707
2714
  this.palPlaybackStarted = false;
2708
2715
  this.streamCorrelator = generateCorrelator();
2709
2716
  this.consentSignals = {};
@@ -3200,6 +3207,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3200
3207
  adIndex: _this.currentAdIndex,
3201
3208
  timestamp: /* @__PURE__ */ new Date().toISOString()
3202
3209
  }).catch(function() {});
3210
+ _this.recordPlayedAdDuration();
3203
3211
  var remaining = _this.getRemainingAdMs();
3204
3212
  _this.consecutiveFailures = 0;
3205
3213
  if (_this.config.debugAdTiming) {
@@ -3275,6 +3283,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3275
3283
  return;
3276
3284
  }
3277
3285
  _this.consecutiveFailures = 0;
3286
+ _this.recordPlayedAdDuration();
3278
3287
  var remaining = _this.getRemainingAdMs();
3279
3288
  if (_this.config.debugAdTiming) {
3280
3289
  console.log("[CONTINUOUS-FETCH] content_resume event: remaining=".concat(remaining, "ms, queued ads=").concat(_this.adRequestQueue.length), _this.adRequestQueue.length > 0 ? {
@@ -5168,7 +5177,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5168
5177
  }
5169
5178
  this.beginNewAdPod();
5170
5179
  this.podAssignedByPrefetch = true;
5171
- var urlsToPregenerate = 5;
5180
+ var urlsToPregenerate = 1;
5172
5181
  var generatedUrls = this.generateVastUrlsWithCorrelators(baseVastUrl, urlsToPregenerate);
5173
5182
  this.pendingAdBreak = _object_spread_props(_object_spread({
5174
5183
  marker: marker
@@ -5193,7 +5202,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5193
5202
  console.log("[PREFETCH] \uD83D\uDD04 Starting ad prefetch for upcoming ad break");
5194
5203
  console.log("[PREFETCH] \uD83D\uDCCB Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
5195
5204
  }
5196
- this.startPreloadPool(baseVastUrl, generatedUrls.slice(0, this.maxPreloadPoolSize));
5205
+ this.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
5197
5206
  }
5198
5207
  },
5199
5208
  {
@@ -5211,6 +5220,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5211
5220
  {
5212
5221
  key: "startPreloadPool",
5213
5222
  value: function startPreloadPool(baseVastUrl, initialUrls) {
5223
+ var startLoopImmediately = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : true;
5214
5224
  if (this.preloadPoolActive) {
5215
5225
  if (this.config.debugAdTiming) {
5216
5226
  console.log("[PRELOAD-POOL] Already active, skipping duplicate start");
@@ -5244,7 +5254,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5244
5254
  }
5245
5255
  }
5246
5256
  }
5247
- this.preloadPoolLoop(baseVastUrl);
5257
+ if (startLoopImmediately) {
5258
+ this.preloadPoolLoop(baseVastUrl);
5259
+ } else if (this.config.debugAdTiming) {
5260
+ console.log("[PRELOAD-POOL] Pre-break mode: preloaded once, waiting for SCTE start to continue fetching");
5261
+ }
5248
5262
  }
5249
5263
  },
5250
5264
  {
@@ -5280,7 +5294,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5280
5294
  epoch = this.adBreakEpoch;
5281
5295
  loadPromise = function() {
5282
5296
  return _async_to_generator(function() {
5283
- var _this, hasAdError, adErrorPayload, errorListenerCleanup, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, lateErrorListener, error;
5297
+ var _this, hasAdError, adErrorPayload, errorListenerCleanup, _preloadAd_getLoadedAdDuration, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, loadedDuration, lateErrorListener, _poolEntry_durationSeconds, error;
5284
5298
  return _ts_generator(this, function(_state) {
5285
5299
  switch(_state.label){
5286
5300
  case 0:
@@ -5376,6 +5390,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5376
5390
  isReady: true,
5377
5391
  loadPromise: Promise.resolve()
5378
5392
  };
5393
+ loadedDuration = (_preloadAd_getLoadedAdDuration = preloadAd.getLoadedAdDuration) === null || _preloadAd_getLoadedAdDuration === void 0 ? void 0 : _preloadAd_getLoadedAdDuration.call(preloadAd);
5394
+ if (loadedDuration != null && loadedDuration > 0) {
5395
+ poolEntry.durationSeconds = loadedDuration;
5396
+ }
5379
5397
  lateErrorListener = function lateErrorListener(payload) {
5380
5398
  var index = _this.preloadPool.findIndex(function(entry) {
5381
5399
  return entry.vastUrl === vastUrl;
@@ -5393,7 +5411,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5393
5411
  preloadAd.on("ad_error", lateErrorListener);
5394
5412
  this.preloadPool.push(poolEntry);
5395
5413
  if (this.config.debugAdTiming) {
5396
- console.log("[PRELOAD-POOL] ✅ Ad preloaded (no errors detected within timeout) (pool size: ".concat(this.preloadPool.length, "/").concat(this.maxPreloadPoolSize, ")"));
5414
+ ;
5415
+ 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)"));
5397
5416
  }
5398
5417
  if (errorListenerCleanup) {
5399
5418
  errorListenerCleanup();
@@ -5674,8 +5693,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5674
5693
  }
5675
5694
  this.inAdBreak = true;
5676
5695
  this.currentAdBreakStartWallClockMs = Date.now();
5696
+ this.adBreakPlayedDurationMs = 0;
5677
5697
  this.currentAdIndex = 0;
5678
- this.totalAdsInBreak = 1;
5698
+ this.totalAdsInBreak = 0;
5679
5699
  this.adPodQueue = [];
5680
5700
  this.showAds = true;
5681
5701
  this.showPlaceholderLayer();
@@ -5729,14 +5749,30 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5729
5749
  case 1:
5730
5750
  _state.trys.push([
5731
5751
  1,
5732
- 8,
5752
+ 12,
5733
5753
  ,
5734
- 14
5754
+ 20
5735
5755
  ]);
5736
5756
  if (!(usePreloadedAd && preloadedController)) return [
5757
+ 3,
5758
+ 5
5759
+ ];
5760
+ if (!!this.ensureLoadedAdFitsBudget(preloadedController)) return [
5737
5761
  3,
5738
5762
  3
5739
5763
  ];
5764
+ this.rejectLoadedAdForDuration(preloadedController);
5765
+ this.startContinuousFetching(baseVastUrl);
5766
+ return [
5767
+ 4,
5768
+ this.tryNextAvailableAdWithRateLimit()
5769
+ ];
5770
+ case 2:
5771
+ _state.sent();
5772
+ return [
5773
+ 2
5774
+ ];
5775
+ case 3:
5740
5776
  preservedMuted = this.adPlayer.getOriginalMutedState();
5741
5777
  preservedVolume = this.adPlayer.getOriginalVolume();
5742
5778
  this.adPlayer.destroy();
@@ -5749,18 +5785,20 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5749
5785
  }
5750
5786
  this.adPlayer.resume();
5751
5787
  this.consecutiveFailures = 0;
5752
- this.currentAdIndex++;
5753
5788
  this.startContinuousFetching(baseVastUrl);
5754
5789
  if (!this.preloadPoolActive) {
5755
5790
  this.preloadPoolActive = true;
5791
+ }
5792
+ if (!this.preloadPoolLoopRunning) {
5756
5793
  this.preloadPoolLoop(baseVastUrl);
5757
5794
  }
5758
5795
  return [
5759
5796
  4,
5760
5797
  this.adPlayer.play()
5761
5798
  ];
5762
- case 2:
5799
+ case 4:
5763
5800
  _state.sent();
5801
+ this.markAdStarted();
5764
5802
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5765
5803
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5766
5804
  }
@@ -5768,21 +5806,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5768
5806
  this.adPlayer.setAdVolume(adVolume);
5769
5807
  return [
5770
5808
  3,
5771
- 7
5809
+ 11
5772
5810
  ];
5773
- case 3:
5811
+ case 5:
5774
5812
  return [
5775
5813
  4,
5776
5814
  this.enforceGlobalRateLimit()
5777
5815
  ];
5778
- case 4:
5816
+ case 6:
5779
5817
  _state.sent();
5780
5818
  this.lastAdRequestTime = Date.now();
5781
5819
  return [
5782
5820
  4,
5783
5821
  this.adPlayer.requestAds(firstAdUrl)
5784
5822
  ];
5785
- case 5:
5823
+ case 7:
5786
5824
  _state.sent();
5787
5825
  sendAdLoadedTracking(this.config.licenseKey, {
5788
5826
  source: "hls",
@@ -5792,8 +5830,24 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5792
5830
  if (this.config.debugAdTiming) {
5793
5831
  console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
5794
5832
  }
5833
+ if (!!this.ensureLoadedAdFitsBudget()) return [
5834
+ 3,
5835
+ 9
5836
+ ];
5837
+ this.rejectLoadedAdForDuration();
5838
+ this.recreateAdController();
5839
+ this.startContinuousFetching(baseVastUrl);
5840
+ return [
5841
+ 4,
5842
+ this.tryNextAvailableAdWithRateLimit()
5843
+ ];
5844
+ case 8:
5845
+ _state.sent();
5846
+ return [
5847
+ 2
5848
+ ];
5849
+ case 9:
5795
5850
  this.consecutiveFailures = 0;
5796
- this.currentAdIndex++;
5797
5851
  this.startContinuousFetching(baseVastUrl);
5798
5852
  if (!this.preloadPoolActive) {
5799
5853
  this.startPreloadPool(baseVastUrl, []);
@@ -5802,44 +5856,61 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5802
5856
  4,
5803
5857
  this.adPlayer.play()
5804
5858
  ];
5805
- case 6:
5859
+ case 10:
5806
5860
  _state.sent();
5861
+ this.markAdStarted();
5807
5862
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5808
5863
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5809
5864
  }
5810
5865
  adVolume1 = currentMuted ? 0 : currentVolume;
5811
5866
  this.adPlayer.setAdVolume(adVolume1);
5812
- _state.label = 7;
5813
- case 7:
5867
+ _state.label = 11;
5868
+ case 11:
5814
5869
  return [
5815
5870
  3,
5816
- 14
5871
+ 20
5817
5872
  ];
5818
- case 8:
5873
+ case 12:
5819
5874
  error = _state.sent();
5820
5875
  if (this.config.debugAdTiming) {
5821
5876
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F First ad request failed:", error);
5822
5877
  }
5823
5878
  if (!!usePreloadedAd) return [
5824
5879
  3,
5825
- 12
5880
+ 18
5826
5881
  ];
5827
5882
  fallbackPreloaded = this.getPreloadedAd();
5828
5883
  if (!fallbackPreloaded) return [
5829
5884
  3,
5830
- 12
5885
+ 18
5831
5886
  ];
5832
5887
  if (this.config.debugAdTiming) {
5833
5888
  console.log("[CONTINUOUS-FETCH] \uD83D\uDD04 First ad failed, using preloaded fallback");
5834
5889
  }
5835
- _state.label = 9;
5836
- case 9:
5890
+ _state.label = 13;
5891
+ case 13:
5837
5892
  _state.trys.push([
5838
- 9,
5839
- 11,
5893
+ 13,
5894
+ 17,
5840
5895
  ,
5841
- 12
5896
+ 18
5842
5897
  ]);
5898
+ if (!!this.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
5899
+ 3,
5900
+ 15
5901
+ ];
5902
+ this.rejectLoadedAdForDuration(fallbackPreloaded.adController);
5903
+ this.startContinuousFetching(baseVastUrl);
5904
+ return [
5905
+ 4,
5906
+ this.tryNextAvailableAdWithRateLimit()
5907
+ ];
5908
+ case 14:
5909
+ _state.sent();
5910
+ return [
5911
+ 2
5912
+ ];
5913
+ case 15:
5843
5914
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
5844
5915
  preservedVolume1 = this.adPlayer.getOriginalVolume();
5845
5916
  this.adPlayer.destroy();
@@ -5849,7 +5920,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5849
5920
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
5850
5921
  this.adPlayer.resume();
5851
5922
  this.consecutiveFailures = 0;
5852
- this.currentAdIndex++;
5853
5923
  this.startContinuousFetching(baseVastUrl);
5854
5924
  if (!this.preloadPoolActive) {
5855
5925
  this.preloadPoolActive = true;
@@ -5859,8 +5929,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5859
5929
  4,
5860
5930
  this.adPlayer.play()
5861
5931
  ];
5862
- case 10:
5932
+ case 16:
5863
5933
  _state.sent();
5934
+ this.markAdStarted();
5864
5935
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5865
5936
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5866
5937
  }
@@ -5869,16 +5940,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5869
5940
  return [
5870
5941
  2
5871
5942
  ];
5872
- case 11:
5943
+ case 17:
5873
5944
  fallbackError = _state.sent();
5874
5945
  if (this.config.debugAdTiming) {
5875
5946
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded fallback also failed:", fallbackError);
5876
5947
  }
5877
5948
  return [
5878
5949
  3,
5879
- 12
5950
+ 18
5880
5951
  ];
5881
- case 12:
5952
+ case 18:
5882
5953
  if (this.isTemporaryAdError(error)) {
5883
5954
  this.temporaryFailureUrls.set(firstAdUrl, Date.now());
5884
5955
  if (this.config.debugAdTiming) {
@@ -5896,13 +5967,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5896
5967
  4,
5897
5968
  this.tryNextAvailableAdWithRateLimit()
5898
5969
  ];
5899
- case 13:
5970
+ case 19:
5900
5971
  _state.sent();
5901
5972
  return [
5902
5973
  3,
5903
- 14
5974
+ 20
5904
5975
  ];
5905
- case 14:
5976
+ case 20:
5906
5977
  return [
5907
5978
  2
5908
5979
  ];
@@ -5943,10 +6014,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5943
6014
  return _ts_generator(this, function(_state) {
5944
6015
  switch(_state.label){
5945
6016
  case 0:
5946
- remaining = _this.getRemainingAdMs();
6017
+ remaining = Math.min(_this.getWallClockRemainingAdMs(), _this.getDurationBudgetRemainingMs());
5947
6018
  if (remaining <= _this.getMinRemainingMsToStartAd()) {
5948
6019
  if (_this.config.debugAdTiming) {
5949
- console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (".concat(remaining, "ms), stopping URL generation"));
6020
+ console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (wall=".concat(_this.getWallClockRemainingAdMs(), "ms, budget=").concat(_this.getDurationBudgetRemainingMs(), "ms), stopping URL generation"));
5950
6021
  }
5951
6022
  return [
5952
6023
  2,
@@ -6028,7 +6099,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6028
6099
  });
6029
6100
  }
6030
6101
  _this.adRequestQueue.push(newAdUrl);
6031
- _this.totalAdsInBreak++;
6032
6102
  generationDelay = _this.consecutiveFailures > 0 ? Math.min(1e3 * Math.pow(2, _this.consecutiveFailures), 5e3) : 500;
6033
6103
  return [
6034
6104
  4,
@@ -6197,7 +6267,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6197
6267
  preloaded = this.getPreloadedAd();
6198
6268
  if (!preloaded) return [
6199
6269
  3,
6200
- 4
6270
+ 6
6201
6271
  ];
6202
6272
  if (this.config.debugAdTiming) {
6203
6273
  console.log("[CONTINUOUS-FETCH] \uD83C\uDFAF Using preloaded ad from pool (".concat(this.preloadPool.length, " remaining in pool)"));
@@ -6211,10 +6281,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6211
6281
  case 1:
6212
6282
  _state.trys.push([
6213
6283
  1,
6214
- 3,
6284
+ 5,
6215
6285
  ,
6216
- 4
6286
+ 6
6217
6287
  ]);
6288
+ if (!!this.ensureLoadedAdFitsBudget(preloaded.adController)) return [
6289
+ 3,
6290
+ 3
6291
+ ];
6292
+ this.rejectLoadedAdForDuration(preloaded.adController);
6293
+ return [
6294
+ 4,
6295
+ this.tryNextAvailableAdWithRateLimit()
6296
+ ];
6297
+ case 2:
6298
+ _state.sent();
6299
+ return [
6300
+ 2
6301
+ ];
6302
+ case 3:
6218
6303
  preservedMuted = this.adPlayer.getOriginalMutedState();
6219
6304
  preservedVolume = this.adPlayer.getOriginalVolume();
6220
6305
  this.adPlayer.destroy();
@@ -6223,15 +6308,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6223
6308
  this.attachImaEventListeners();
6224
6309
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
6225
6310
  this.adPlayer.resume();
6226
- this.currentAdIndex++;
6227
- this.totalAdRequestsInBreak++;
6228
- this.lastAdRequestTime = Date.now();
6229
6311
  return [
6230
6312
  4,
6231
6313
  this.adPlayer.play()
6232
6314
  ];
6233
- case 2:
6315
+ case 4:
6234
6316
  _state.sent();
6317
+ this.markAdStarted();
6235
6318
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6236
6319
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
6237
6320
  }
@@ -6240,25 +6323,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6240
6323
  return [
6241
6324
  2
6242
6325
  ];
6243
- case 3:
6326
+ case 5:
6244
6327
  error = _state.sent();
6245
6328
  if (this.config.debugAdTiming) {
6246
6329
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded ad failed to play:", error);
6247
6330
  }
6248
6331
  return [
6249
6332
  3,
6250
- 4
6333
+ 6
6251
6334
  ];
6252
- case 4:
6335
+ case 6:
6253
6336
  if (!(this.adRequestQueue.length > 0)) return [
6254
6337
  3,
6255
- 10
6338
+ 14
6256
6339
  ];
6257
6340
  nextAdUrl = this.adRequestQueue.shift();
6258
6341
  if (!nextAdUrl) {
6259
6342
  return [
6260
6343
  3,
6261
- 4
6344
+ 6
6262
6345
  ];
6263
6346
  }
6264
6347
  if (this.failedVastUrls.has(nextAdUrl)) {
@@ -6267,7 +6350,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6267
6350
  }
6268
6351
  return [
6269
6352
  3,
6270
- 4
6353
+ 6
6271
6354
  ];
6272
6355
  }
6273
6356
  if (this.isUrlInCooldown(nextAdUrl)) {
@@ -6276,40 +6359,51 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6276
6359
  }
6277
6360
  return [
6278
6361
  3,
6279
- 4
6362
+ 6
6280
6363
  ];
6281
6364
  }
6282
6365
  if (this.config.debugAdTiming) {
6283
- 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)"));
6366
+ console.log("[CONTINUOUS-FETCH] \uD83C\uDFAC Requesting next ad via HLS ad player (".concat(this.currentAdIndex + 1, ", ").concat(this.adRequestQueue.length, " remaining in queue)"));
6284
6367
  }
6285
- this.currentAdIndex++;
6286
- this.totalAdRequestsInBreak++;
6287
- this.lastAdRequestTime = Date.now();
6288
- _state.label = 5;
6289
- case 5:
6368
+ _state.label = 7;
6369
+ case 7:
6290
6370
  _state.trys.push([
6291
- 5,
6292
6371
  7,
6372
+ 9,
6293
6373
  ,
6294
- 9
6374
+ 13
6295
6375
  ]);
6296
6376
  return [
6297
6377
  4,
6298
6378
  this.playSingleAd(nextAdUrl)
6299
6379
  ];
6300
- case 6:
6380
+ case 8:
6301
6381
  _state.sent();
6302
6382
  this.consecutiveFailures = 0;
6303
6383
  return [
6304
6384
  3,
6305
- 9
6385
+ 13
6306
6386
  ];
6307
- case 7:
6387
+ case 9:
6308
6388
  error1 = _state.sent();
6309
6389
  errorMessage = error1.message;
6310
6390
  if (this.config.debugAdTiming) {
6311
6391
  console.log("[CONTINUOUS-FETCH] \u274C Ad request failed:", errorMessage);
6312
6392
  }
6393
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6394
+ 3,
6395
+ 11
6396
+ ];
6397
+ return [
6398
+ 4,
6399
+ this.tryNextAvailableAdWithRateLimit()
6400
+ ];
6401
+ case 10:
6402
+ _state.sent();
6403
+ return [
6404
+ 2
6405
+ ];
6406
+ case 11:
6313
6407
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6314
6408
  this.consecutiveFailures++;
6315
6409
  }
@@ -6317,21 +6411,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6317
6411
  4,
6318
6412
  this.tryNextAvailableAdWithRateLimit()
6319
6413
  ];
6320
- case 8:
6414
+ case 12:
6321
6415
  _state.sent();
6322
6416
  return [
6323
6417
  3,
6324
- 9
6418
+ 13
6325
6419
  ];
6326
- case 9:
6420
+ case 13:
6327
6421
  return [
6328
6422
  2
6329
6423
  ];
6330
- case 10:
6424
+ case 14:
6331
6425
  maxRetries = 3;
6332
6426
  if (!(this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3)) return [
6333
6427
  3,
6334
- 13
6428
+ 17
6335
6429
  ];
6336
6430
  if (this.config.debugAdTiming) {
6337
6431
  console.log("[CONTINUOUS-FETCH] ⏳ Queue empty, waiting for URLs... (".concat(retryCount + 1, "/").concat(maxRetries, ")"));
@@ -6342,18 +6436,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6342
6436
  return setTimeout(resolve, 500);
6343
6437
  })
6344
6438
  ];
6345
- case 11:
6439
+ case 15:
6346
6440
  _state.sent();
6347
6441
  return [
6348
6442
  4,
6349
6443
  this.tryNextAvailableAd(retryCount + 1)
6350
6444
  ];
6351
- case 12:
6445
+ case 16:
6352
6446
  _state.sent();
6353
6447
  return [
6354
6448
  2
6355
6449
  ];
6356
- case 13:
6450
+ case 17:
6357
6451
  if (!this.isShowingPlaceholder && remaining > 1e3) {
6358
6452
  this.showPlaceholderAndWaitForAds();
6359
6453
  } else {
@@ -6416,7 +6510,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6416
6510
  case 1:
6417
6511
  if (!(i < maxChecks)) return [
6418
6512
  3,
6419
- 10
6513
+ 12
6420
6514
  ];
6421
6515
  return [
6422
6516
  4,
@@ -6437,7 +6531,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6437
6531
  }
6438
6532
  return [
6439
6533
  3,
6440
- 10
6534
+ 12
6441
6535
  ];
6442
6536
  }
6443
6537
  if (!this.hasTimeToStartAnotherAd()) {
@@ -6446,19 +6540,19 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6446
6540
  }
6447
6541
  return [
6448
6542
  3,
6449
- 10
6543
+ 12
6450
6544
  ];
6451
6545
  }
6452
6546
  _state.label = 3;
6453
6547
  case 3:
6454
6548
  if (!(this.adRequestQueue.length > 0)) return [
6455
6549
  3,
6456
- 9
6550
+ 11
6457
6551
  ];
6458
6552
  if (!this.hasTimeToStartAnotherAd()) {
6459
6553
  return [
6460
6554
  3,
6461
- 9
6555
+ 11
6462
6556
  ];
6463
6557
  }
6464
6558
  nextAdUrl = this.adRequestQueue.shift();
@@ -6485,16 +6579,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6485
6579
  }
6486
6580
  this.isShowingPlaceholder = false;
6487
6581
  this.adPlayer.hidePlaceholder();
6488
- this.currentAdIndex++;
6489
- this.totalAdRequestsInBreak++;
6490
- this.lastAdRequestTime = Date.now();
6491
6582
  _state.label = 4;
6492
6583
  case 4:
6493
6584
  _state.trys.push([
6494
6585
  4,
6495
6586
  6,
6496
6587
  ,
6497
- 8
6588
+ 10
6498
6589
  ]);
6499
6590
  return [
6500
6591
  4,
@@ -6505,11 +6596,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6505
6596
  this.consecutiveFailures = 0;
6506
6597
  return [
6507
6598
  3,
6508
- 8
6599
+ 10
6509
6600
  ];
6510
6601
  case 6:
6511
6602
  error = _state.sent();
6512
6603
  errorMessage = error.message;
6604
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6605
+ 3,
6606
+ 8
6607
+ ];
6608
+ return [
6609
+ 4,
6610
+ this.tryNextAvailableAdWithRateLimit()
6611
+ ];
6612
+ case 7:
6613
+ _state.sent();
6614
+ return [
6615
+ 2
6616
+ ];
6617
+ case 8:
6513
6618
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6514
6619
  this.consecutiveFailures++;
6515
6620
  }
@@ -6517,23 +6622,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6517
6622
  4,
6518
6623
  this.tryNextAvailableAdWithRateLimit()
6519
6624
  ];
6520
- case 7:
6625
+ case 9:
6521
6626
  _state.sent();
6522
6627
  return [
6523
6628
  3,
6524
- 8
6629
+ 10
6525
6630
  ];
6526
- case 8:
6631
+ case 10:
6527
6632
  return [
6528
6633
  2
6529
6634
  ];
6530
- case 9:
6635
+ case 11:
6531
6636
  i++;
6532
6637
  return [
6533
6638
  3,
6534
6639
  1
6535
6640
  ];
6536
- case 10:
6641
+ case 12:
6537
6642
  if (this.config.debugAdTiming) {
6538
6643
  console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout, ending ad break");
6539
6644
  }
@@ -7005,6 +7110,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7005
7110
  2
7006
7111
  ];
7007
7112
  }
7113
+ if (!this.ensureLoadedAdFitsBudget()) {
7114
+ this.rejectLoadedAdForDuration();
7115
+ this.recreateAdController();
7116
+ throw new Error("ad_duration_exceeds_budget");
7117
+ }
7008
7118
  _state.label = 4;
7009
7119
  case 4:
7010
7120
  _state.trys.push([
@@ -7020,6 +7130,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7020
7130
  ];
7021
7131
  case 5:
7022
7132
  _state.sent();
7133
+ this.markAdStarted();
7023
7134
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7024
7135
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7025
7136
  }
@@ -7051,6 +7162,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7051
7162
  ,
7052
7163
  10
7053
7164
  ]);
7165
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback.adController)) {
7166
+ this.rejectLoadedAdForDuration(preloadedFallback.adController);
7167
+ throw playError;
7168
+ }
7054
7169
  this.clearAdFailsafeTimer();
7055
7170
  preservedMuted = this.adPlayer.getOriginalMutedState();
7056
7171
  preservedVolume = this.adPlayer.getOriginalVolume();
@@ -7061,14 +7176,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7061
7176
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
7062
7177
  this.adPlayer.resume();
7063
7178
  this.consecutiveFailures = 0;
7064
- this.currentAdIndex++;
7065
- this.totalAdRequestsInBreak++;
7066
7179
  return [
7067
7180
  4,
7068
7181
  this.adPlayer.play()
7069
7182
  ];
7070
7183
  case 8:
7071
7184
  _state.sent();
7185
+ this.markAdStarted();
7072
7186
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7073
7187
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7074
7188
  }
@@ -7129,6 +7243,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7129
7243
  ,
7130
7244
  16
7131
7245
  ]);
7246
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback1.adController)) {
7247
+ this.rejectLoadedAdForDuration(preloadedFallback1.adController);
7248
+ throw error;
7249
+ }
7132
7250
  this.clearAdRequestWatchdog();
7133
7251
  this.clearAdFailsafeTimer();
7134
7252
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
@@ -7140,14 +7258,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7140
7258
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
7141
7259
  this.adPlayer.resume();
7142
7260
  this.consecutiveFailures = 0;
7143
- this.currentAdIndex++;
7144
- this.totalAdRequestsInBreak++;
7145
7261
  return [
7146
7262
  4,
7147
7263
  this.adPlayer.play()
7148
7264
  ];
7149
7265
  case 14:
7150
7266
  _state.sent();
7267
+ this.markAdStarted();
7151
7268
  this.adPlayer.setAdVolume(this.getAdAudioVolume());
7152
7269
  return [
7153
7270
  2
@@ -7208,6 +7325,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7208
7325
  this.adRequestQueue = [];
7209
7326
  this.inAdBreak = false;
7210
7327
  this.adDetectSentForCurrentBreak = false;
7328
+ this.adBreakPlayedDurationMs = 0;
7211
7329
  this.activeScte35BreakKey = void 0;
7212
7330
  this.scheduledScte35BreakKey = void 0;
7213
7331
  this.expectedAdBreakDurationMs = void 0;
@@ -7397,13 +7515,117 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7397
7515
  }
7398
7516
  },
7399
7517
  {
7400
- key: "getRemainingAdMs",
7401
- value: function getRemainingAdMs() {
7402
- if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) return 0;
7518
+ key: "getWallClockRemainingAdMs",
7519
+ value: function getWallClockRemainingAdMs() {
7520
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
7521
+ return 0;
7522
+ }
7403
7523
  var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
7404
7524
  return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
7405
7525
  }
7406
7526
  },
7527
+ {
7528
+ key: "getDurationBudgetRemainingMs",
7529
+ value: function getDurationBudgetRemainingMs() {
7530
+ if (this.expectedAdBreakDurationMs == null) {
7531
+ return 0;
7532
+ }
7533
+ return Math.max(0, this.expectedAdBreakDurationMs - this.adBreakPlayedDurationMs);
7534
+ }
7535
+ },
7536
+ {
7537
+ key: "getLoadedAdDurationMs",
7538
+ value: function getLoadedAdDurationMs() {
7539
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7540
+ var _adController_getLoadedAdDuration;
7541
+ var durationSec = (_adController_getLoadedAdDuration = adController.getLoadedAdDuration) === null || _adController_getLoadedAdDuration === void 0 ? void 0 : _adController_getLoadedAdDuration.call(adController);
7542
+ if (durationSec == null || durationSec <= 0) {
7543
+ return 0;
7544
+ }
7545
+ return Math.round(durationSec * 1e3);
7546
+ }
7547
+ },
7548
+ {
7549
+ key: "adFitsRemainingBudget",
7550
+ value: function adFitsRemainingBudget(durationMs) {
7551
+ var adController = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : this.adPlayer;
7552
+ if (durationMs <= 0) {
7553
+ return true;
7554
+ }
7555
+ return durationMs <= this.getDurationBudgetRemainingMs();
7556
+ }
7557
+ },
7558
+ {
7559
+ key: "logAdDurationDecision",
7560
+ value: function logAdDurationDecision(accepted, durationMs) {
7561
+ var adController = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : this.adPlayer;
7562
+ var _ref;
7563
+ var _adController_getLoadedAdId;
7564
+ if (!this.config.debugAdTiming) {
7565
+ return;
7566
+ }
7567
+ var remainingBudget = this.getDurationBudgetRemainingMs();
7568
+ var adId = (_ref = (_adController_getLoadedAdId = adController.getLoadedAdId) === null || _adController_getLoadedAdId === void 0 ? void 0 : _adController_getLoadedAdId.call(adController)) !== null && _ref !== void 0 ? _ref : "unknown";
7569
+ if (accepted) {
7570
+ console.log("[AD-DURATION] Accepted ad id=".concat(adId, ", duration=").concat(durationMs, "ms, budget remaining after play=").concat(Math.max(0, remainingBudget - durationMs), "ms"));
7571
+ } else {
7572
+ console.log("[AD-DURATION] Rejected ad id=".concat(adId, ", duration=").concat(durationMs, "ms exceeds remaining budget=").concat(remainingBudget, "ms — requesting another ad"));
7573
+ }
7574
+ }
7575
+ },
7576
+ {
7577
+ key: "recordPlayedAdDuration",
7578
+ value: function recordPlayedAdDuration() {
7579
+ var durationMs = this.getLoadedAdDurationMs();
7580
+ if (durationMs <= 0) {
7581
+ return;
7582
+ }
7583
+ this.adBreakPlayedDurationMs += durationMs;
7584
+ if (this.config.debugAdTiming) {
7585
+ var _this_expectedAdBreakDurationMs;
7586
+ 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"));
7587
+ }
7588
+ }
7589
+ },
7590
+ {
7591
+ key: "ensureLoadedAdFitsBudget",
7592
+ value: function ensureLoadedAdFitsBudget() {
7593
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7594
+ var durationMs = this.getLoadedAdDurationMs(adController);
7595
+ var fits = this.adFitsRemainingBudget(durationMs, adController);
7596
+ this.logAdDurationDecision(fits, durationMs, adController);
7597
+ return fits;
7598
+ }
7599
+ },
7600
+ {
7601
+ key: "rejectLoadedAdForDuration",
7602
+ value: function rejectLoadedAdForDuration() {
7603
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7604
+ try {
7605
+ adController.destroy();
7606
+ } catch (unused) {}
7607
+ }
7608
+ },
7609
+ {
7610
+ key: "markAdStarted",
7611
+ value: function markAdStarted() {
7612
+ this.currentAdIndex++;
7613
+ this.totalAdsInBreak = this.currentAdIndex;
7614
+ this.totalAdRequestsInBreak++;
7615
+ this.lastAdRequestTime = Date.now();
7616
+ }
7617
+ },
7618
+ {
7619
+ key: "getRemainingAdMs",
7620
+ value: function getRemainingAdMs() {
7621
+ var wallClockRemaining = this.getWallClockRemainingAdMs();
7622
+ var budgetRemaining = this.getDurationBudgetRemainingMs();
7623
+ if (this.expectedAdBreakDurationMs == null) {
7624
+ return wallClockRemaining;
7625
+ }
7626
+ return Math.min(wallClockRemaining, budgetRemaining);
7627
+ }
7628
+ },
7407
7629
  {
7408
7630
  key: "getMinRemainingMsToStartAd",
7409
7631
  value: function getMinRemainingMsToStartAd() {
@@ -7417,7 +7639,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7417
7639
  {
7418
7640
  key: "hasTimeToStartAnotherAd",
7419
7641
  value: function hasTimeToStartAnotherAd() {
7420
- return this.getRemainingAdMs() > this.getMinRemainingMsToStartAd();
7642
+ var minRemaining = this.getMinRemainingMsToStartAd();
7643
+ return this.getWallClockRemainingAdMs() > minRemaining && this.getDurationBudgetRemainingMs() > minRemaining;
7421
7644
  }
7422
7645
  },
7423
7646
  {