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.
@@ -1328,6 +1328,12 @@ function createHlsAdPlayer(contentVideo, options) {
1328
1328
  contentVideo.style.visibility = "visible";
1329
1329
  contentVideo.style.opacity = "1";
1330
1330
  }
1331
+ },
1332
+ getLoadedAdDuration: function getLoadedAdDuration() {
1333
+ return currentAd === null || currentAd === void 0 ? void 0 : currentAd.duration;
1334
+ },
1335
+ getLoadedAdId: function getLoadedAdId() {
1336
+ return currentAd === null || currentAd === void 0 ? void 0 : currentAd.id;
1331
1337
  }
1332
1338
  };
1333
1339
  }
@@ -2740,6 +2746,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2740
2746
  this.preloadPoolLoopRunning = false;
2741
2747
  this.adDetectSentForCurrentBreak = false;
2742
2748
  this.adBreakEpoch = 0;
2749
+ this.adBreakPlayedDurationMs = 0;
2743
2750
  this.palPlaybackStarted = false;
2744
2751
  this.streamCorrelator = generateCorrelator();
2745
2752
  this.consentSignals = {};
@@ -3236,6 +3243,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3236
3243
  adIndex: _this.currentAdIndex,
3237
3244
  timestamp: /* @__PURE__ */ new Date().toISOString()
3238
3245
  }).catch(function() {});
3246
+ _this.recordPlayedAdDuration();
3239
3247
  var remaining = _this.getRemainingAdMs();
3240
3248
  _this.consecutiveFailures = 0;
3241
3249
  if (_this.config.debugAdTiming) {
@@ -3311,6 +3319,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3311
3319
  return;
3312
3320
  }
3313
3321
  _this.consecutiveFailures = 0;
3322
+ _this.recordPlayedAdDuration();
3314
3323
  var remaining = _this.getRemainingAdMs();
