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.
@@ -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 ? {
@@ -5266,7 +5275,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5266
5275
  epoch = this.adBreakEpoch;
5267
5276
  loadPromise = function() {
5268
5277
  return _async_to_generator(function() {
5269
- var _this, hasAdError, adErrorPayload, errorListenerCleanup, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, lateErrorListener, error;
5278
+ var _this, hasAdError, adErrorPayload, errorListenerCleanup, _preloadAd_getLoadedAdDuration, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, loadedDuration, lateErrorListener, _poolEntry_durationSeconds, error;
5270
5279
  return _ts_generator(this, function(_state) {
5271
5280
  switch(_state.label){
5272
5281
  case 0:
@@ -5362,6 +5371,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5362
5371
  isReady: true,
5363
5372
  loadPromise: Promise.resolve()
5364
5373
  };
5374
+ loadedDuration = (_preloadAd_getLoadedAdDuration = preloadAd.getLoadedAdDuration) === null || _preloadAd_getLoadedAdDuration === void 0 ? void 0 : _preloadAd_getLoadedAdDuration.call(preloadAd);
5375
+ if (loadedDuration != null && loadedDuration > 0) {
5376
+ poolEntry.durationSeconds = loadedDuration;
5377
+ }
5365
5378
  lateErrorListener = function lateErrorListener(payload) {
5366
5379
  var index = _this.preloadPool.findIndex(function(entry) {
5367
5380
  return entry.vastUrl === vastUrl;
@@ -5379,7 +5392,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5379
5392
  preloadAd.on("ad_error", lateErrorListener);
5380
5393
  this.preloadPool.push(poolEntry);
5381
5394
  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, ")"));
5395
+ ;
5396
+ 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
5397
  }
5384
5398
  if (errorListenerCleanup) {
5385
5399
  errorListenerCleanup();
@@ -5660,8 +5674,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5660
5674
  }
5661
5675
  this.inAdBreak = true;
5662
5676
  this.currentAdBreakStartWallClockMs = Date.now();
5677
+ this.adBreakPlayedDurationMs = 0;
5663
5678
  this.currentAdIndex = 0;
5664
- this.totalAdsInBreak = 1;
5679
+ this.totalAdsInBreak = 0;
5665
5680
  this.adPodQueue = [];
5666
5681
  this.showAds = true;
5667
5682
  this.showPlaceholderLayer();
@@ -5715,14 +5730,30 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5715
5730
  case 1:
5716
5731
  _state.trys.push([
5717
5732
  1,
5718
- 8,
5733
+ 12,
5719
5734
  ,
5720
- 14
5735
+ 20
5721
5736
  ]);
5722
5737
  if (!(usePreloadedAd && preloadedController)) return [
5738
+ 3,
5739
+ 5
5740
+ ];
5741
+ if (!!this.ensureLoadedAdFitsBudget(preloadedController)) return [
5723
5742
  3,
5724
5743
  3
5725
5744
  ];
5745
+ this.rejectLoadedAdForDuration(preloadedController);
5746
+ this.startContinuousFetching(baseVastUrl);
5747
+ return [
5748
+ 4,
5749
+ this.tryNextAvailableAdWithRateLimit()
5750
+ ];
5751
+ case 2:
5752
+ _state.sent();
5753
+ return [
5754
+ 2
5755
+ ];
5756
+ case 3:
5726
5757
  preservedMuted = this.adPlayer.getOriginalMutedState();
5727
5758
  preservedVolume = this.adPlayer.getOriginalVolume();
5728
5759
  this.adPlayer.destroy();
@@ -5735,7 +5766,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5735
5766
  }
5736
5767
  this.adPlayer.resume();
5737
5768
  this.consecutiveFailures = 0;
5738
- this.currentAdIndex++;
5739
5769
  this.startContinuousFetching(baseVastUrl);
5740
5770
  if (!this.preloadPoolActive) {
5741
5771
  this.preloadPoolActive = true;
@@ -5745,8 +5775,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5745
5775
  4,
5746
5776
  this.adPlayer.play()
5747
5777
  ];
5748
- case 2:
5778
+ case 4:
5749
5779
  _state.sent();
5780
+ this.markAdStarted();
5750
5781
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5751
5782
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5752
5783
  }
@@ -5754,21 +5785,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5754
5785
  this.adPlayer.setAdVolume(adVolume);
5755
5786
  return [
5756
5787
  3,
5757
- 7
5788
+ 11
5758
5789
  ];
5759
- case 3:
5790
+ case 5:
5760
5791
  return [
5761
5792
  4,
5762
5793
  this.enforceGlobalRateLimit()
5763
5794
  ];
5764
- case 4:
5795
+ case 6:
5765
5796
  _state.sent();
5766
5797
  this.lastAdRequestTime = Date.now();
5767
5798
  return [
5768
5799
  4,
5769
5800
  this.adPlayer.requestAds(firstAdUrl)
5770
5801
  ];
5771
- case 5:
5802
+ case 7:
5772
5803
  _state.sent();
5773
5804
  sendAdLoadedTracking(this.config.licenseKey, {
5774
5805
  source: "hls",
@@ -5778,8 +5809,24 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5778
5809
  if (this.config.debugAdTiming) {
5779
5810
  console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
5780
5811
  }
5812
+ if (!!this.ensureLoadedAdFitsBudget()) return [
5813
+ 3,
5814
+ 9
5815
+ ];
5816
+ this.rejectLoadedAdForDuration();
5817
+ this.recreateAdController();
5818
+ this.startContinuousFetching(baseVastUrl);
5819
+ return [
5820
+ 4,
5821
+ this.tryNextAvailableAdWithRateLimit()
5822
+ ];
5823
+ case 8:
5824
+ _state.sent();
5825
+ return [
5826
+ 2
5827
+ ];
5828
+ case 9:
5781
5829
  this.consecutiveFailures = 0;
5782
- this.currentAdIndex++;
5783
5830
  this.startContinuousFetching(baseVastUrl);
5784
5831
  if (!this.preloadPoolActive) {
5785
5832
  this.startPreloadPool(baseVastUrl, []);
@@ -5788,44 +5835,61 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5788
5835
  4,
5789
5836
  this.adPlayer.play()
5790
5837
  ];
5791
- case 6:
5838
+ case 10:
5792
5839
  _state.sent();
5840
+ this.markAdStarted();
5793
5841
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5794
5842
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5795
5843
  }
5796
5844
  adVolume1 = currentMuted ? 0 : currentVolume;
5797
5845
  this.adPlayer.setAdVolume(adVolume1);
5798
- _state.label = 7;
5799
- case 7:
5846
+ _state.label = 11;
5847
+ case 11:
5800
5848
  return [
5801
5849
  3,
5802
- 14
5850
+ 20
5803
5851
  ];
5804
- case 8:
5852
+ case 12:
5805
5853
  error = _state.sent();
5806
5854
  if (this.config.debugAdTiming) {
5807
5855
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F First ad request failed:", error);
5808
5856
  }
5809
5857
  if (!!usePreloadedAd) return [
5810
5858
  3,
5811
- 12
5859
+ 18
5812
5860
  ];
5813
5861
  fallbackPreloaded = this.getPreloadedAd();
5814
5862
  if (!fallbackPreloaded) return [
5815
5863
  3,
5816
- 12
5864
+ 18
5817
5865
  ];
5818
5866
  if (this.config.debugAdTiming) {
5819
5867
  console.log("[CONTINUOUS-FETCH] \uD83D\uDD04 First ad failed, using preloaded fallback");
5820
5868
  }
5821
- _state.label = 9;
5822
- case 9:
5869
+ _state.label = 13;
5870
+ case 13:
5823
5871
  _state.trys.push([
5824
- 9,
5825
- 11,
5872
+ 13,
5873
+ 17,
5826
5874
  ,
5827
- 12
5875
+ 18
5828
5876
  ]);
5877
+ if (!!this.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
5878
+ 3,
5879
+ 15
5880
+ ];
5881
+ this.rejectLoadedAdForDuration(fallbackPreloaded.adController);
5882
+ this.startContinuousFetching(baseVastUrl);
5883
+ return [
5884
+ 4,
5885
+ this.tryNextAvailableAdWithRateLimit()
5886
+ ];
5887
+ case 14:
5888
+ _state.sent();
5889
+ return [
5890
+ 2
5891
+ ];
5892
+ case 15:
5829
5893
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
5830
5894
  preservedVolume1 = this.adPlayer.getOriginalVolume();
5831
5895
  this.adPlayer.destroy();
@@ -5835,7 +5899,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5835
5899
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
5836
5900
  this.adPlayer.resume();
5837
5901
  this.consecutiveFailures = 0;
5838
- this.currentAdIndex++;
5839
5902
  this.startContinuousFetching(baseVastUrl);
5840
5903
  if (!this.preloadPoolActive) {
5841
5904
  this.preloadPoolActive = true;
@@ -5845,8 +5908,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5845
5908
  4,
5846
5909
  this.adPlayer.play()
5847
5910
  ];
5848
- case 10:
5911
+ case 16:
5849
5912
  _state.sent();
5913
+ this.markAdStarted();
5850
5914
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5851
5915
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5852
5916
  }
@@ -5855,16 +5919,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5855
5919
  return [
5856
5920
  2
5857
5921
  ];
5858
- case 11:
5922
+ case 17:
5859
5923
  fallbackError = _state.sent();
5860
5924
  if (this.config.debugAdTiming) {
5861
5925
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded fallback also failed:", fallbackError);
5862
5926
  }
5863
5927
  return [
5864
5928
  3,
5865
- 12
5929
+ 18
5866
5930
  ];
5867
- case 12:
5931
+ case 18:
5868
5932
  if (this.isTemporaryAdError(error)) {
5869
5933
  this.temporaryFailureUrls.set(firstAdUrl, Date.now());
5870
5934
  if (this.config.debugAdTiming) {
@@ -5882,13 +5946,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5882
5946
  4,
5883
5947
  this.tryNextAvailableAdWithRateLimit()
5884
5948
  ];
5885
- case 13:
5949
+ case 19:
5886
5950
  _state.sent();
5887
5951
  return [
5888
5952
  3,
5889
- 14
5953
+ 20
5890
5954
  ];
5891
- case 14:
5955
+ case 20:
5892
5956
  return [
5893
5957
  2
5894
5958
  ];
@@ -5929,10 +5993,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5929
5993
  return _ts_generator(this, function(_state) {
5930
5994
  switch(_state.label){
5931
5995
  case 0:
5932
- remaining = _this.getRemainingAdMs();
5996
+ remaining = Math.min(_this.getWallClockRemainingAdMs(), _this.getDurationBudgetRemainingMs());
5933
5997
  if (remaining <= _this.getMinRemainingMsToStartAd()) {
5934
5998
  if (_this.config.debugAdTiming) {
5935
- console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (".concat(remaining, "ms), stopping URL generation"));
5999
+ console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (wall=".concat(_this.getWallClockRemainingAdMs(), "ms, budget=").concat(_this.getDurationBudgetRemainingMs(), "ms), stopping URL generation"));
5936
6000
  }
5937
6001
  return [
5938
6002
  2,
@@ -6014,7 +6078,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6014
6078
  });
6015
6079
  }
6016
6080
  _this.adRequestQueue.push(newAdUrl);
6017
- _this.totalAdsInBreak++;
6018
6081
  generationDelay = _this.consecutiveFailures > 0 ? Math.min(1e3 * Math.pow(2, _this.consecutiveFailures), 5e3) : 500;
6019
6082
  return [
6020
6083
  4,
@@ -6183,7 +6246,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6183
6246
  preloaded = this.getPreloadedAd();
6184
6247
  if (!preloaded) return [
6185
6248
  3,
6186
- 4
6249
+ 6
6187
6250
  ];
6188
6251
  if (this.config.debugAdTiming) {
6189
6252
  console.log("[CONTINUOUS-FETCH] \uD83C\uDFAF Using preloaded ad from pool (".concat(this.preloadPool.length, " remaining in pool)"));
@@ -6197,10 +6260,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6197
6260
  case 1:
6198
6261
  _state.trys.push([
6199
6262
  1,
6200
- 3,
6263
+ 5,
6201
6264
  ,
6202
- 4
6265
+ 6
6203
6266
  ]);
6267
+ if (!!this.ensureLoadedAdFitsBudget(preloaded.adController)) return [
6268
+ 3,
6269
+ 3
6270
+ ];
6271
+ this.rejectLoadedAdForDuration(preloaded.adController);
6272
+ return [
6273
+ 4,
6274
+ this.tryNextAvailableAdWithRateLimit()
6275
+ ];
6276
+ case 2:
6277
+ _state.sent();
6278
+ return [
6279
+ 2
6280
+ ];
6281
+ case 3:
6204
6282
  preservedMuted = this.adPlayer.getOriginalMutedState();
6205
6283
  preservedVolume = this.adPlayer.getOriginalVolume();
6206
6284
  this.adPlayer.destroy();
@@ -6209,15 +6287,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6209
6287
  this.attachImaEventListeners();
6210
6288
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
6211
6289
  this.adPlayer.resume();
6212
- this.currentAdIndex++;
6213
- this.totalAdRequestsInBreak++;
6214
- this.lastAdRequestTime = Date.now();
6215
6290
  return [
6216
6291
  4,
6217
6292
  this.adPlayer.play()
6218
6293
  ];
6219
- case 2:
6294
+ case 4:
6220
6295
  _state.sent();
6296
+ this.markAdStarted();
6221
6297
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6222
6298
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
6223
6299
  }
@@ -6226,25 +6302,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6226
6302
  return [
6227
6303
  2
6228
6304
  ];
6229
- case 3:
6305
+ case 5:
6230
6306
  error = _state.sent();
6231
6307
  if (this.config.debugAdTiming) {
6232
6308
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded ad failed to play:", error);
6233
6309
  }
6234
6310
  return [
6235
6311
  3,
6236
- 4
6312
+ 6
6237
6313
  ];
6238
- case 4:
6314
+ case 6:
6239
6315
  if (!(this.adRequestQueue.length > 0)) return [
6240
6316
  3,
6241
- 10
6317
+ 14
6242
6318
  ];
6243
6319
  nextAdUrl = this.adRequestQueue.shift();
6244
6320
  if (!nextAdUrl) {
6245
6321
  return [
6246
6322
  3,
6247
- 4
6323
+ 6
6248
6324
  ];
6249
6325
  }
6250
6326
  if (this.failedVastUrls.has(nextAdUrl)) {
@@ -6253,7 +6329,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6253
6329
  }
6254
6330
  return [
6255
6331
  3,
6256
- 4
6332
+ 6
6257
6333
  ];
6258
6334
  }
6259
6335
  if (this.isUrlInCooldown(nextAdUrl)) {
@@ -6262,40 +6338,51 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6262
6338
  }
6263
6339
  return [
6264
6340
  3,
6265
- 4
6341
+ 6
6266
6342
  ];
6267
6343
  }
6268
6344
  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)"));
6345
+ 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
6346
  }
6271
- this.currentAdIndex++;
6272
- this.totalAdRequestsInBreak++;
6273
- this.lastAdRequestTime = Date.now();
6274
- _state.label = 5;
6275
- case 5:
6347
+ _state.label = 7;
6348
+ case 7:
6276
6349
  _state.trys.push([
6277
- 5,
6278
6350
  7,
6351
+ 9,
6279
6352
  ,
6280
- 9
6353
+ 13
6281
6354
  ]);
6282
6355
  return [
6283
6356
  4,
6284
6357
  this.playSingleAd(nextAdUrl)
6285
6358
  ];
6286
- case 6:
6359
+ case 8:
6287
6360
  _state.sent();
6288
6361
  this.consecutiveFailures = 0;
6289
6362
  return [
6290
6363
  3,
6291
- 9
6364
+ 13
6292
6365
  ];
6293
- case 7:
6366
+ case 9:
6294
6367
  error1 = _state.sent();
6295
6368
  errorMessage = error1.message;
6296
6369
  if (this.config.debugAdTiming) {
6297
6370
  console.log("[CONTINUOUS-FETCH] \u274C Ad request failed:", errorMessage);
6298
6371
  }
6372
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6373
+ 3,
6374
+ 11
6375
+ ];
6376
+ return [
6377
+ 4,
6378
+ this.tryNextAvailableAdWithRateLimit()
6379
+ ];
6380
+ case 10:
6381
+ _state.sent();
6382
+ return [
6383
+ 2
6384
+ ];
6385
+ case 11:
6299
6386
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6300
6387
  this.consecutiveFailures++;
6301
6388
  }
@@ -6303,21 +6390,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6303
6390
  4,
6304
6391
  this.tryNextAvailableAdWithRateLimit()
6305
6392
  ];
6306
- case 8:
6393
+ case 12:
6307
6394
  _state.sent();
6308
6395
  return [
6309
6396
  3,
6310
- 9
6397
+ 13
6311
6398
  ];
6312
- case 9:
6399
+ case 13:
6313
6400
  return [
6314
6401
  2
6315
6402
  ];
6316
- case 10:
6403
+ case 14:
6317
6404
  maxRetries = 3;
6318
6405
  if (!(this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3)) return [
6319
6406
  3,
6320
- 13
6407
+ 17
6321
6408
  ];
6322
6409
  if (this.config.debugAdTiming) {
6323
6410
  console.log("[CONTINUOUS-FETCH] ⏳ Queue empty, waiting for URLs... (".concat(retryCount + 1, "/").concat(maxRetries, ")"));
@@ -6328,18 +6415,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6328
6415
  return setTimeout(resolve, 500);
6329
6416
  })
