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.
package/lib/index.js CHANGED
@@ -1262,6 +1262,12 @@ function createHlsAdPlayer(contentVideo, options) {
1262
1262
  contentVideo.style.visibility = "visible";
1263
1263
  contentVideo.style.opacity = "1";
1264
1264
  }
1265
+ },
1266
+ getLoadedAdDuration: function getLoadedAdDuration() {
1267
+ return currentAd === null || currentAd === void 0 ? void 0 : currentAd.duration;
1268
+ },
1269
+ getLoadedAdId: function getLoadedAdId() {
1270
+ return currentAd === null || currentAd === void 0 ? void 0 : currentAd.id;
1265
1271
  }
1266
1272
  };
1267
1273
  }
@@ -2724,6 +2730,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2724
2730
  this.preloadPoolLoopRunning = false;
2725
2731
  this.adDetectSentForCurrentBreak = false;
2726
2732
  this.adBreakEpoch = 0;
2733
+ this.adBreakPlayedDurationMs = 0;
2727
2734
  this.palPlaybackStarted = false;
2728
2735
  this.streamCorrelator = generateCorrelator();
2729
2736
  this.consentSignals = {};
@@ -3220,6 +3227,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3220
3227
  adIndex: _this.currentAdIndex,
3221
3228
  timestamp: /* @__PURE__ */ new Date().toISOString()
3222
3229
  }).catch(function() {});
3230
+ _this.recordPlayedAdDuration();
3223
3231
  var remaining = _this.getRemainingAdMs();
3224
3232
  _this.consecutiveFailures = 0;
3225
3233
  if (_this.config.debugAdTiming) {
@@ -3295,6 +3303,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3295
3303
  return;
3296
3304
  }
3297
3305
  _this.consecutiveFailures = 0;
3306
+ _this.recordPlayedAdDuration();
3298
3307
  var remaining = _this.getRemainingAdMs();