3315
3324
  if (_this.config.debugAdTiming) {
3316
3325
  console.log("[CONTINUOUS-FETCH] content_resume event: remaining=".concat(remaining, "ms, queued ads=").concat(_this.adRequestQueue.length), _this.adRequestQueue.length > 0 ? {
@@ -5204,7 +5213,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5204
5213
  }
5205
5214
  this.beginNewAdPod();
5206
5215
  this.podAssignedByPrefetch = true;
5207
- var urlsToPregenerate = 5;
5216
+ var urlsToPregenerate = 1;
5208
5217
  var generatedUrls = this.generateVastUrlsWithCorrelators(baseVastUrl, urlsToPregenerate);
5209
5218
  this.pendingAdBreak = _object_spread_props(_object_spread({
5210
5219
  marker: marker
@@ -5229,7 +5238,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5229
5238
  console.log("[PREFETCH] \uD83D\uDD04 Starting ad prefetch for upcoming ad break");
5230
5239
  console.log("[PREFETCH] \uD83D\uDCCB Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
5231
5240
  }
5232
- this.startPreloadPool(baseVastUrl, generatedUrls.slice(0, this.maxPreloadPoolSize));
5241
+ this.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
5233
5242
  }
5234
5243
  },
5235
5244
  {
@@ -5247,6 +5256,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5247
5256
  {
5248
5257
  key: "startPreloadPool",
5249
5258
  value: function startPreloadPool(baseVastUrl, initialUrls) {
5259
+ var startLoopImmediately = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : true;
5250
5260
  if (this.preloadPoolActive) {
5251
5261
  if (this.config.debugAdTiming) {
5252
5262
  console.log("[PRELOAD-POOL] Already active, skipping duplicate start");
@@ -5280,7 +5290,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5280
5290
  }
5281
5291
  }
5282
5292
  }
5283
- this.preloadPoolLoop(baseVastUrl);
5293
+ if (startLoopImmediately) {
5294
+ this.preloadPoolLoop(baseVastUrl);
5295
+ } else if (this.config.debugAdTiming) {
5296
+ console.log("[PRELOAD-POOL] Pre-break mode: preloaded once, waiting for SCTE start to continue fetching");
5297
+ }
5284
5298
  }
5285
5299
  },
5286
5300
  {
@@ -5316,7 +5330,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5316
5330
  epoch = this.adBreakEpoch;
5317
5331
  loadPromise = function() {
5318
5332
  return _async_to_generator(function() {
5319
- var _this, hasAdError, adErrorPayload, errorListenerCleanup, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, lateErrorListener, error;
5333
+ var _this, hasAdError, adErrorPayload, errorListenerCleanup, _preloadAd_getLoadedAdDuration, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, loadedDuration, lateErrorListener, _poolEntry_durationSeconds, error;
5320
5334
  return _ts_generator(this, function(_state) {
5321
5335
  switch(_state.label){
5322
5336
  case 0:
@@ -5412,6 +5426,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5412
5426
  isReady: true,
5413
5427
  loadPromise: Promise.resolve()
5414
5428
  };
5429
+ loadedDuration = (_preloadAd_getLoadedAdDuration = preloadAd.getLoadedAdDuration) === null || _preloadAd_getLoadedAdDuration === void 0 ? void 0 : _preloadAd_getLoadedAdDuration.call(preloadAd);
5430
+ if (loadedDuration != null && loadedDuration > 0) {
5431
+ poolEntry.durationSeconds = loadedDuration;
5432
+ }
5415
5433
  lateErrorListener = function lateErrorListener(payload) {
5416
5434
  var index = _this.preloadPool.findIndex(function(entry) {
5417
5435
  return entry.vastUrl === vastUrl;
@@ -5429,7 +5447,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5429
5447
  preloadAd.on("ad_error", lateErrorListener);
5430
5448
  this.preloadPool.push(poolEntry);
5431
5449
  if (this.config.debugAdTiming) {
5432
- console.log("[PRELOAD-POOL] ✅ Ad preloaded (no errors detected within timeout) (pool size: ".concat(this.preloadPool.length, "/").concat(this.maxPreloadPoolSize, ")"));
5450
+ ;
5451
+ 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)"));
5433
5452
  }
5434
5453
  if (errorListenerCleanup) {
5435
5454
  errorListenerCleanup();
@@ -5710,8 +5729,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5710
5729
  }
5711
5730
  this.inAdBreak = true;
5712
5731
  this.currentAdBreakStartWallClockMs = Date.now();
5732
+ this.adBreakPlayedDurationMs = 0;
5713
5733
  this.currentAdIndex = 0;
5714
- this.totalAdsInBreak = 1;
5734
+ this.totalAdsInBreak = 0;
5715
5735
  this.adPodQueue = [];
5716
5736
  this.showAds = true;
5717
5737
  this.showPlaceholderLayer();
@@ -5765,14 +5785,30 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5765
5785
  case 1:
5766
5786
  _state.trys.push([
5767
5787
  1,
5768
- 8,
5788
+ 12,
5769
5789
  ,
5770
- 14
5790
+ 20
5771
5791
  ]);
5772
5792
  if (!(usePreloadedAd && preloadedController)) return [
5793
+ 3,
5794
+ 5
5795
+ ];
5796
+ if (!!this.ensureLoadedAdFitsBudget(preloadedController)) return [
5773
5797
  3,
5774
5798
  3
5775
5799
  ];
5800
+ this.rejectLoadedAdForDuration(preloadedController);
5801
+ this.startContinuousFetching(baseVastUrl);
5802
+ return [
5803
+ 4,
5804
+ this.tryNextAvailableAdWithRateLimit()
5805
+ ];
5806
+ case 2:
5807
+ _state.sent();
5808
+ return [
5809
+ 2
5810
+ ];
5811
+ case 3:
5776
5812
  preservedMuted = this.adPlayer.getOriginalMutedState();
5777
5813
  preservedVolume = this.adPlayer.getOriginalVolume();
5778
5814
  this.adPlayer.destroy();
@@ -5785,18 +5821,20 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5785
5821
  }
5786
5822
  this.adPlayer.resume();
5787
5823
  this.consecutiveFailures = 0;
5788
- this.currentAdIndex++;
5789
5824
  this.startContinuousFetching(baseVastUrl);
5790
5825
  if (!this.preloadPoolActive) {
5791
5826
  this.preloadPoolActive = true;
5827
+ }
5828
+ if (!this.preloadPoolLoopRunning) {
5792
5829
  this.preloadPoolLoop(baseVastUrl);
5793
5830
  }
5794
5831
  return [
5795
5832
  4,
5796
5833
  this.adPlayer.play()
5797
5834
  ];
5798
- case 2:
5835
+ case 4:
5799
5836
  _state.sent();
5837
+ this.markAdStarted();
5800
5838
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5801
5839
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5802
5840
  }
@@ -5804,21 +5842,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5804
5842
  this.adPlayer.setAdVolume(adVolume);
5805
5843
  return [
5806
5844
  3,
5807
- 7
5845
+ 11
5808
5846
  ];
5809
- case 3:
5847
+ case 5:
5810
5848
  return [
5811
5849
  4,
5812
5850
  this.enforceGlobalRateLimit()
5813
5851
  ];
5814
- case 4:
5852
+ case 6:
5815
5853
  _state.sent();
5816
5854
  this.lastAdRequestTime = Date.now();
5817
5855
  return [
5818
5856
  4,
5819
5857
  this.adPlayer.requestAds(firstAdUrl)
5820
5858
  ];
5821
- case 5:
5859
+ case 7:
5822
5860
  _state.sent();
5823
5861
  sendAdLoadedTracking(this.config.licenseKey, {
5824
5862
  source: "hls",
@@ -5828,8 +5866,24 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5828
5866
  if (this.config.debugAdTiming) {
5829
5867
  console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
5830
5868
  }
5869
+ if (!!this.ensureLoadedAdFitsBudget()) return [
5870
+ 3,
5871
+ 9
5872
+ ];
5873
+ this.rejectLoadedAdForDuration();
5874
+ this.recreateAdController();
5875
+ this.startContinuousFetching(baseVastUrl);
5876
+ return [
5877
+ 4,
5878
+ this.tryNextAvailableAdWithRateLimit()
5879
+ ];
5880
+ case 8:
5881
+ _state.sent();
5882
+ return [
5883
+ 2
5884
+ ];
5885
+ case 9:
5831
5886
  this.consecutiveFailures = 0;
5832
- this.currentAdIndex++;
5833
5887
  this.startContinuousFetching(baseVastUrl);
5834
5888
  if (!this.preloadPoolActive) {
5835
5889
  this.startPreloadPool(baseVastUrl, []);
@@ -5838,44 +5892,61 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5838
5892
  4,
5839
5893
  this.adPlayer.play()
5840
5894
  ];
5841
- case 6:
5895
+ case 10:
5842
5896
  _state.sent();
5897
+ this.markAdStarted();
5843
5898
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5844
5899
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5845
5900
  }
5846
5901
  adVolume1 = currentMuted ? 0 : currentVolume;
5847
5902
  this.adPlayer.setAdVolume(adVolume1);
5848
- _state.label = 7;
5849
- case 7:
5903
+ _state.label = 11;
5904
+ case 11:
5850
5905
  return [
5851
5906
  3,
5852
- 14
5907
+ 20
5853
5908
  ];
5854
- case 8:
5909
+ case 12:
5855
5910
  error = _state.sent();
5856
5911
  if (this.config.debugAdTiming) {
5857
5912
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F First ad request failed:", error);
5858
5913
  }
5859
5914
  if (!!usePreloadedAd) return [
5860
5915
  3,
5861
- 12
5916
+ 18
5862
5917
  ];
5863
5918
  fallbackPreloaded = this.getPreloadedAd();
5864
5919
  if (!fallbackPreloaded) return [
5865
5920
  3,
5866
- 12
5921
+ 18
5867
5922
  ];
5868
5923
  if (this.config.debugAdTiming) {
5869
5924
  console.log("[CONTINUOUS-FETCH] \uD83D\uDD04 First ad failed, using preloaded fallback");
5870
5925
  }
5871
- _state.label = 9;
5872
- case 9:
5926
+ _state.label = 13;
5927
+ case 13:
5873
5928
  _state.trys.push([
5874
- 9,
5875
- 11,
5929
+ 13,
5930
+ 17,
5876
5931
  ,
5877
- 12
5932
+ 18
5878
5933
  ]);
5934
+ if (!!this.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
5935
+ 3,
5936
+ 15
5937
+ ];
5938
+ this.rejectLoadedAdForDuration(fallbackPreloaded.adController);
5939
+ this.startContinuousFetching(baseVastUrl);
5940
+ return [
5941
+ 4,
5942
+ this.tryNextAvailableAdWithRateLimit()
5943
+ ];
5944
+ case 14:
5945
+ _state.sent();
5946
+ return [
5947
+ 2
5948
+ ];
5949
+ case 15:
5879
5950
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
5880
5951
  preservedVolume1 = this.adPlayer.getOriginalVolume();
5881
5952
  this.adPlayer.destroy();
@@ -5885,7 +5956,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5885
5956
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
5886
5957
  this.adPlayer.resume();
5887
5958
  this.consecutiveFailures = 0;
5888
- this.currentAdIndex++;
5889
5959
  this.startContinuousFetching(baseVastUrl);
5890
5960
  if (!this.preloadPoolActive) {
5891
5961
  this.preloadPoolActive = true;
@@ -5895,8 +5965,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5895
5965
  4,
5896
5966
  this.adPlayer.play()
5897
5967
  ];
5898
- case 10:
5968
+ case 16:
5899
5969
  _state.sent();
5970
+ this.markAdStarted();
5900
5971
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5901
5972
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5902
5973
  }
@@ -5905,16 +5976,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5905
5976
  return [
5906
5977
  2
5907
5978
  ];
5908
- case 11:
5979
+ case 17:
5909
5980
  fallbackError = _state.sent();
5910
5981
  if (this.config.debugAdTiming) {
5911
5982
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded fallback also failed:", fallbackError);
5912
5983
  }
5913
5984
  return [
5914
5985
  3,
5915
- 12
5986
+ 18
5916
5987
  ];
5917
- case 12:
5988
+ case 18:
5918
5989
  if (this.isTemporaryAdError(error)) {
5919
5990
  this.temporaryFailureUrls.set(firstAdUrl, Date.now());
5920
5991
  if (this.config.debugAdTiming) {
@@ -5932,13 +6003,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5932
6003
  4,
5933
6004
  this.tryNextAvailableAdWithRateLimit()
5934
6005
  ];
5935
- case 13:
6006
+ case 19:
5936
6007
  _state.sent();
5937
6008
  return [
5938
6009
  3,
5939
- 14
6010
+ 20
5940
6011
  ];
5941
- case 14:
6012
+ case 20:
5942
6013
  return [
5943
6014
  2
5944
6015
  ];
@@ -5979,10 +6050,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5979
6050
  return _ts_generator(this, function(_state) {
5980
6051
  switch(_state.label){
5981
6052
  case 0:
5982
- remaining = _this.getRemainingAdMs();
6053
+ remaining = Math.min(_this.getWallClockRemainingAdMs(), _this.getDurationBudgetRemainingMs());
5983
6054
  if (remaining <= _this.getMinRemainingMsToStartAd()) {
5984
6055
  if (_this.config.debugAdTiming) {
5985
- console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (".concat(remaining, "ms), stopping URL generation"));
6056
+ console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (wall=".concat(_this.getWallClockRemainingAdMs(), "ms, budget=").concat(_this.getDurationBudgetRemainingMs(), "ms), stopping URL generation"));
5986
6057
  }
5987
6058
  return [
5988
6059
  2,
@@ -6064,7 +6135,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6064
6135
  });
6065
6136
  }
6066
6137
  _this.adRequestQueue.push(newAdUrl);
6067
- _this.totalAdsInBreak++;
6068
6138
  generationDelay = _this.consecutiveFailures > 0 ? Math.min(1e3 * Math.pow(2, _this.consecutiveFailures), 5e3) : 500;
6069
6139
  return [
6070
6140
  4,
@@ -6233,7 +6303,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6233
6303
  preloaded = this.getPreloadedAd();
6234
6304
  if (!preloaded) return [
6235
6305
  3,
6236
- 4
6306
+ 6
6237
6307
  ];
6238
6308
  if (this.config.debugAdTiming) {
6239
6309
  console.log("[CONTINUOUS-FETCH] \uD83C\uDFAF Using preloaded ad from pool (".concat(this.preloadPool.length, " remaining in pool)"));
@@ -6247,10 +6317,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6247
6317
  case 1:
6248
6318
  _state.trys.push([
6249
6319
  1,
6250
- 3,
6320
+ 5,
6251
6321
  ,
6252
- 4
6322
+ 6
6253
6323
  ]);
6324
+ if (!!this.ensureLoadedAdFitsBudget(preloaded.adController)) return [
6325
+ 3,
6326
+ 3
6327
+ ];
6328
+ this.rejectLoadedAdForDuration(preloaded.adController);
6329
+ return [
6330
+ 4,
6331
+ this.tryNextAvailableAdWithRateLimit()
6332
+ ];
6333
+ case 2:
6334
+ _state.sent();
6335
+ return [
6336
+ 2
6337
+ ];
6338
+ case 3:
6254
6339
  preservedMuted = this.adPlayer.getOriginalMutedState();
6255
6340
  preservedVolume = this.adPlayer.getOriginalVolume();
6256
6341
  this.adPlayer.destroy();
@@ -6259,15 +6344,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6259
6344
  this.attachImaEventListeners();
6260
6345
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
6261
6346
  this.adPlayer.resume();
6262
- this.currentAdIndex++;
6263
- this.totalAdRequestsInBreak++;
6264
- this.lastAdRequestTime = Date.now();
6265
6347
  return [
6266
6348
  4,
6267
6349
  this.adPlayer.play()
6268
6350
  ];
6269
- case 2:
6351
+ case 4:
6270
6352
  _state.sent();
6353
+ this.markAdStarted();
6271
6354
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6272
6355
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
6273
6356
  }
@@ -6276,25 +6359,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6276
6359
  return [
6277
6360
  2
6278
6361
  ];
6279
- case 3:
6362
+ case 5:
6280
6363
  error = _state.sent();
6281
6364
  if (this.config.debugAdTiming) {
6282
6365
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded ad failed to play:", error);
6283
6366
  }
6284
6367
  return [
6285
6368
  3,
6286
- 4
6369
+ 6
6287
6370
  ];
6288
- case 4:
6371
+ case 6:
6289
6372
  if (!(this.adRequestQueue.length > 0)) return [
6290
6373
  3,
6291
- 10
6374
+ 14
6292
6375
  ];
6293
6376
  nextAdUrl = this.adRequestQueue.shift();
6294
6377
  if (!nextAdUrl) {
6295
6378
  return [
6296
6379
  3,
6297
- 4
6380
+ 6
6298
6381
  ];
6299
6382
  }
6300
6383
  if (this.failedVastUrls.has(nextAdUrl)) {
@@ -6303,7 +6386,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6303
6386
  }
6304
6387
  return [
6305
6388
  3,
6306
- 4
6389
+ 6
6307
6390
  ];
6308
6391
  }
6309
6392
  if (this.isUrlInCooldown(nextAdUrl)) {
@@ -6312,40 +6395,51 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6312
6395
  }
6313
6396
  return [
6314
6397
  3,
6315
- 4
6398
+ 6
6316
6399
  ];
6317
6400
  }
6318
6401
  if (this.config.debugAdTiming) {
6319
- 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)"));
6402
+ console.log("[CONTINUOUS-FETCH] \uD83C\uDFAC Requesting next ad via HLS ad player (".concat(this.currentAdIndex + 1, ", ").concat(this.adRequestQueue.length, " remaining in queue)"));
6320
6403
  }
6321
- this.currentAdIndex++;
6322
- this.totalAdRequestsInBreak++;
6323
- this.lastAdRequestTime = Date.now();
6324
- _state.label = 5;
6325
- case 5:
6404
+ _state.label = 7;
6405
+ case 7:
6326
6406
  _state.trys.push([
6327
- 5,
6328
6407
  7,
6408
+ 9,
6329
6409
  ,
6330
- 9
6410
+ 13
6331
6411
  ]);
6332
6412
  return [
6333
6413
  4,
6334
6414
  this.playSingleAd(nextAdUrl)
6335
6415
  ];
6336
- case 6:
6416
+ case 8:
6337
6417
  _state.sent();
6338
6418
  this.consecutiveFailures = 0;
6339
6419
  return [
6340
6420
  3,
6341
- 9
6421
+ 13
6342
6422
  ];
6343
- case 7:
6423
+ case 9:
6344
6424
  error1 = _state.sent();
6345
6425
  errorMessage = error1.message;
6346
6426
  if (this.config.debugAdTiming) {
6347
6427
  console.log("[CONTINUOUS-FETCH] \u274C Ad request failed:", errorMessage);
6348
6428
  }
6429
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6430
+ 3,
6431
+ 11
6432
+ ];
6433
+ return [
6434
+ 4,
6435
+ this.tryNextAvailableAdWithRateLimit()
6436
+ ];
6437
+ case 10:
6438
+ _state.sent();
6439
+ return [
6440
+ 2
6441
+ ];
6442
+ case 11:
6349
6443
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6350
6444
  this.consecutiveFailures++;
6351
6445
  }
@@ -6353,21 +6447,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6353
6447
  4,
6354
6448
  this.tryNextAvailableAdWithRateLimit()
6355
6449
  ];
6356
- case 8:
6450
+ case 12:
6357
6451
  _state.sent();
6358
6452
  return [
6359
6453
  3,
6360
- 9
6454
+ 13
6361
6455
  ];
6362
- case 9:
6456
+ case 13:
6363
6457
  return [
6364
6458
  2
6365
6459
  ];
6366
- case 10:
6460
+ case 14:
6367
6461
  maxRetries = 3;
6368
6462
  if (!(this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3)) return [
6369
6463
  3,
6370
- 13
6464
+ 17
6371
6465
  ];
6372
6466
  if (this.config.debugAdTiming) {
6373
6467
  console.log("[CONTINUOUS-FETCH] ⏳ Queue empty, waiting for URLs... (".concat(retryCount + 1, "/").concat(maxRetries, ")"));
@@ -6378,18 +6472,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6378
6472
  return setTimeout(resolve, 500);
6379
6473
  })
6380
6474
  ];
6381
- case 11:
6475
+ case 15:
6382
6476
  _state.sent();
6383
6477
  return [
6384
6478
  4,
6385
6479
  this.tryNextAvailableAd(retryCount + 1)
6386
6480
  ];
6387
- case 12:
6481
+ case 16:
6388
6482
  _state.sent();
6389
6483
  return [
6390
6484
  2
6391
6485
  ];
6392
- case 13:
6486
+ case 17:
6393
6487
  if (!this.isShowingPlaceholder && remaining > 1e3) {
6394
6488
  this.showPlaceholderAndWaitForAds();
6395
6489
  } else {
@@ -6452,7 +6546,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6452
6546
  case 1:
6453
6547
  if (!(i < maxChecks)) return [
6454
6548
  3,
6455
- 10
6549
+ 12
6456
6550
  ];
6457
6551
  return [
6458
6552
  4,
@@ -6473,7 +6567,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6473
6567
  }
6474
6568
  return [
6475
6569
  3,
6476
- 10
6570
+ 12
6477
6571
  ];
6478
6572
  }
6479
6573
  if (!this.hasTimeToStartAnotherAd()) {
@@ -6482,19 +6576,19 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6482
6576
  }
6483
6577
  return [
6484
6578
  3,
6485
- 10
6579
+ 12
6486
6580
  ];
6487
6581
  }
6488
6582
  _state.label = 3;
6489
6583
  case 3:
6490
6584
  if (!(this.adRequestQueue.length > 0)) return [
6491
6585
  3,
6492
- 9
6586
+ 11
6493
6587
  ];
6494
6588
  if (!this.hasTimeToStartAnotherAd()) {
6495
6589
  return [
6496
6590
  3,
6497
- 9
6591
+ 11
6498
6592
  ];
6499
6593
  }
6500
6594
  nextAdUrl = this.adRequestQueue.shift();
@@ -6521,16 +6615,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6521
6615
  }
6522
6616
  this.isShowingPlaceholder = false;
6523
6617
  this.adPlayer.hidePlaceholder();
6524
- this.currentAdIndex++;
6525
- this.totalAdRequestsInBreak++;
6526
- this.lastAdRequestTime = Date.now();
6527
6618
  _state.label = 4;
6528
6619
  case 4:
6529
6620
  _state.trys.push([
6530
6621
  4,
6531
6622
  6,
6532
6623
  ,
6533
- 8
6624
+ 10
6534
6625
  ]);
6535
6626
  return [
6536
6627
  4,
@@ -6541,11 +6632,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6541
6632
  this.consecutiveFailures = 0;
6542
6633
  return [
6543
6634
  3,
6544
- 8
6635
+ 10
6545
6636
  ];
6546
6637
  case 6:
6547
6638
  error = _state.sent();
6548
6639
  errorMessage = error.message;
6640
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6641
+ 3,
6642
+ 8
6643
+ ];
6644
+ return [
6645
+ 4,
6646
+ this.tryNextAvailableAdWithRateLimit()
6647
+ ];
6648
+ case 7:
6649
+ _state.sent();
6650
+ return [
6651
+ 2
6652
+ ];
6653
+ case 8:
6549
6654
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6550
6655
  this.consecutiveFailures++;
6551
6656
  }
@@ -6553,23 +6658,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6553
6658
  4,
6554
6659
  this.tryNextAvailableAdWithRateLimit()
6555
6660
  ];
6556
- case 7:
6661
+ case 9:
6557
6662
  _state.sent();
6558
6663
  return [
6559
6664
  3,
6560
- 8
6665
+ 10
6561
6666
  ];
6562
- case 8:
6667
+ case 10:
6563
6668
  return [
6564
6669
  2
6565
6670
  ];
6566
- case 9:
6671
+ case 11:
6567
6672
  i++;
6568
6673
  return [
6569
6674
  3,
6570
6675
  1
6571
6676
  ];
6572
- case 10:
6677
+ case 12:
6573
6678
  if (this.config.debugAdTiming) {
6574
6679
  console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout, ending ad break");
6575
6680
  }
@@ -7041,6 +7146,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7041
7146
  2
7042
7147
  ];