6330
6417
  ];
6331
- case 11:
6418
+ case 15:
6332
6419
  _state.sent();
6333
6420
  return [
6334
6421
  4,
6335
6422
  this.tryNextAvailableAd(retryCount + 1)
6336
6423
  ];
6337
- case 12:
6424
+ case 16:
6338
6425
  _state.sent();
6339
6426
  return [
6340
6427
  2
6341
6428
  ];
6342
- case 13:
6429
+ case 17:
6343
6430
  if (!this.isShowingPlaceholder && remaining > 1e3) {
6344
6431
  this.showPlaceholderAndWaitForAds();
6345
6432
  } else {
@@ -6402,7 +6489,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6402
6489
  case 1:
6403
6490
  if (!(i < maxChecks)) return [
6404
6491
  3,
6405
- 10
6492
+ 12
6406
6493
  ];
6407
6494
  return [
6408
6495
  4,
@@ -6423,7 +6510,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6423
6510
  }
6424
6511
  return [
6425
6512
  3,
6426
- 10
6513
+ 12
6427
6514
  ];
6428
6515
  }
6429
6516
  if (!this.hasTimeToStartAnotherAd()) {
@@ -6432,19 +6519,19 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6432
6519
  }
6433
6520
  return [
6434
6521
  3,
6435
- 10
6522
+ 12
6436
6523
  ];