3299
3308
  if (_this.config.debugAdTiming) {
3300
3309
  console.log("[CONTINUOUS-FETCH] content_resume event: remaining=".concat(remaining, "ms, queued ads=").concat(_this.adRequestQueue.length), _this.adRequestQueue.length > 0 ? {
@@ -5188,7 +5197,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5188
5197
  }
5189
5198
  this.beginNewAdPod();
5190
5199
  this.podAssignedByPrefetch = true;
5191
- var urlsToPregenerate = 5;
5200
+ var urlsToPregenerate = 1;
5192
5201
  var generatedUrls = this.generateVastUrlsWithCorrelators(baseVastUrl, urlsToPregenerate);
5193
5202
  this.pendingAdBreak = _object_spread_props(_object_spread({
5194
5203
  marker: marker
@@ -5213,7 +5222,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5213
5222
  console.log("[PREFETCH] \uD83D\uDD04 Starting ad prefetch for upcoming ad break");
5214
5223
  console.log("[PREFETCH] \uD83D\uDCCB Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
5215
5224
  }
5216
- this.startPreloadPool(baseVastUrl, generatedUrls.slice(0, this.maxPreloadPoolSize));
5225
+ this.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
5217
5226
  }
5218
5227
  },
5219
5228
  {
@@ -5231,6 +5240,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5231
5240
  {
5232
5241
  key: "startPreloadPool",
5233
5242
  value: function startPreloadPool(baseVastUrl, initialUrls) {
5243
+ var startLoopImmediately = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : true;
5234
5244
  if (this.preloadPoolActive) {
5235
5245
  if (this.config.debugAdTiming) {
5236
5246
  console.log("[PRELOAD-POOL] Already active, skipping duplicate start");
@@ -5264,7 +5274,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5264
5274
  }
5265
5275
  }
5266
5276
  }
5267
- this.preloadPoolLoop(baseVastUrl);
5277
+ if (startLoopImmediately) {
5278
+ this.preloadPoolLoop(baseVastUrl);
5279
+ } else if (this.config.debugAdTiming) {
5280
+ console.log("[PRELOAD-POOL] Pre-break mode: preloaded once, waiting for SCTE start to continue fetching");
5281
+ }
5268
5282
  }
5269
5283
  },
5270
5284
  {
@@ -5300,7 +5314,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5300
5314
  epoch = this.adBreakEpoch;
5301
5315
  loadPromise = function() {
5302
5316
  return _async_to_generator(function() {
5303
- var _this, hasAdError, adErrorPayload, errorListenerCleanup, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, lateErrorListener, error;
5317
+ var _this, hasAdError, adErrorPayload, errorListenerCleanup, _preloadAd_getLoadedAdDuration, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, loadedDuration, lateErrorListener, _poolEntry_durationSeconds, error;
5304
5318
  return _ts_generator(this, function(_state) {
5305
5319
  switch(_state.label){
5306
5320
  case 0:
@@ -5396,6 +5410,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5396
5410
  isReady: true,
5397
5411
  loadPromise: Promise.resolve()
5398
5412
  };
5413
+ loadedDuration = (_preloadAd_getLoadedAdDuration = preloadAd.getLoadedAdDuration) === null || _preloadAd_getLoadedAdDuration === void 0 ? void 0 : _preloadAd_getLoadedAdDuration.call(preloadAd);
5414
+ if (loadedDuration != null && loadedDuration > 0) {
5415
+ poolEntry.durationSeconds = loadedDuration;
5416
+ }
5399
5417
  lateErrorListener = function lateErrorListener(payload) {
5400
5418
  var index = _this.preloadPool.findIndex(function(entry) {
5401
5419
  return entry.vastUrl === vastUrl;
@@ -5413,7 +5431,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5413
5431
  preloadAd.on("ad_error", lateErrorListener);
5414
5432
  this.preloadPool.push(poolEntry);
5415
5433
  if (this.config.debugAdTiming) {
5416
- console.log("[PRELOAD-POOL] ✅ Ad preloaded (no errors detected within timeout) (pool size: ".concat(this.preloadPool.length, "/").concat(this.maxPreloadPoolSize, ")"));
5434
+ ;
5435
+ 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)"));
5417
5436
  }
5418
5437
  if (errorListenerCleanup) {
5419
5438
  errorListenerCleanup();
@@ -5694,8 +5713,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5694
5713
  }
5695
5714
  this.inAdBreak = true;
5696
5715
  this.currentAdBreakStartWallClockMs = Date.now();
5716
+ this.adBreakPlayedDurationMs = 0;
5697
5717
  this.currentAdIndex = 0;
5698
- this.totalAdsInBreak = 1;
5718
+ this.totalAdsInBreak = 0;
5699
5719
  this.adPodQueue = [];
5700
5720
  this.showAds = true;
5701
5721
  this.showPlaceholderLayer();
@@ -5749,14 +5769,30 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5749
5769
  case 1:
5750
5770
  _state.trys.push([
5751
5771
  1,
5752
- 8,
5772
+ 12,
5753
5773
  ,
5754
- 14
5774
+ 20
5755
5775
  ]);
5756
5776
  if (!(usePreloadedAd && preloadedController)) return [
5777
+ 3,
5778
+ 5
5779
+ ];
5780
+ if (!!this.ensureLoadedAdFitsBudget(preloadedController)) return [
5757
5781
  3,
5758
5782
  3
5759
5783
  ];
5784
+ this.rejectLoadedAdForDuration(preloadedController);
5785
+ this.startContinuousFetching(baseVastUrl);
5786
+ return [
5787
+ 4,
5788
+ this.tryNextAvailableAdWithRateLimit()
5789
+ ];
5790
+ case 2:
5791
+ _state.sent();
5792
+ return [
5793
+ 2
5794
+ ];
5795
+ case 3:
5760
5796
  preservedMuted = this.adPlayer.getOriginalMutedState();
5761
5797
  preservedVolume = this.adPlayer.getOriginalVolume();
5762
5798
  this.adPlayer.destroy();
@@ -5769,18 +5805,20 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5769
5805
  }
5770
5806
  this.adPlayer.resume();
5771
5807
  this.consecutiveFailures = 0;
5772
- this.currentAdIndex++;
5773
5808
  this.startContinuousFetching(baseVastUrl);
5774
5809
  if (!this.preloadPoolActive) {
5775
5810
  this.preloadPoolActive = true;
5811
+ }
5812
+ if (!this.preloadPoolLoopRunning) {
5776
5813
  this.preloadPoolLoop(baseVastUrl);
5777
5814
  }
5778
5815
  return [
5779
5816
  4,
5780
5817
  this.adPlayer.play()
5781
5818
  ];
5782
- case 2:
5819
+ case 4:
5783
5820
  _state.sent();
5821
+ this.markAdStarted();
5784
5822
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5785
5823
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5786
5824
  }
@@ -5788,21 +5826,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5788
5826
  this.adPlayer.setAdVolume(adVolume);
5789
5827
  return [
5790
5828
  3,
5791
- 7
5829
+ 11
5792
5830
  ];
5793
- case 3:
5831
+ case 5:
5794
5832
  return [
5795
5833
  4,
5796
5834
  this.enforceGlobalRateLimit()
5797
5835
  ];
5798
- case 4:
5836
+ case 6:
5799
5837
  _state.sent();
5800
5838
  this.lastAdRequestTime = Date.now();
5801
5839
  return [
5802
5840
  4,
5803
5841
  this.adPlayer.requestAds(firstAdUrl)
5804
5842
  ];
5805
- case 5:
5843
+ case 7:
5806
5844
  _state.sent();
5807
5845
  sendAdLoadedTracking(this.config.licenseKey, {
5808
5846
  source: "hls",
@@ -5812,8 +5850,24 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5812
5850
  if (this.config.debugAdTiming) {
5813
5851
  console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
5814
5852
  }
5853
+ if (!!this.ensureLoadedAdFitsBudget()) return [
5854
+ 3,
5855
+ 9
5856
+ ];
5857
+ this.rejectLoadedAdForDuration();
5858
+ this.recreateAdController();
5859
+ this.startContinuousFetching(baseVastUrl);
5860
+ return [
5861
+ 4,
5862
+ this.tryNextAvailableAdWithRateLimit()
5863
+ ];
5864
+ case 8:
5865
+ _state.sent();
5866
+ return [
5867
+ 2
5868
+ ];
5869
+ case 9:
5815
5870
  this.consecutiveFailures = 0;
5816
- this.currentAdIndex++;
5817
5871
  this.startContinuousFetching(baseVastUrl);
5818
5872
  if (!this.preloadPoolActive) {
5819
5873
  this.startPreloadPool(baseVastUrl, []);
@@ -5822,44 +5876,61 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5822
5876
  4,
5823
5877
  this.adPlayer.play()
5824
5878
  ];
5825
- case 6:
5879
+ case 10:
5826
5880
  _state.sent();
5881
+ this.markAdStarted();
5827
5882
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5828
5883
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5829
5884
  }
5830
5885
  adVolume1 = currentMuted ? 0 : currentVolume;
5831
5886
  this.adPlayer.setAdVolume(adVolume1);
5832
- _state.label = 7;
5833
- case 7:
5887
+ _state.label = 11;
5888
+ case 11:
5834
5889
  return [
5835
5890
  3,
5836
- 14
5891
+ 20
5837
5892
  ];
5838
- case 8:
5893
+ case 12:
5839
5894
  error = _state.sent();
5840
5895
  if (this.config.debugAdTiming) {
5841
5896
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F First ad request failed:", error);
5842
5897
  }
5843
5898
  if (!!usePreloadedAd) return [
5844
5899
  3,
5845
- 12
5900
+ 18
5846
5901
  ];
5847
5902
  fallbackPreloaded = this.getPreloadedAd();
5848
5903
  if (!fallbackPreloaded) return [
5849
5904
  3,
5850
- 12
5905
+ 18
5851
5906
  ];
5852
5907
  if (this.config.debugAdTiming) {
5853
5908
  console.log("[CONTINUOUS-FETCH] \uD83D\uDD04 First ad failed, using preloaded fallback");
5854
5909
  }
5855
- _state.label = 9;
5856
- case 9:
5910
+ _state.label = 13;
5911
+ case 13:
5857
5912
  _state.trys.push([
5858
- 9,
5859
- 11,
5913
+ 13,
5914
+ 17,
5860
5915
  ,
5861
- 12
5916
+ 18
5862
5917
  ]);
5918
+ if (!!this.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
5919
+ 3,
5920
+ 15
5921
+ ];
5922
+ this.rejectLoadedAdForDuration(fallbackPreloaded.adController);
5923
+ this.startContinuousFetching(baseVastUrl);
5924
+ return [
5925
+ 4,
5926
+ this.tryNextAvailableAdWithRateLimit()
5927
+ ];
5928
+ case 14:
5929
+ _state.sent();
5930
+ return [
5931
+ 2
5932
+ ];
5933
+ case 15:
5863
5934
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
5864
5935
  preservedVolume1 = this.adPlayer.getOriginalVolume();
5865
5936
  this.adPlayer.destroy();
@@ -5869,7 +5940,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5869
5940
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
5870
5941
  this.adPlayer.resume();
5871
5942
  this.consecutiveFailures = 0;
5872
- this.currentAdIndex++;
5873
5943
  this.startContinuousFetching(baseVastUrl);
5874
5944
  if (!this.preloadPoolActive) {
5875
5945
  this.preloadPoolActive = true;
@@ -5879,8 +5949,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5879
5949
  4,
5880
5950
  this.adPlayer.play()
5881
5951
  ];
5882
- case 10:
5952
+ case 16:
5883
5953
  _state.sent();
5954
+ this.markAdStarted();
5884
5955
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5885
5956
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5886
5957
  }
@@ -5889,16 +5960,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5889
5960
  return [
5890
5961
  2
5891
5962
  ];
5892
- case 11:
5963
+ case 17:
5893
5964
  fallbackError = _state.sent();
5894
5965
  if (this.config.debugAdTiming) {
5895
5966
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded fallback also failed:", fallbackError);
5896
5967
  }
5897
5968
  return [
5898
5969
  3,
5899
- 12
5970
+ 18
5900
5971
  ];
5901
- case 12:
5972
+ case 18:
5902
5973
  if (this.isTemporaryAdError(error)) {
5903
5974
  this.temporaryFailureUrls.set(firstAdUrl, Date.now());
5904
5975
  if (this.config.debugAdTiming) {
@@ -5916,13 +5987,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5916
5987
  4,
5917
5988
  this.tryNextAvailableAdWithRateLimit()
5918
5989
  ];
5919
- case 13:
5990
+ case 19:
5920
5991
  _state.sent();
5921
5992
  return [
5922
5993
  3,
5923
- 14
5994
+ 20
5924
5995
  ];
5925
- case 14:
5996
+ case 20:
5926
5997
  return [
5927
5998
  2
5928
5999
  ];
@@ -5963,10 +6034,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5963
6034
  return _ts_generator(this, function(_state) {
5964
6035
  switch(_state.label){
5965
6036
  case 0:
5966
- remaining = _this.getRemainingAdMs();
6037
+ remaining = Math.min(_this.getWallClockRemainingAdMs(), _this.getDurationBudgetRemainingMs());
5967
6038
  if (remaining <= _this.getMinRemainingMsToStartAd()) {
5968
6039
  if (_this.config.debugAdTiming) {
5969
- console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (".concat(remaining, "ms), stopping URL generation"));
6040
+ console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (wall=".concat(_this.getWallClockRemainingAdMs(), "ms, budget=").concat(_this.getDurationBudgetRemainingMs(), "ms), stopping URL generation"));
5970
6041
  }
5971
6042
  return [
5972
6043
  2,
@@ -6048,7 +6119,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6048
6119
  });
6049
6120
  }
6050
6121
  _this.adRequestQueue.push(newAdUrl);
6051
- _this.totalAdsInBreak++;
6052
6122
  generationDelay = _this.consecutiveFailures > 0 ? Math.min(1e3 * Math.pow(2, _this.consecutiveFailures), 5e3) : 500;
6053
6123
  return [
6054
6124
  4,
@@ -6217,7 +6287,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6217
6287
  preloaded = this.getPreloadedAd();
6218
6288
  if (!preloaded) return [
6219
6289
  3,
6220
- 4
6290
+ 6
6221
6291
  ];
6222
6292
  if (this.config.debugAdTiming) {
6223
6293
  console.log("[CONTINUOUS-FETCH] \uD83C\uDFAF Using preloaded ad from pool (".concat(this.preloadPool.length, " remaining in pool)"));
@@ -6231,10 +6301,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6231
6301
  case 1:
6232
6302
  _state.trys.push([
6233
6303
  1,
6234
- 3,
6304
+ 5,
6235
6305
  ,
6236
- 4
6306
+ 6
6237
6307
  ]);
6308
+ if (!!this.ensureLoadedAdFitsBudget(preloaded.adController)) return [
6309
+ 3,
6310
+ 3
6311
+ ];
6312
+ this.rejectLoadedAdForDuration(preloaded.adController);
6313
+ return [
6314
+ 4,
6315
+ this.tryNextAvailableAdWithRateLimit()
6316
+ ];
6317
+ case 2:
6318
+ _state.sent();
6319
+ return [
6320
+ 2
6321
+ ];
6322
+ case 3:
6238
6323
  preservedMuted = this.adPlayer.getOriginalMutedState();
6239
6324
  preservedVolume = this.adPlayer.getOriginalVolume();
6240
6325
  this.adPlayer.destroy();
@@ -6243,15 +6328,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6243
6328
  this.attachImaEventListeners();
6244
6329
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
6245
6330
  this.adPlayer.resume();
6246
- this.currentAdIndex++;
6247
- this.totalAdRequestsInBreak++;
6248
- this.lastAdRequestTime = Date.now();
6249
6331
  return [
6250
6332
  4,
6251
6333
  this.adPlayer.play()
6252
6334
  ];
6253
- case 2:
6335
+ case 4:
6254
6336
  _state.sent();
6337
+ this.markAdStarted();
6255
6338
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6256
6339
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
6257
6340
  }
@@ -6260,25 +6343,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6260
6343
  return [
6261
6344
  2
6262
6345
  ];
6263
- case 3:
6346
+ case 5:
6264
6347
  error = _state.sent();
6265
6348
  if (this.config.debugAdTiming) {
6266
6349
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded ad failed to play:", error);
6267
6350
  }
6268
6351
  return [
6269
6352
  3,
6270
- 4
6353
+ 6
6271
6354
  ];
6272
- case 4:
6355
+ case 6:
6273
6356
  if (!(this.adRequestQueue.length > 0)) return [
6274
6357
  3,
6275
- 10
6358
+ 14
6276
6359
  ];
6277
6360
  nextAdUrl = this.adRequestQueue.shift();
6278
6361
  if (!nextAdUrl) {
6279
6362
  return [
6280
6363
  3,
6281
- 4
6364
+ 6
6282
6365
  ];
6283
6366
  }
6284
6367
  if (this.failedVastUrls.has(nextAdUrl)) {
@@ -6287,7 +6370,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6287
6370
  }
6288
6371
  return [
6289
6372
  3,
6290
- 4
6373
+ 6
6291
6374
  ];
6292
6375
  }
6293
6376
  if (this.isUrlInCooldown(nextAdUrl)) {
@@ -6296,40 +6379,51 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6296
6379
  }
6297
6380
  return [
6298
6381
  3,
6299
- 4
6382
+ 6
6300
6383
  ];
6301
6384
  }
6302
6385
  if (this.config.debugAdTiming) {
6303
- 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)"));
6386
+ console.log("[CONTINUOUS-FETCH] \uD83C\uDFAC Requesting next ad via HLS ad player (".concat(this.currentAdIndex + 1, ", ").concat(this.adRequestQueue.length, " remaining in queue)"));
6304
6387
  }
6305
- this.currentAdIndex++;
6306
- this.totalAdRequestsInBreak++;
6307
- this.lastAdRequestTime = Date.now();
6308
- _state.label = 5;
6309
- case 5:
6388
+ _state.label = 7;
6389
+ case 7:
6310
6390
  _state.trys.push([
6311
- 5,
6312
6391
  7,
6392
+ 9,
6313
6393
  ,
6314
- 9
6394
+ 13
6315
6395
  ]);
6316
6396
  return [
6317
6397
  4,
6318
6398
  this.playSingleAd(nextAdUrl)
6319
6399
  ];
6320
- case 6:
6400
+ case 8:
6321
6401
  _state.sent();
6322
6402
  this.consecutiveFailures = 0;
6323
6403
  return [
6324
6404
  3,
6325
- 9
6405
+ 13
6326
6406
  ];
6327
- case 7:
6407
+ case 9:
6328
6408
  error1 = _state.sent();
6329
6409
  errorMessage = error1.message;
6330
6410
  if (this.config.debugAdTiming) {
6331
6411
  console.log("[CONTINUOUS-FETCH] \u274C Ad request failed:", errorMessage);
6332
6412
  }
6413
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6414
+ 3,
6415
+ 11
6416
+ ];
6417
+ return [
6418
+ 4,
6419
+ this.tryNextAvailableAdWithRateLimit()
6420
+ ];
6421
+ case 10:
6422
+ _state.sent();
6423
+ return [
6424
+ 2
6425
+ ];
6426
+ case 11:
6333
6427
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6334
6428
  this.consecutiveFailures++;
6335
6429
  }
@@ -6337,21 +6431,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6337
6431
  4,
6338
6432
  this.tryNextAvailableAdWithRateLimit()
6339
6433
  ];
6340
- case 8:
6434
+ case 12:
6341
6435
  _state.sent();
6342
6436
  return [
6343
6437
  3,
6344
- 9
6438
+ 13
6345
6439
  ];
6346
- case 9:
6440
+ case 13:
6347
6441
  return [
6348
6442
  2
6349
6443
  ];
6350
- case 10:
6444
+ case 14:
6351
6445
  maxRetries = 3;
6352
6446
  if (!(this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3)) return [
6353
6447
  3,
6354
- 13
6448
+ 17
6355
6449
  ];
6356
6450
  if (this.config.debugAdTiming) {
6357
6451
  console.log("[CONTINUOUS-FETCH] ⏳ Queue empty, waiting for URLs... (".concat(retryCount + 1, "/").concat(maxRetries, ")"));
@@ -6362,18 +6456,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6362
6456
  return setTimeout(resolve, 500);
6363
6457
  })
6364
6458
  ];
6365
- case 11:
6459
+ case 15:
6366
6460
  _state.sent();
6367
6461
  return [
6368
6462
  4,
6369
6463
  this.tryNextAvailableAd(retryCount + 1)
6370
6464
  ];
6371
- case 12:
6465
+ case 16:
6372
6466
  _state.sent();
6373
6467
  return [
6374
6468
  2
6375
6469
  ];
6376
- case 13:
6470
+ case 17:
6377
6471
  if (!this.isShowingPlaceholder && remaining > 1e3) {
6378
6472
  this.showPlaceholderAndWaitForAds();
6379
6473
  } else {
@@ -6436,7 +6530,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6436
6530
  case 1:
6437
6531
  if (!(i < maxChecks)) return [
6438
6532
  3,
6439
- 10
6533
+ 12
6440
6534
  ];
6441
6535
  return [
6442
6536
  4,
@@ -6457,7 +6551,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6457
6551
  }
6458
6552
  return [
6459
6553
  3,
6460
- 10
6554
+ 12
6461
6555
  ];
6462
6556
  }
6463
6557
  if (!this.hasTimeToStartAnotherAd()) {
@@ -6466,19 +6560,19 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6466
6560
  }
6467
6561
  return [
6468
6562
  3,
6469
- 10
6563
+ 12
6470
6564
  ];
6471
6565
  }
6472
6566
  _state.label = 3;
6473
6567
  case 3:
6474
6568
  if (!(this.adRequestQueue.length > 0)) return [
6475
6569
  3,
6476
- 9
6570
+ 11
6477
6571
  ];
6478
6572
  if (!this.hasTimeToStartAnotherAd()) {
6479
6573
  return [
6480
6574
  3,
6481
- 9
6575
+ 11
6482
6576
  ];
6483
6577
  }
6484
6578
  nextAdUrl = this.adRequestQueue.shift();
@@ -6505,16 +6599,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6505
6599
  }
6506
6600
  this.isShowingPlaceholder = false;
6507
6601
  this.adPlayer.hidePlaceholder();
6508
- this.currentAdIndex++;
6509
- this.totalAdRequestsInBreak++;
6510
- this.lastAdRequestTime = Date.now();
6511
6602
  _state.label = 4;
6512
6603
  case 4:
6513
6604
  _state.trys.push([
6514
6605
  4,
6515
6606
  6,
6516
6607
  ,
6517
- 8
6608
+ 10
6518
6609
  ]);
6519
6610
  return [
6520
6611
  4,
@@ -6525,11 +6616,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6525
6616
  this.consecutiveFailures = 0;
6526
6617
  return [
6527
6618
  3,
6528
- 8
6619
+ 10
6529
6620
  ];
6530
6621
  case 6:
6531
6622
  error = _state.sent();
6532
6623
  errorMessage = error.message;
6624
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6625
+ 3,
6626
+ 8
6627
+ ];
6628
+ return [
6629
+ 4,
6630
+ this.tryNextAvailableAdWithRateLimit()
6631
+ ];
6632
+ case 7:
6633
+ _state.sent();
6634
+ return [
6635
+ 2
6636
+ ];
6637
+ case 8:
6533
6638
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6534
6639
  this.consecutiveFailures++;
6535
6640
  }
@@ -6537,23 +6642,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6537
6642
  4,
6538
6643
  this.tryNextAvailableAdWithRateLimit()
6539
6644
  ];
6540
- case 7:
6645
+ case 9:
6541
6646
  _state.sent();
6542
6647
  return [
6543
6648
  3,
6544
- 8
6649
+ 10
6545
6650
  ];
6546
- case 8:
6651
+ case 10:
6547
6652
  return [
6548
6653
  2
6549
6654
  ];
6550
- case 9:
6655
+ case 11:
6551
6656
  i++;
6552
6657
  return [
6553
6658
  3,
6554
6659
  1
6555
6660
  ];
6556
- case 10:
6661
+ case 12:
6557
6662
  if (this.config.debugAdTiming) {
6558
6663
  console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout, ending ad break");
6559
6664
  }
@@ -7025,6 +7130,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7025
7130
  2
7026
7131
  ];