7043
7148
  }
7149
+ if (!this.ensureLoadedAdFitsBudget()) {
7150
+ this.rejectLoadedAdForDuration();
7151
+ this.recreateAdController();
7152
+ throw new Error("ad_duration_exceeds_budget");
7153
+ }
7044
7154
  _state.label = 4;
7045
7155
  case 4:
7046
7156
  _state.trys.push([
@@ -7056,6 +7166,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7056
7166
  ];
7057
7167
  case 5:
7058
7168
  _state.sent();
7169
+ this.markAdStarted();
7059
7170
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7060
7171
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7061
7172
  }
@@ -7087,6 +7198,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7087
7198
  ,
7088
7199
  10
7089
7200
  ]);
7201
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback.adController)) {
7202
+ this.rejectLoadedAdForDuration(preloadedFallback.adController);
7203
+ throw playError;
7204
+ }
7090
7205
  this.clearAdFailsafeTimer();
7091
7206
  preservedMuted = this.adPlayer.getOriginalMutedState();
7092
7207
  preservedVolume = this.adPlayer.getOriginalVolume();
@@ -7097,14 +7212,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7097
7212
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
7098
7213
  this.adPlayer.resume();
7099
7214
  this.consecutiveFailures = 0;
7100
- this.currentAdIndex++;
7101
- this.totalAdRequestsInBreak++;
7102
7215
  return [
7103
7216
  4,
7104
7217
  this.adPlayer.play()
7105
7218
  ];