6437
6524
  }
6438
6525
  _state.label = 3;
6439
6526
  case 3:
6440
6527
  if (!(this.adRequestQueue.length > 0)) return [
6441
6528
  3,
6442
- 9
6529
+ 11
6443
6530
  ];
6444
6531
  if (!this.hasTimeToStartAnotherAd()) {
6445
6532
  return [
6446
6533
  3,
6447
- 9
6534
+ 11
6448
6535
  ];
6449
6536
  }
6450
6537
  nextAdUrl = this.adRequestQueue.shift();
@@ -6471,16 +6558,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6471
6558
  }
6472
6559
  this.isShowingPlaceholder = false;
6473
6560
  this.adPlayer.hidePlaceholder();
6474
- this.currentAdIndex++;
6475
- this.totalAdRequestsInBreak++;
6476
- this.lastAdRequestTime = Date.now();
6477
6561
  _state.label = 4;
6478
6562
  case 4:
6479
6563
  _state.trys.push([
6480
6564
  4,
6481
6565
  6,
6482
6566
  ,
6483
- 8
6567
+ 10
6484
6568
  ]);
6485
6569
  return [
6486
6570
  4,
@@ -6491,11 +6575,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6491
6575
  this.consecutiveFailures = 0;
6492
6576
  return [
6493
6577
  3,
6494
- 8
6578
+ 10
6495
6579
  ];
6496
6580
  case 6:
6497
6581
  error = _state.sent();
6498
6582
  errorMessage = error.message;
6583
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6584
+ 3,
6585
+ 8
6586
+ ];
6587
+ return [
6588
+ 4,
6589
+ this.tryNextAvailableAdWithRateLimit()
6590
+ ];
6591
+ case 7:
6592
+ _state.sent();
6593
+ return [
6594
+ 2
6595
+ ];
6596
+ case 8:
6499
6597
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6500
6598
  this.consecutiveFailures++;
