stormcloud-video-player 0.8.10 → 0.8.11

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 ? {
@@ -5280,7 +5289,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5280
5289
  epoch = this.adBreakEpoch;
5281
5290
  loadPromise = function() {
5282
5291
  return _async_to_generator(function() {
5283
- var _this, hasAdError, adErrorPayload, errorListenerCleanup, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, lateErrorListener, error;
5292
+ var _this, hasAdError, adErrorPayload, errorListenerCleanup, _preloadAd_getLoadedAdDuration, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, loadedDuration, lateErrorListener, _poolEntry_durationSeconds, error;
5284
5293
  return _ts_generator(this, function(_state) {
5285
5294
  switch(_state.label){
5286
5295
  case 0:
@@ -5376,6 +5385,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5376
5385
  isReady: true,
5377
5386
  loadPromise: Promise.resolve()
5378
5387
  };
5388
+ loadedDuration = (_preloadAd_getLoadedAdDuration = preloadAd.getLoadedAdDuration) === null || _preloadAd_getLoadedAdDuration === void 0 ? void 0 : _preloadAd_getLoadedAdDuration.call(preloadAd);
5389
+ if (loadedDuration != null && loadedDuration > 0) {
5390
+ poolEntry.durationSeconds = loadedDuration;
5391
+ }
5379
5392
  lateErrorListener = function lateErrorListener(payload) {
5380
5393
  var index = _this.preloadPool.findIndex(function(entry) {
5381
5394
  return entry.vastUrl === vastUrl;
@@ -5393,7 +5406,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5393
5406
  preloadAd.on("ad_error", lateErrorListener);
5394
5407
  this.preloadPool.push(poolEntry);
5395
5408
  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, ")"));
5409
+ ;
5410
+ 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
5411
  }
5398
5412
  if (errorListenerCleanup) {
5399
5413
  errorListenerCleanup();
@@ -5674,8 +5688,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5674
5688
  }
5675
5689
  this.inAdBreak = true;
5676
5690
  this.currentAdBreakStartWallClockMs = Date.now();
5691
+ this.adBreakPlayedDurationMs = 0;
5677
5692
  this.currentAdIndex = 0;
5678
- this.totalAdsInBreak = 1;
5693
+ this.totalAdsInBreak = 0;
5679
5694
  this.adPodQueue = [];
5680
5695
  this.showAds = true;
5681
5696
  this.showPlaceholderLayer();
@@ -5729,14 +5744,30 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5729
5744
  case 1:
5730
5745
  _state.trys.push([
5731
5746
  1,
5732
- 8,
5747
+ 12,
5733
5748
  ,
5734
- 14
5749
+ 20
5735
5750
  ]);
5736
5751
  if (!(usePreloadedAd && preloadedController)) return [
5752
+ 3,
5753
+ 5
5754
+ ];
5755
+ if (!!this.ensureLoadedAdFitsBudget(preloadedController)) return [
5737
5756
  3,
5738
5757
  3
5739
5758
  ];
5759
+ this.rejectLoadedAdForDuration(preloadedController);
5760
+ this.startContinuousFetching(baseVastUrl);
5761
+ return [
5762
+ 4,
5763
+ this.tryNextAvailableAdWithRateLimit()
5764
+ ];
5765
+ case 2:
5766
+ _state.sent();
5767
+ return [
5768
+ 2
5769
+ ];
5770
+ case 3:
5740
5771
  preservedMuted = this.adPlayer.getOriginalMutedState();
5741
5772
  preservedVolume = this.adPlayer.getOriginalVolume();
5742
5773
  this.adPlayer.destroy();
@@ -5749,7 +5780,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5749
5780
  }
5750
5781
  this.adPlayer.resume();
5751
5782
  this.consecutiveFailures = 0;
5752
- this.currentAdIndex++;
5753
5783
  this.startContinuousFetching(baseVastUrl);
5754
5784
  if (!this.preloadPoolActive) {
5755
5785
  this.preloadPoolActive = true;
@@ -5759,8 +5789,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5759
5789
  4,
5760
5790
  this.adPlayer.play()
5761
5791
  ];
5762
- case 2:
5792
+ case 4:
5763
5793
  _state.sent();
5794
+ this.markAdStarted();
5764
5795
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5765
5796
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5766
5797
  }
@@ -5768,21 +5799,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5768
5799
  this.adPlayer.setAdVolume(adVolume);
5769
5800
  return [
5770
5801
  3,
5771
- 7
5802
+ 11
5772
5803
  ];
5773
- case 3:
5804
+ case 5:
5774
5805
  return [
5775
5806
  4,
5776
5807
  this.enforceGlobalRateLimit()
5777
5808
  ];
5778
- case 4:
5809
+ case 6:
5779
5810
  _state.sent();
5780
5811
  this.lastAdRequestTime = Date.now();
5781
5812
  return [
5782
5813
  4,
5783
5814
  this.adPlayer.requestAds(firstAdUrl)
5784
5815
  ];
5785
- case 5:
5816
+ case 7:
5786
5817
  _state.sent();
5787
5818
  sendAdLoadedTracking(this.config.licenseKey, {
5788
5819
  source: "hls",
@@ -5792,8 +5823,24 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5792
5823
  if (this.config.debugAdTiming) {
5793
5824
  console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
5794
5825
  }
5826
+ if (!!this.ensureLoadedAdFitsBudget()) return [
5827
+ 3,
5828
+ 9
5829
+ ];
5830
+ this.rejectLoadedAdForDuration();
5831
+ this.recreateAdController();
5832
+ this.startContinuousFetching(baseVastUrl);
5833
+ return [
5834
+ 4,
5835
+ this.tryNextAvailableAdWithRateLimit()
5836
+ ];
5837
+ case 8:
5838
+ _state.sent();
5839
+ return [
5840
+ 2
5841
+ ];
5842
+ case 9:
5795
5843
  this.consecutiveFailures = 0;
5796
- this.currentAdIndex++;
5797
5844
  this.startContinuousFetching(baseVastUrl);
5798
5845
  if (!this.preloadPoolActive) {
5799
5846
  this.startPreloadPool(baseVastUrl, []);
@@ -5802,44 +5849,61 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5802
5849
  4,
5803
5850
  this.adPlayer.play()
5804
5851
  ];
5805
- case 6:
5852
+ case 10:
5806
5853
  _state.sent();
5854
+ this.markAdStarted();
5807
5855
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5808
5856
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5809
5857
  }
5810
5858
  adVolume1 = currentMuted ? 0 : currentVolume;
5811
5859
  this.adPlayer.setAdVolume(adVolume1);
5812
- _state.label = 7;
5813
- case 7:
5860
+ _state.label = 11;
5861
+ case 11:
5814
5862
  return [
5815
5863
  3,
5816
- 14
5864
+ 20
5817
5865
  ];
5818
- case 8:
5866
+ case 12:
5819
5867
  error = _state.sent();
5820
5868
  if (this.config.debugAdTiming) {
5821
5869
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F First ad request failed:", error);
5822
5870
  }
5823
5871
  if (!!usePreloadedAd) return [
5824
5872
  3,
5825
- 12
5873
+ 18
5826
5874
  ];
5827
5875
  fallbackPreloaded = this.getPreloadedAd();
5828
5876
  if (!fallbackPreloaded) return [
5829
5877
  3,
5830
- 12
5878
+ 18
5831
5879
  ];
5832
5880
  if (this.config.debugAdTiming) {
5833
5881
  console.log("[CONTINUOUS-FETCH] \uD83D\uDD04 First ad failed, using preloaded fallback");
5834
5882
  }
5835
- _state.label = 9;
5836
- case 9:
5883
+ _state.label = 13;
5884
+ case 13:
5837
5885
  _state.trys.push([
5838
- 9,
5839
- 11,
5886
+ 13,
5887
+ 17,
5840
5888
  ,
5841
- 12
5889
+ 18
5842
5890
  ]);
5891
+ if (!!this.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
5892
+ 3,
5893
+ 15
5894
+ ];
5895
+ this.rejectLoadedAdForDuration(fallbackPreloaded.adController);
5896
+ this.startContinuousFetching(baseVastUrl);
5897
+ return [
5898
+ 4,
5899
+ this.tryNextAvailableAdWithRateLimit()
5900
+ ];
5901
+ case 14:
5902
+ _state.sent();
5903
+ return [
5904
+ 2
5905
+ ];
5906
+ case 15:
5843
5907
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
5844
5908
  preservedVolume1 = this.adPlayer.getOriginalVolume();
5845
5909
  this.adPlayer.destroy();
@@ -5849,7 +5913,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5849
5913
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
5850
5914
  this.adPlayer.resume();
5851
5915
  this.consecutiveFailures = 0;
5852
- this.currentAdIndex++;
5853
5916
  this.startContinuousFetching(baseVastUrl);
5854
5917
  if (!this.preloadPoolActive) {
5855
5918
  this.preloadPoolActive = true;
@@ -5859,8 +5922,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5859
5922
  4,
5860
5923
  this.adPlayer.play()
5861
5924
  ];
5862
- case 10:
5925
+ case 16:
5863
5926
  _state.sent();
5927
+ this.markAdStarted();
5864
5928
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5865
5929
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5866
5930
  }
@@ -5869,16 +5933,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5869
5933
  return [
5870
5934
  2
5871
5935
  ];
5872
- case 11:
5936
+ case 17:
5873
5937
  fallbackError = _state.sent();
5874
5938
  if (this.config.debugAdTiming) {
5875
5939
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded fallback also failed:", fallbackError);
5876
5940
  }
5877
5941
  return [
5878
5942
  3,
5879
- 12
5943
+ 18
5880
5944
  ];
5881
- case 12:
5945
+ case 18:
5882
5946
  if (this.isTemporaryAdError(error)) {
5883
5947
  this.temporaryFailureUrls.set(firstAdUrl, Date.now());
5884
5948
  if (this.config.debugAdTiming) {
@@ -5896,13 +5960,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5896
5960
  4,
5897
5961
  this.tryNextAvailableAdWithRateLimit()
5898
5962
  ];
5899
- case 13:
5963
+ case 19:
5900
5964
  _state.sent();
5901
5965
  return [
5902
5966
  3,
5903
- 14
5967
+ 20
5904
5968
  ];
5905
- case 14:
5969
+ case 20:
5906
5970
  return [
5907
5971
  2
5908
5972
  ];
@@ -5943,10 +6007,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5943
6007
  return _ts_generator(this, function(_state) {
5944
6008
  switch(_state.label){
5945
6009
  case 0:
5946
- remaining = _this.getRemainingAdMs();
6010
+ remaining = Math.min(_this.getWallClockRemainingAdMs(), _this.getDurationBudgetRemainingMs());
5947
6011
  if (remaining <= _this.getMinRemainingMsToStartAd()) {
5948
6012
  if (_this.config.debugAdTiming) {
5949
- console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (".concat(remaining, "ms), stopping URL generation"));
6013
+ console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (wall=".concat(_this.getWallClockRemainingAdMs(), "ms, budget=").concat(_this.getDurationBudgetRemainingMs(), "ms), stopping URL generation"));
5950
6014
  }
5951
6015
  return [
5952
6016
  2,
@@ -6028,7 +6092,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6028
6092
  });
6029
6093
  }
6030
6094
  _this.adRequestQueue.push(newAdUrl);
6031
- _this.totalAdsInBreak++;
6032
6095
  generationDelay = _this.consecutiveFailures > 0 ? Math.min(1e3 * Math.pow(2, _this.consecutiveFailures), 5e3) : 500;
6033
6096
  return [
6034
6097
  4,
@@ -6197,7 +6260,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6197
6260
  preloaded = this.getPreloadedAd();
6198
6261
  if (!preloaded) return [
6199
6262
  3,
6200
- 4
6263
+ 6
6201
6264
  ];
6202
6265
  if (this.config.debugAdTiming) {
6203
6266
  console.log("[CONTINUOUS-FETCH] \uD83C\uDFAF Using preloaded ad from pool (".concat(this.preloadPool.length, " remaining in pool)"));
@@ -6211,10 +6274,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6211
6274
  case 1:
6212
6275
  _state.trys.push([
6213
6276
  1,
6214
- 3,
6277
+ 5,
6215
6278
  ,
6216
- 4
6279
+ 6
6217
6280
  ]);
6281
+ if (!!this.ensureLoadedAdFitsBudget(preloaded.adController)) return [
6282
+ 3,
6283
+ 3
6284
+ ];
6285
+ this.rejectLoadedAdForDuration(preloaded.adController);
6286
+ return [
6287
+ 4,
6288
+ this.tryNextAvailableAdWithRateLimit()
6289
+ ];
6290
+ case 2:
6291
+ _state.sent();
6292
+ return [
6293
+ 2
6294
+ ];
6295
+ case 3:
6218
6296
  preservedMuted = this.adPlayer.getOriginalMutedState();
6219
6297
  preservedVolume = this.adPlayer.getOriginalVolume();
6220
6298
  this.adPlayer.destroy();
@@ -6223,15 +6301,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6223
6301
  this.attachImaEventListeners();
6224
6302
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
6225
6303
  this.adPlayer.resume();
6226
- this.currentAdIndex++;
6227
- this.totalAdRequestsInBreak++;
6228
- this.lastAdRequestTime = Date.now();
6229
6304
  return [
6230
6305
  4,
6231
6306
  this.adPlayer.play()
6232
6307
  ];
6233
- case 2:
6308
+ case 4:
6234
6309
  _state.sent();
6310
+ this.markAdStarted();
6235
6311
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6236
6312
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
6237
6313
  }
@@ -6240,25 +6316,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6240
6316
  return [
6241
6317
  2
6242
6318
  ];
6243
- case 3:
6319
+ case 5:
6244
6320
  error = _state.sent();
6245
6321
  if (this.config.debugAdTiming) {
6246
6322
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded ad failed to play:", error);
6247
6323
  }
6248
6324
  return [
6249
6325
  3,
6250
- 4
6326
+ 6
6251
6327
  ];
6252
- case 4:
6328
+ case 6:
6253
6329
  if (!(this.adRequestQueue.length > 0)) return [
6254
6330
  3,
6255
- 10
6331
+ 14
6256
6332
  ];
6257
6333
  nextAdUrl = this.adRequestQueue.shift();
6258
6334
  if (!nextAdUrl) {
6259
6335
  return [
6260
6336
  3,
6261
- 4
6337
+ 6
6262
6338
  ];
6263
6339
  }
6264
6340
  if (this.failedVastUrls.has(nextAdUrl)) {
@@ -6267,7 +6343,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6267
6343
  }
6268
6344
  return [
6269
6345
  3,
6270
- 4
6346
+ 6
6271
6347
  ];
6272
6348
  }
6273
6349
  if (this.isUrlInCooldown(nextAdUrl)) {
@@ -6276,40 +6352,51 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6276
6352
  }
6277
6353
  return [
6278
6354
  3,
6279
- 4
6355
+ 6
6280
6356
  ];
6281
6357
  }
6282
6358
  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)"));
6359
+ 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
6360
  }
6285
- this.currentAdIndex++;
6286
- this.totalAdRequestsInBreak++;
6287
- this.lastAdRequestTime = Date.now();
6288
- _state.label = 5;
6289
- case 5:
6361
+ _state.label = 7;
6362
+ case 7:
6290
6363
  _state.trys.push([
6291
- 5,
6292
6364
  7,
6365
+ 9,
6293
6366
  ,
6294
- 9
6367
+ 13
6295
6368
  ]);
6296
6369
  return [
6297
6370
  4,
6298
6371
  this.playSingleAd(nextAdUrl)
6299
6372
  ];
6300
- case 6:
6373
+ case 8:
6301
6374
  _state.sent();
6302
6375
  this.consecutiveFailures = 0;
6303
6376
  return [
6304
6377
  3,
6305
- 9
6378
+ 13
6306
6379
  ];
6307
- case 7:
6380
+ case 9:
6308
6381
  error1 = _state.sent();
6309
6382
  errorMessage = error1.message;
6310
6383
  if (this.config.debugAdTiming) {
6311
6384
  console.log("[CONTINUOUS-FETCH] \u274C Ad request failed:", errorMessage);
6312
6385
  }
6386
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6387
+ 3,
6388
+ 11
6389
+ ];
6390
+ return [
6391
+ 4,
6392
+ this.tryNextAvailableAdWithRateLimit()
6393
+ ];
6394
+ case 10:
6395
+ _state.sent();
6396
+ return [
6397
+ 2
6398
+ ];
6399
+ case 11:
6313
6400
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6314
6401
  this.consecutiveFailures++;
6315
6402
  }
@@ -6317,21 +6404,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6317
6404
  4,
6318
6405
  this.tryNextAvailableAdWithRateLimit()
6319
6406
  ];
6320
- case 8:
6407
+ case 12:
6321
6408
  _state.sent();
6322
6409
  return [
6323
6410
  3,
6324
- 9
6411
+ 13
6325
6412
  ];
6326
- case 9:
6413
+ case 13:
6327
6414
  return [
6328
6415
  2
6329
6416
  ];
6330
- case 10:
6417
+ case 14:
6331
6418
  maxRetries = 3;
6332
6419
  if (!(this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3)) return [
6333
6420
  3,
6334
- 13
6421
+ 17
6335
6422
  ];
6336
6423
  if (this.config.debugAdTiming) {
6337
6424
  console.log("[CONTINUOUS-FETCH] ⏳ Queue empty, waiting for URLs... (".concat(retryCount + 1, "/").concat(maxRetries, ")"));
@@ -6342,18 +6429,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6342
6429
  return setTimeout(resolve, 500);
6343
6430
  })