7106
7219
  case 8:
7107
7220
  _state.sent();
7221
+ this.markAdStarted();
7108
7222
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7109
7223
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7110
7224
  }
@@ -7165,6 +7279,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7165
7279
  ,
7166
7280
  16
7167
7281
  ]);
7282
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback1.adController)) {
7283
+ this.rejectLoadedAdForDuration(preloadedFallback1.adController);
7284
+ throw error;
7285
+ }
7168
7286
  this.clearAdRequestWatchdog();
7169
7287
  this.clearAdFailsafeTimer();
7170
7288
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
@@ -7176,14 +7294,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7176
7294
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
7177
7295
  this.adPlayer.resume();
7178
7296
  this.consecutiveFailures = 0;
7179
- this.currentAdIndex++;
7180
- this.totalAdRequestsInBreak++;
7181
7297
  return [
7182
7298
  4,
7183
7299
  this.adPlayer.play()
7184
7300
  ];
7185
7301
  case 14:
7186
7302
  _state.sent();
7303
+ this.markAdStarted();
7187
7304
  this.adPlayer.setAdVolume(this.getAdAudioVolume());
7188
7305
  return [
7189
7306
  2
@@ -7244,6 +7361,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7244
7361
  this.adRequestQueue = [];
7245
7362
  this.inAdBreak = false;
7246
7363
  this.adDetectSentForCurrentBreak = false;
7364
+ this.adBreakPlayedDurationMs = 0;
7247
7365
  this.activeScte35BreakKey = void 0;
7248
7366
  this.scheduledScte35BreakKey = void 0;
7249
7367
  this.expectedAdBreakDurationMs = void 0;
@@ -7433,13 +7551,117 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7433
7551
  }