6501
6599
  }
@@ -6503,23 +6601,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6503
6601
  4,
6504
6602
  this.tryNextAvailableAdWithRateLimit()
6505
6603
  ];
6506
- case 7:
6604
+ case 9:
6507
6605
  _state.sent();
6508
6606
  return [
6509
6607
  3,
6510
- 8
6608
+ 10
6511
6609
  ];
6512
- case 8:
6610
+ case 10:
6513
6611
  return [
6514
6612
  2
6515
6613
  ];
6516
- case 9:
6614
+ case 11:
6517
6615
  i++;
6518
6616
  return [
6519
6617
  3,
6520
6618
  1
6521
6619
  ];
6522
- case 10:
6620
+ case 12:
6523
6621
  if (this.config.debugAdTiming) {
6524
6622
  console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout, ending ad break");
6525
6623
  }
@@ -6991,6 +7089,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6991
7089
  2
6992
7090
  ];
6993
7091
  }
7092
+ if (!this.ensureLoadedAdFitsBudget()) {
7093
+ this.rejectLoadedAdForDuration();
7094
+ this.recreateAdController();
7095
+ throw new Error("ad_duration_exceeds_budget");
7096
+ }
6994
7097
  _state.label = 4;
6995
7098
  case 4:
6996
7099
  _state.trys.push([
@@ -7006,6 +7109,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7006
7109
  ];
7007
7110
  case 5:
7008
7111
  _state.sent();
7112
+ this.markAdStarted();
7009
7113
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7010
7114
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7011
7115
  }