7027
7132
  }
7133
+ if (!this.ensureLoadedAdFitsBudget()) {
7134
+ this.rejectLoadedAdForDuration();
7135
+ this.recreateAdController();
7136
+ throw new Error("ad_duration_exceeds_budget");
7137
+ }
7028
7138
  _state.label = 4;
7029
7139
  case 4:
7030
7140
  _state.trys.push([
@@ -7040,6 +7150,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7040
7150
  ];
7041
7151
  case 5:
7042
7152
  _state.sent();
7153
+ this.markAdStarted();
7043
7154
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7044
7155
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7045
7156
  }
@@ -7071,6 +7182,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7071
7182
  ,
7072
7183
  10
7073
7184
  ]);
7185
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback.adController)) {
7186
+ this.rejectLoadedAdForDuration(preloadedFallback.adController);
7187
+ throw playError;
7188
+ }
7074
7189
  this.clearAdFailsafeTimer();
7075
7190
  preservedMuted = this.adPlayer.getOriginalMutedState();
7076
7191
  preservedVolume = this.adPlayer.getOriginalVolume();
@@ -7081,14 +7196,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7081
7196
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
7082
7197
  this.adPlayer.resume();
7083
7198
  this.consecutiveFailures = 0;
7084
- this.currentAdIndex++;
7085
- this.totalAdRequestsInBreak++;
7086
7199
  return [
7087
7200
  4,
7088
7201
  this.adPlayer.play()
7089
7202
  ];