7434
7552
  },
7435
7553
  {
7436
- key: "getRemainingAdMs",
7437
- value: function getRemainingAdMs() {
7438
- if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) return 0;
7554
+ key: "getWallClockRemainingAdMs",
7555
+ value: function getWallClockRemainingAdMs() {
7556
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
7557
+ return 0;
7558
+ }
7439
7559
  var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
7440
7560
  return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
7441
7561
  }
7442
7562
  },
7563
+ {
7564
+ key: "getDurationBudgetRemainingMs",
7565
+ value: function getDurationBudgetRemainingMs() {
7566
+ if (this.expectedAdBreakDurationMs == null) {
7567
+ return 0;
7568
+ }
7569
+ return Math.max(0, this.expectedAdBreakDurationMs - this.adBreakPlayedDurationMs);
7570
+ }
7571
+ },
7572
+ {
7573
+ key: "getLoadedAdDurationMs",
7574
+ value: function getLoadedAdDurationMs() {
7575
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7576
+ var _adController_getLoadedAdDuration;
7577
+ var durationSec = (_adController_getLoadedAdDuration = adController.getLoadedAdDuration) === null || _adController_getLoadedAdDuration === void 0 ? void 0 : _adController_getLoadedAdDuration.call(adController);
7578
+ if (durationSec == null || durationSec <= 0) {
7579
+ return 0;
7580
+ }
7581
+ return Math.round(durationSec * 1e3);
7582
+ }
7583
+ },
7584
+ {
7585
+ key: "adFitsRemainingBudget",
7586
+ value: function adFitsRemainingBudget(durationMs) {
7587
+ var adController = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : this.adPlayer;
7588
+ if (durationMs <= 0) {
7589
+ return true;
7590
+ }
7591
+ return durationMs <= this.getDurationBudgetRemainingMs();
7592
+ }
7593
+ },
7594
+ {
7595
+ key: "logAdDurationDecision",
7596
+ value: function logAdDurationDecision(accepted, durationMs) {
7597
+ var adController = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : this.adPlayer;
7598
+ var _ref;
7599
+ var _adController_getLoadedAdId;
7600
+ if (!this.config.debugAdTiming) {
7601
+ return;
7602
+ }
7603
+ var remainingBudget = this.getDurationBudgetRemainingMs();
7604
+ var adId = (_ref = (_adController_getLoadedAdId = adController.getLoadedAdId) === null || _adController_getLoadedAdId === void 0 ? void 0 : _adController_getLoadedAdId.call(adController)) !== null && _ref !== void 0 ? _ref : "unknown";
7605
+ if (accepted) {
7606
+ console.log("[AD-DURATION] Accepted ad id=".concat(adId, ", duration=").concat(durationMs, "ms, budget remaining after play=").concat(Math.max(0, remainingBudget - durationMs), "ms"));
7607
+ } else {
7608
+ console.log("[AD-DURATION] Rejected ad id=".concat(adId, ", duration=").concat(durationMs, "ms exceeds remaining budget=").concat(remainingBudget, "ms — requesting another ad"));
7609
+ }
7610
+ }
7611
+ },
7612
+ {
7613
+ key: "recordPlayedAdDuration",
7614
+ value: function recordPlayedAdDuration() {
7615
+ var durationMs = this.getLoadedAdDurationMs();
7616
+ if (durationMs <= 0) {
7617
+ return;
7618
+ }
7619
+ this.adBreakPlayedDurationMs += durationMs;
7620
+ if (this.config.debugAdTiming) {
7621
+ var _this_expectedAdBreakDurationMs;
7622
+ 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"));
7623
+ }
7624
+ }
7625
+ },
7626
+ {
7627
+ key: "ensureLoadedAdFitsBudget",
7628
+ value: function ensureLoadedAdFitsBudget() {
7629
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7630
+ var durationMs = this.getLoadedAdDurationMs(adController);
7631
+ var fits = this.adFitsRemainingBudget(durationMs, adController);
7632
+ this.logAdDurationDecision(fits, durationMs, adController);
7633
+ return fits;
7634
+ }
7635
+ },
7636
+ {
7637
+ key: "rejectLoadedAdForDuration",
7638
+ value: function rejectLoadedAdForDuration() {
7639
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7640
+ try {
7641
+ adController.destroy();
7642
+ } catch (unused) {}
7643
+ }
7644
+ },
7645
+ {
7646
+ key: "markAdStarted",
7647
+ value: function markAdStarted() {
7648
+ this.currentAdIndex++;
7649
+ this.totalAdsInBreak = this.currentAdIndex;
7650
+ this.totalAdRequestsInBreak++;
7651
+ this.lastAdRequestTime = Date.now();
7652
+ }
7653
+ },
7654
+ {
7655
+ key: "getRemainingAdMs",
7656
+ value: function getRemainingAdMs() {
7657
+ var wallClockRemaining = this.getWallClockRemainingAdMs();
7658
+ var budgetRemaining = this.getDurationBudgetRemainingMs();
7659
+ if (this.expectedAdBreakDurationMs == null) {
7660
+ return wallClockRemaining;
7661
+ }
7662
+ return Math.min(wallClockRemaining, budgetRemaining);
7663
+ }
7664
+ },
7443
7665
  {
7444
7666
  key: "getMinRemainingMsToStartAd",
7445
7667
  value: function getMinRemainingMsToStartAd() {
@@ -7453,7 +7675,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7453
7675
  {
7454
7676
  key: "hasTimeToStartAnotherAd",
7455
7677
  value: function hasTimeToStartAnotherAd() {
7456
- return this.getRemainingAdMs() > this.getMinRemainingMsToStartAd();
7678
+ var minRemaining = this.getMinRemainingMsToStartAd();
7679
+ return this.getWallClockRemainingAdMs() > minRemaining && this.getDurationBudgetRemainingMs() > minRemaining;
7457
7680
  }
7458
7681
  },
7459
7682
  {