6344
6431
  ];
6345
- case 11:
6432
+ case 15:
6346
6433
  _state.sent();
6347
6434
  return [
6348
6435
  4,
6349
6436
  this.tryNextAvailableAd(retryCount + 1)
6350
6437
  ];
6351
- case 12:
6438
+ case 16:
6352
6439
  _state.sent();
6353
6440
  return [
6354
6441
  2
6355
6442
  ];
6356
- case 13:
6443
+ case 17:
6357
6444
  if (!this.isShowingPlaceholder && remaining > 1e3) {
6358
6445
  this.showPlaceholderAndWaitForAds();
6359
6446
  } else {
@@ -6416,7 +6503,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6416
6503
  case 1:
6417
6504
  if (!(i < maxChecks)) return [
6418
6505
  3,
6419
- 10
6506
+ 12
6420
6507
  ];
6421
6508
  return [
6422
6509
  4,
@@ -6437,7 +6524,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6437
6524
  }
6438
6525
  return [
6439
6526
  3,
6440
- 10
6527
+ 12
6441
6528
  ];
6442
6529
  }
6443
6530
  if (!this.hasTimeToStartAnotherAd()) {
@@ -6446,19 +6533,19 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6446
6533
  }
6447
6534
  return [
6448
6535
  3,
6449
- 10
6536
+ 12
6450
6537
  ];