7090
7203
  case 8:
7091
7204
  _state.sent();
7205
+ this.markAdStarted();
7092
7206
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7093
7207
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7094
7208
  }
@@ -7149,6 +7263,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7149
7263
  ,
7150
7264
  16
7151
7265
  ]);
7266
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback1.adController)) {
7267
+ this.rejectLoadedAdForDuration(preloadedFallback1.adController);
7268
+ throw error;
7269
+ }
7152
7270
  this.clearAdRequestWatchdog();
7153
7271
  this.clearAdFailsafeTimer();
7154
7272
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
@@ -7160,14 +7278,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7160
7278
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
7161
7279
  this.adPlayer.resume();
7162
7280
  this.consecutiveFailures = 0;
7163
- this.currentAdIndex++;
7164
- this.totalAdRequestsInBreak++;
7165
7281
  return [
7166
7282
  4,
7167
7283
  this.adPlayer.play()
7168
7284
  ];
7169
7285
  case 14:
7170
7286
  _state.sent();
7287
+ this.markAdStarted();
7171
7288
  this.adPlayer.setAdVolume(this.getAdAudioVolume());
7172
7289
  return [
7173
7290
  2
@@ -7228,6 +7345,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7228
7345
  this.adRequestQueue = [];
7229
7346
  this.inAdBreak = false;
7230
7347
  this.adDetectSentForCurrentBreak = false;
7348
+ this.adBreakPlayedDurationMs = 0;
7231
7349
  this.activeScte35BreakKey = void 0;
7232
7350
  this.scheduledScte35BreakKey = void 0;
7233
7351
  this.expectedAdBreakDurationMs = void 0;
@@ -7417,13 +7535,117 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7417
7535
  }