@@ -7037,6 +7141,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7037
7141
  ,
7038
7142
  10
7039
7143
  ]);
7144
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback.adController)) {
7145
+ this.rejectLoadedAdForDuration(preloadedFallback.adController);
7146
+ throw playError;
7147
+ }
7040
7148
  this.clearAdFailsafeTimer();
7041
7149
  preservedMuted = this.adPlayer.getOriginalMutedState();
7042
7150
  preservedVolume = this.adPlayer.getOriginalVolume();
@@ -7047,14 +7155,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7047
7155
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
7048
7156
  this.adPlayer.resume();
7049
7157
  this.consecutiveFailures = 0;
7050
- this.currentAdIndex++;
7051
- this.totalAdRequestsInBreak++;
7052
7158
  return [
7053
7159
  4,
7054
7160
  this.adPlayer.play()
7055
7161
  ];
7056
7162
  case 8:
7057
7163
  _state.sent();
7164
+ this.markAdStarted();
7058
7165
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7059
7166
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7060
7167
  }
@@ -7115,6 +7222,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7115
7222
  ,
7116
7223
  16
7117
7224
  ]);
7225
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback1.adController)) {
7226
+ this.rejectLoadedAdForDuration(preloadedFallback1.adController);
7227
+ throw error;
7228
+ }
7118
7229
  this.clearAdRequestWatchdog();