6451
6538
  }
6452
6539
  _state.label = 3;
6453
6540
  case 3:
6454
6541
  if (!(this.adRequestQueue.length > 0)) return [
6455
6542
  3,
6456
- 9
6543
+ 11
6457
6544
  ];
6458
6545
  if (!this.hasTimeToStartAnotherAd()) {
6459
6546
  return [
6460
6547
  3,
6461
- 9
6548
+ 11
6462
6549
  ];
6463
6550
  }
6464
6551
  nextAdUrl = this.adRequestQueue.shift();
@@ -6485,16 +6572,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6485
6572
  }
6486
6573
  this.isShowingPlaceholder = false;
6487
6574
  this.adPlayer.hidePlaceholder();
6488
- this.currentAdIndex++;
6489
- this.totalAdRequestsInBreak++;
6490
- this.lastAdRequestTime = Date.now();
6491
6575
  _state.label = 4;
6492
6576
  case 4:
6493
6577
  _state.trys.push([
6494
6578
  4,
6495
6579
  6,
6496
6580
  ,
6497
- 8
6581
+ 10
6498
6582
  ]);
6499
6583
  return [
6500
6584
  4,
@@ -6505,11 +6589,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6505
6589
  this.consecutiveFailures = 0;
6506
6590
  return [
6507
6591
  3,
6508
- 8
6592
+ 10
6509
6593
  ];
6510
6594
  case 6:
6511
6595
  error = _state.sent();
6512
6596
  errorMessage = error.message;
6597
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6598
+ 3,
6599
+ 8
6600
+ ];
6601
+ return [
6602
+ 4,
6603
+ this.tryNextAvailableAdWithRateLimit()
6604
+ ];
6605
+ case 7:
6606
+ _state.sent();
6607
+ return [
6608
+ 2
6609
+ ];
6610
+ case 8:
6513
6611
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6514
6612
  this.consecutiveFailures++;