7418
7536
  },
7419
7537
  {
7420
- key: "getRemainingAdMs",
7421
- value: function getRemainingAdMs() {
7422
- if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) return 0;
7538
+ key: "getWallClockRemainingAdMs",
7539
+ value: function getWallClockRemainingAdMs() {
7540
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
7541
+ return 0;
7542
+ }
7423
7543
  var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
7424
7544
  return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
7425
7545
  }
7426
7546
  },
7547
+ {
7548
+ key: "getDurationBudgetRemainingMs",
7549
+ value: function getDurationBudgetRemainingMs() {
7550
+ if (this.expectedAdBreakDurationMs == null) {
7551
+ return 0;
7552
+ }
7553
+ return Math.max(0, this.expectedAdBreakDurationMs - this.adBreakPlayedDurationMs);
7554
+ }
7555
+ },
7556
+ {
7557
+ key: "getLoadedAdDurationMs",
7558
+ value: function getLoadedAdDurationMs() {
7559
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7560
+ var _adController_getLoadedAdDuration;
7561
+ var durationSec = (_adController_getLoadedAdDuration = adController.getLoadedAdDuration) === null || _adController_getLoadedAdDuration === void 0 ? void 0 : _adController_getLoadedAdDuration.call(adController);
7562
+ if (durationSec == null || durationSec <= 0) {
7563
+ return 0;
7564
+ }
7565
+ return Math.round(durationSec * 1e3);
7566
+ }
7567
+ },
7568
+ {
7569
+ key: "adFitsRemainingBudget",
7570
+ value: function adFitsRemainingBudget(durationMs) {
7571
+ var adController = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : this.adPlayer;
7572
+ if (durationMs <= 0) {
7573
+ return true;
7574
+ }
7575
+ return durationMs <= this.getDurationBudgetRemainingMs();
7576
+ }
7577
+ },
7578
+ {
7579
+ key: "logAdDurationDecision",
7580
+ value: function logAdDurationDecision(accepted, durationMs) {
7581
+ var adController = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : this.adPlayer;
7582
+ var _ref;
7583
+ var _adController_getLoadedAdId;
7584
+ if (!this.config.debugAdTiming) {
7585
+ return;
7586
+ }
7587
+ var remainingBudget = this.getDurationBudgetRemainingMs();
7588
+ var adId = (_ref = (_adController_getLoadedAdId = adController.getLoadedAdId) === null || _adController_getLoadedAdId === void 0 ? void 0 : _adController_getLoadedAdId.call(adController)) !== null && _ref !== void 0 ? _ref : "unknown";
7589
+ if (accepted) {
7590
+ console.log("[AD-DURATION] Accepted ad id=".concat(adId, ", duration=").concat(durationMs, "ms, budget remaining after play=").concat(Math.max(0, remainingBudget - durationMs), "ms"));
7591
+ } else {
7592
+ console.log("[AD-DURATION] Rejected ad id=".concat(adId, ", duration=").concat(durationMs, "ms exceeds remaining budget=").concat(remainingBudget, "ms — requesting another ad"));
7593
+ }
7594
+ }
7595
+ },
7596
+ {
7597
+ key: "recordPlayedAdDuration",
7598
+ value: function recordPlayedAdDuration() {
7599
+ var durationMs = this.getLoadedAdDurationMs();
7600
+ if (durationMs <= 0) {
7601
+ return;
7602
+ }
7603
+ this.adBreakPlayedDurationMs += durationMs;
7604
+ if (this.config.debugAdTiming) {
7605
+ var _this_expectedAdBreakDurationMs;
7606
+ 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"));
7607
+ }
7608
+ }
7609
+ },
7610
+ {
7611
+ key: "ensureLoadedAdFitsBudget",
7612
+ value: function ensureLoadedAdFitsBudget() {
7613
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7614
+ var durationMs = this.getLoadedAdDurationMs(adController);
7615
+ var fits = this.adFitsRemainingBudget(durationMs, adController);
7616
+ this.logAdDurationDecision(fits, durationMs, adController);
7617
+ return fits;
7618
+ }
7619
+ },
7620
+ {
7621
+ key: "rejectLoadedAdForDuration",
7622
+ value: function rejectLoadedAdForDuration() {
7623
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7624
+ try {
7625
+ adController.destroy();
7626
+ } catch (unused) {}
7627
+ }
7628
+ },
7629
+ {
7630
+ key: "markAdStarted",
7631
+ value: function markAdStarted() {
7632
+ this.currentAdIndex++;
7633
+ this.totalAdsInBreak = this.currentAdIndex;
7634
+ this.totalAdRequestsInBreak++;
7635
+ this.lastAdRequestTime = Date.now();
7636
+ }
7637
+ },
7638
+ {
7639
+ key: "getRemainingAdMs",
7640
+ value: function getRemainingAdMs() {
7641
+ var wallClockRemaining = this.getWallClockRemainingAdMs();
7642
+ var budgetRemaining = this.getDurationBudgetRemainingMs();
7643
+ if (this.expectedAdBreakDurationMs == null) {
7644
+ return wallClockRemaining;
7645
+ }
7646
+ return Math.min(wallClockRemaining, budgetRemaining);
7647
+ }
7648
+ },
7427
7649
  {
7428
7650
  key: "getMinRemainingMsToStartAd",
7429
7651
  value: function getMinRemainingMsToStartAd() {
@@ -7437,7 +7659,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7437
7659
  {
7438
7660
  key: "hasTimeToStartAnotherAd",
7439
7661
  value: function hasTimeToStartAnotherAd() {
7440
- return this.getRemainingAdMs() > this.getMinRemainingMsToStartAd();
7662
+ var minRemaining = this.getMinRemainingMsToStartAd();
7663
+ return this.getWallClockRemainingAdMs() > minRemaining && this.getDurationBudgetRemainingMs() > minRemaining;
7441
7664
  }
7442
7665
  },
7443
7666
  {