7119
7230
  this.clearAdFailsafeTimer();
7120
7231
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
@@ -7126,14 +7237,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7126
7237
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
7127
7238
  this.adPlayer.resume();
7128
7239
  this.consecutiveFailures = 0;
7129
- this.currentAdIndex++;
7130
- this.totalAdRequestsInBreak++;
7131
7240
  return [
7132
7241
  4,
7133
7242
  this.adPlayer.play()
7134
7243
  ];
7135
7244
  case 14:
7136
7245
  _state.sent();
7246
+ this.markAdStarted();
7137
7247
  this.adPlayer.setAdVolume(this.getAdAudioVolume());
7138
7248
  return [
7139
7249
  2
@@ -7194,6 +7304,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7194
7304
  this.adRequestQueue = [];
7195
7305
  this.inAdBreak = false;
7196
7306
  this.adDetectSentForCurrentBreak = false;
7307
+ this.adBreakPlayedDurationMs = 0;
7197
7308
  this.activeScte35BreakKey = void 0;
7198
7309
  this.scheduledScte35BreakKey = void 0;
7199
7310
  this.expectedAdBreakDurationMs = void 0;
@@ -7383,13 +7494,117 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7383
7494
  }
7384
7495
  },
7385
7496
  {
7386
- key: "getRemainingAdMs",
7387
- value: function getRemainingAdMs() {
7388
- if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) return 0;
7497
+ key: "getWallClockRemainingAdMs",
7498
+ value: function getWallClockRemainingAdMs() {
7499
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
7500
+ return 0;
7501
+ }
7389
7502
  var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
7390
7503
  return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
7391
7504
  }