6515
6613
  }
@@ -6517,23 +6615,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6517
6615
  4,
6518
6616
  this.tryNextAvailableAdWithRateLimit()
6519
6617
  ];
6520
- case 7:
6618
+ case 9:
6521
6619
  _state.sent();
6522
6620
  return [
6523
6621
  3,
6524
- 8
6622
+ 10
6525
6623
  ];
6526
- case 8:
6624
+ case 10:
6527
6625
  return [
6528
6626
  2
6529
6627
  ];
6530
- case 9:
6628
+ case 11:
6531
6629
  i++;
6532
6630
  return [
6533
6631
  3,
6534
6632
  1
6535
6633
  ];
6536
- case 10:
6634
+ case 12:
6537
6635
  if (this.config.debugAdTiming) {
6538
6636
  console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout, ending ad break");
6539
6637
  }
@@ -7005,6 +7103,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7005
7103
  2
7006
7104
  ];
7007
7105
  }
7106
+ if (!this.ensureLoadedAdFitsBudget()) {
7107
+ this.rejectLoadedAdForDuration();
7108
+ this.recreateAdController();
7109
+ throw new Error("ad_duration_exceeds_budget");
7110
+ }
7008
7111
  _state.label = 4;
7009
7112
  case 4:
7010
7113
  _state.trys.push([
@@ -7020,6 +7123,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7020
7123
  ];
7021
7124
  case 5:
7022
7125
  _state.sent();
7126
+ this.markAdStarted();
7023
7127
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7024
7128
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7025
7129
  }
@@ -7051,6 +7155,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7051
7155
  ,
7052
7156
  10
7053
7157
  ]);
7158
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback.adController)) {
7159
+ this.rejectLoadedAdForDuration(preloadedFallback.adController);
7160
+ throw playError;
7161
+ }
7054
7162
  this.clearAdFailsafeTimer();
7055
7163
  preservedMuted = this.adPlayer.getOriginalMutedState();
7056
7164
  preservedVolume = this.adPlayer.getOriginalVolume();
@@ -7061,14 +7169,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7061
7169
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
7062
7170
  this.adPlayer.resume();
7063
7171
  this.consecutiveFailures = 0;
7064
- this.currentAdIndex++;
7065
- this.totalAdRequestsInBreak++;
7066
7172
  return [
7067
7173
  4,
7068
7174
  this.adPlayer.play()
7069
7175
  ];
7070
7176
  case 8:
7071
7177
  _state.sent();
7178
+ this.markAdStarted();
7072
7179
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7073
7180
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7074
7181
  }
@@ -7129,6 +7236,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7129
7236
  ,
7130
7237
  16
7131
7238
  ]);
7239
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback1.adController)) {
7240
+ this.rejectLoadedAdForDuration(preloadedFallback1.adController);
7241
+ throw error;
7242
+ }
7132
7243
  this.clearAdRequestWatchdog();