7392
7505
  },
7506
+ {
7507
+ key: "getDurationBudgetRemainingMs",
7508
+ value: function getDurationBudgetRemainingMs() {
7509
+ if (this.expectedAdBreakDurationMs == null) {
7510
+ return 0;
7511
+ }
7512
+ return Math.max(0, this.expectedAdBreakDurationMs - this.adBreakPlayedDurationMs);
7513
+ }
7514
+ },
7515
+ {
7516
+ key: "getLoadedAdDurationMs",
7517
+ value: function getLoadedAdDurationMs() {
7518
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7519
+ var _adController_getLoadedAdDuration;
7520
+ var durationSec = (_adController_getLoadedAdDuration = adController.getLoadedAdDuration) === null || _adController_getLoadedAdDuration === void 0 ? void 0 : _adController_getLoadedAdDuration.call(adController);
7521
+ if (durationSec == null || durationSec <= 0) {
7522
+ return 0;
7523
+ }
7524
+ return Math.round(durationSec * 1e3);
7525
+ }
7526
+ },
7527
+ {
7528
+ key: "adFitsRemainingBudget",
7529
+ value: function adFitsRemainingBudget(durationMs) {
7530
+ var adController = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : this.adPlayer;
7531
+ if (durationMs <= 0) {
7532
+ return true;
7533
+ }
7534
+ return durationMs <= this.getDurationBudgetRemainingMs();
7535
+ }
7536
+ },
7537
+ {
7538
+ key: "logAdDurationDecision",
7539
+ value: function logAdDurationDecision(accepted, durationMs) {
7540
+ var adController = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : this.adPlayer;
7541
+ var _ref;
7542
+ var _adController_getLoadedAdId;
7543
+ if (!this.config.debugAdTiming) {
7544
+ return;
7545
+ }
7546
+ var remainingBudget = this.getDurationBudgetRemainingMs();
7547
+ var adId = (_ref = (_adController_getLoadedAdId = adController.getLoadedAdId) === null || _adController_getLoadedAdId === void 0 ? void 0 : _adController_getLoadedAdId.call(adController)) !== null && _ref !== void 0 ? _ref : "unknown";
7548
+ if (accepted) {
7549
+ console.log("[AD-DURATION] Accepted ad id=".concat(adId, ", duration=").concat(durationMs, "ms, budget remaining after play=").concat(Math.max(0, remainingBudget - durationMs), "ms"));
7550
+ } else {
7551
+ console.log("[AD-DURATION] Rejected ad id=".concat(adId, ", duration=").concat(durationMs, "ms exceeds remaining budget=").concat(remainingBudget, "ms — requesting another ad"));
7552
+ }
7553
+ }
7554
+ },
7555
+ {
7556
+ key: "recordPlayedAdDuration",
7557
+ value: function recordPlayedAdDuration() {
7558
+ var durationMs = this.getLoadedAdDurationMs();
7559
+ if (durationMs <= 0) {
7560
+ return;
7561
+ }
7562
+ this.adBreakPlayedDurationMs += durationMs;
7563
+ if (this.config.debugAdTiming) {
7564
+ var _this_expectedAdBreakDurationMs;
7565
+ 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"));
7566
+ }
7567
+ }
7568
+ },
7569
+ {
7570
+ key: "ensureLoadedAdFitsBudget",
7571
+ value: function ensureLoadedAdFitsBudget() {
7572
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7573
+ var durationMs = this.getLoadedAdDurationMs(adController);
7574
+ var fits = this.adFitsRemainingBudget(durationMs, adController);
7575
+ this.logAdDurationDecision(fits, durationMs, adController);
7576
+ return fits;
7577
+ }
7578
+ },
7579
+ {
7580
+ key: "rejectLoadedAdForDuration",
7581
+ value: function rejectLoadedAdForDuration() {
7582
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7583
+ try {
7584
+ adController.destroy();
7585
+ } catch (unused) {}
7586
+ }
7587
+ },
7588
+ {
7589
+ key: "markAdStarted",
7590
+ value: function markAdStarted() {
7591
+ this.currentAdIndex++;
7592
+ this.totalAdsInBreak = this.currentAdIndex;
7593
+ this.totalAdRequestsInBreak++;
7594
+ this.lastAdRequestTime = Date.now();
7595
+ }
7596
+ },
7597
+ {
7598
+ key: "getRemainingAdMs",
7599
+ value: function getRemainingAdMs() {
7600
+ var wallClockRemaining = this.getWallClockRemainingAdMs();
7601
+ var budgetRemaining = this.getDurationBudgetRemainingMs();
7602
+ if (this.expectedAdBreakDurationMs == null) {
7603
+ return wallClockRemaining;
7604
+ }
7605
+ return Math.min(wallClockRemaining, budgetRemaining);
7606
+ }
7607
+ },
7393
7608
  {
7394
7609
  key: "getMinRemainingMsToStartAd",
7395
7610
  value: function getMinRemainingMsToStartAd() {
@@ -7403,7 +7618,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7403
7618
  {
7404
7619
  key: "hasTimeToStartAnotherAd",
7405
7620
  value: function hasTimeToStartAnotherAd() {
7406
- return this.getRemainingAdMs() > this.getMinRemainingMsToStartAd();
7621
+ var minRemaining = this.getMinRemainingMsToStartAd();
7622
+ return this.getWallClockRemainingAdMs() > minRemaining && this.getDurationBudgetRemainingMs() > minRemaining;
7407
7623
  }
7408
7624
  },
7409
7625
  {