7133
7244
  this.clearAdFailsafeTimer();
7134
7245
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
@@ -7140,14 +7251,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7140
7251
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
7141
7252
  this.adPlayer.resume();
7142
7253
  this.consecutiveFailures = 0;
7143
- this.currentAdIndex++;
7144
- this.totalAdRequestsInBreak++;
7145
7254
  return [
7146
7255
  4,
7147
7256
  this.adPlayer.play()
7148
7257
  ];
7149
7258
  case 14:
7150
7259
  _state.sent();
7260
+ this.markAdStarted();
7151
7261
  this.adPlayer.setAdVolume(this.getAdAudioVolume());
7152
7262
  return [
7153
7263
  2
@@ -7208,6 +7318,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7208
7318
  this.adRequestQueue = [];
7209
7319
  this.inAdBreak = false;
7210
7320
  this.adDetectSentForCurrentBreak = false;
7321
+ this.adBreakPlayedDurationMs = 0;
7211
7322
  this.activeScte35BreakKey = void 0;
7212
7323
  this.scheduledScte35BreakKey = void 0;
7213
7324
  this.expectedAdBreakDurationMs = void 0;
@@ -7397,13 +7508,117 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7397
7508
  }
7398
7509
  },
7399
7510
  {
7400
- key: "getRemainingAdMs",
7401
- value: function getRemainingAdMs() {
7402
- if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) return 0;
7511
+ key: "getWallClockRemainingAdMs",
7512
+ value: function getWallClockRemainingAdMs() {
7513
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
7514
+ return 0;
7515
+ }
7403
7516
  var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
7404
7517
  return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
7405
7518
  }
7406
7519
  },
7520
+ {
7521
+ key: "getDurationBudgetRemainingMs",
7522
+ value: function getDurationBudgetRemainingMs() {
7523
+ if (this.expectedAdBreakDurationMs == null) {
7524
+ return 0;
7525
+ }
7526
+ return Math.max(0, this.expectedAdBreakDurationMs - this.adBreakPlayedDurationMs);
7527
+ }
7528
+ },
7529
+ {
7530
+ key: "getLoadedAdDurationMs",
7531
+ value: function getLoadedAdDurationMs() {
7532
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7533
+ var _adController_getLoadedAdDuration;
7534
+ var durationSec = (_adController_getLoadedAdDuration = adController.getLoadedAdDuration) === null || _adController_getLoadedAdDuration === void 0 ? void 0 : _adController_getLoadedAdDuration.call(adController);
7535
+ if (durationSec == null || durationSec <= 0) {
7536
+ return 0;
7537
+ }
7538
+ return Math.round(durationSec * 1e3);
7539
+ }
7540
+ },
7541
+ {
7542
+ key: "adFitsRemainingBudget",
7543
+ value: function adFitsRemainingBudget(durationMs) {
7544
+ var adController = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : this.adPlayer;
7545
+ if (durationMs <= 0) {
7546
+ return true;
7547
+ }
7548
+ return durationMs <= this.getDurationBudgetRemainingMs();
7549
+ }
7550
+ },
7551
+ {
7552
+ key: "logAdDurationDecision",
7553
+ value: function logAdDurationDecision(accepted, durationMs) {
7554
+ var adController = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : this.adPlayer;
7555
+ var _ref;
7556
+ var _adController_getLoadedAdId;
7557
+ if (!this.config.debugAdTiming) {
7558
+ return;
7559
+ }
7560
+ var remainingBudget = this.getDurationBudgetRemainingMs();
7561
+ var adId = (_ref = (_adController_getLoadedAdId = adController.getLoadedAdId) === null || _adController_getLoadedAdId === void 0 ? void 0 : _adController_getLoadedAdId.call(adController)) !== null && _ref !== void 0 ? _ref : "unknown";
7562
+ if (accepted) {
7563
+ console.log("[AD-DURATION] Accepted ad id=".concat(adId, ", duration=").concat(durationMs, "ms, budget remaining after play=").concat(Math.max(0, remainingBudget - durationMs), "ms"));
7564
+ } else {
7565
+ console.log("[AD-DURATION] Rejected ad id=".concat(adId, ", duration=").concat(durationMs, "ms exceeds remaining budget=").concat(remainingBudget, "ms — requesting another ad"));
7566
+ }
7567
+ }
7568
+ },
7569
+ {
7570
+ key: "recordPlayedAdDuration",
7571
+ value: function recordPlayedAdDuration() {
7572
+ var durationMs = this.getLoadedAdDurationMs();
7573
+ if (durationMs <= 0) {
7574
+ return;
7575
+ }
7576
+ this.adBreakPlayedDurationMs += durationMs;
7577
+ if (this.config.debugAdTiming) {
7578
+ var _this_expectedAdBreakDurationMs;
7579
+ 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"));
7580
+ }
7581
+ }
7582
+ },
7583
+ {
7584
+ key: "ensureLoadedAdFitsBudget",
7585
+ value: function ensureLoadedAdFitsBudget() {
7586
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7587
+ var durationMs = this.getLoadedAdDurationMs(adController);
7588
+ var fits = this.adFitsRemainingBudget(durationMs, adController);
7589
+ this.logAdDurationDecision(fits, durationMs, adController);
7590
+ return fits;
7591
+ }
7592
+ },
7593
+ {
7594
+ key: "rejectLoadedAdForDuration",
7595
+ value: function rejectLoadedAdForDuration() {
7596
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7597
+ try {
7598
+ adController.destroy();
7599
+ } catch (unused) {}
7600
+ }
7601
+ },
7602
+ {
7603
+ key: "markAdStarted",
7604
+ value: function markAdStarted() {
7605
+ this.currentAdIndex++;
7606
+ this.totalAdsInBreak = this.currentAdIndex;
7607
+ this.totalAdRequestsInBreak++;
7608
+ this.lastAdRequestTime = Date.now();
7609
+ }
7610
+ },
7611
+ {
7612
+ key: "getRemainingAdMs",
7613
+ value: function getRemainingAdMs() {
7614
+ var wallClockRemaining = this.getWallClockRemainingAdMs();
7615
+ var budgetRemaining = this.getDurationBudgetRemainingMs();
7616
+ if (this.expectedAdBreakDurationMs == null) {
7617
+ return wallClockRemaining;
7618
+ }
7619
+ return Math.min(wallClockRemaining, budgetRemaining);
7620
+ }
7621
+ },
7407
7622
  {
7408
7623
  key: "getMinRemainingMsToStartAd",
7409
7624
  value: function getMinRemainingMsToStartAd() {
@@ -7417,7 +7632,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7417
7632
  {
7418
7633
  key: "hasTimeToStartAnotherAd",
7419
7634
  value: function hasTimeToStartAnotherAd() {
7420
- return this.getRemainingAdMs() > this.getMinRemainingMsToStartAd();
7635
+ var minRemaining = this.getMinRemainingMsToStartAd();
7636
+ return this.getWallClockRemainingAdMs() > minRemaining && this.getDurationBudgetRemainingMs() > minRemaining;
7421
7637
  }
7422
7638
  },
7423
7639
  {