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.
@@ -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 ? {
@@ -5316,7 +5325,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5316
5325
  epoch = this.adBreakEpoch;
5317
5326
  loadPromise = function() {
5318
5327
  return _async_to_generator(function() {
5319
- var _this, hasAdError, adErrorPayload, errorListenerCleanup, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, lateErrorListener, error;
5328
+ var _this, hasAdError, adErrorPayload, errorListenerCleanup, _preloadAd_getLoadedAdDuration, continueLiveStreamDuringAds, preloadAd, errorListener, isNoFill, poolEntry, loadedDuration, lateErrorListener, _poolEntry_durationSeconds, error;
5320
5329
  return _ts_generator(this, function(_state) {
5321
5330
  switch(_state.label){
5322
5331
  case 0:
@@ -5412,6 +5421,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5412
5421
  isReady: true,
5413
5422
  loadPromise: Promise.resolve()
5414
5423
  };
5424
+ loadedDuration = (_preloadAd_getLoadedAdDuration = preloadAd.getLoadedAdDuration) === null || _preloadAd_getLoadedAdDuration === void 0 ? void 0 : _preloadAd_getLoadedAdDuration.call(preloadAd);
5425
+ if (loadedDuration != null && loadedDuration > 0) {
5426
+ poolEntry.durationSeconds = loadedDuration;
5427
+ }
5415
5428
  lateErrorListener = function lateErrorListener(payload) {
5416
5429
  var index = _this.preloadPool.findIndex(function(entry) {
5417
5430
  return entry.vastUrl === vastUrl;
@@ -5429,7 +5442,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5429
5442
  preloadAd.on("ad_error", lateErrorListener);
5430
5443
  this.preloadPool.push(poolEntry);
5431
5444
  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, ")"));
5445
+ ;
5446
+ 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
5447
  }
5434
5448
  if (errorListenerCleanup) {
5435
5449
  errorListenerCleanup();
@@ -5710,8 +5724,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5710
5724
  }
5711
5725
  this.inAdBreak = true;
5712
5726
  this.currentAdBreakStartWallClockMs = Date.now();
5727
+ this.adBreakPlayedDurationMs = 0;
5713
5728
  this.currentAdIndex = 0;
5714
- this.totalAdsInBreak = 1;
5729
+ this.totalAdsInBreak = 0;
5715
5730
  this.adPodQueue = [];
5716
5731
  this.showAds = true;
5717
5732
  this.showPlaceholderLayer();
@@ -5765,14 +5780,30 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5765
5780
  case 1:
5766
5781
  _state.trys.push([
5767
5782
  1,
5768
- 8,
5783
+ 12,
5769
5784
  ,
5770
- 14
5785
+ 20
5771
5786
  ]);
5772
5787
  if (!(usePreloadedAd && preloadedController)) return [
5788
+ 3,
5789
+ 5
5790
+ ];
5791
+ if (!!this.ensureLoadedAdFitsBudget(preloadedController)) return [
5773
5792
  3,
5774
5793
  3
5775
5794
  ];
5795
+ this.rejectLoadedAdForDuration(preloadedController);
5796
+ this.startContinuousFetching(baseVastUrl);
5797
+ return [
5798
+ 4,
5799
+ this.tryNextAvailableAdWithRateLimit()
5800
+ ];
5801
+ case 2:
5802
+ _state.sent();
5803
+ return [
5804
+ 2
5805
+ ];
5806
+ case 3:
5776
5807
  preservedMuted = this.adPlayer.getOriginalMutedState();
5777
5808
  preservedVolume = this.adPlayer.getOriginalVolume();
5778
5809
  this.adPlayer.destroy();
@@ -5785,7 +5816,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5785
5816
  }
5786
5817
  this.adPlayer.resume();
5787
5818
  this.consecutiveFailures = 0;
5788
- this.currentAdIndex++;
5789
5819
  this.startContinuousFetching(baseVastUrl);
5790
5820
  if (!this.preloadPoolActive) {
5791
5821
  this.preloadPoolActive = true;
@@ -5795,8 +5825,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5795
5825
  4,
5796
5826
  this.adPlayer.play()
5797
5827
  ];
5798
- case 2:
5828
+ case 4:
5799
5829
  _state.sent();
5830
+ this.markAdStarted();
5800
5831
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5801
5832
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5802
5833
  }
@@ -5804,21 +5835,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5804
5835
  this.adPlayer.setAdVolume(adVolume);
5805
5836
  return [
5806
5837
  3,
5807
- 7
5838
+ 11
5808
5839
  ];
5809
- case 3:
5840
+ case 5:
5810
5841
  return [
5811
5842
  4,
5812
5843
  this.enforceGlobalRateLimit()
5813
5844
  ];
5814
- case 4:
5845
+ case 6:
5815
5846
  _state.sent();
5816
5847
  this.lastAdRequestTime = Date.now();
5817
5848
  return [
5818
5849
  4,
5819
5850
  this.adPlayer.requestAds(firstAdUrl)
5820
5851
  ];
5821
- case 5:
5852
+ case 7:
5822
5853
  _state.sent();
5823
5854
  sendAdLoadedTracking(this.config.licenseKey, {
5824
5855
  source: "hls",
@@ -5828,8 +5859,24 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5828
5859
  if (this.config.debugAdTiming) {
5829
5860
  console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
5830
5861
  }
5862
+ if (!!this.ensureLoadedAdFitsBudget()) return [
5863
+ 3,
5864
+ 9
5865
+ ];
5866
+ this.rejectLoadedAdForDuration();
5867
+ this.recreateAdController();
5868
+ this.startContinuousFetching(baseVastUrl);
5869
+ return [
5870
+ 4,
5871
+ this.tryNextAvailableAdWithRateLimit()
5872
+ ];
5873
+ case 8:
5874
+ _state.sent();
5875
+ return [
5876
+ 2
5877
+ ];
5878
+ case 9:
5831
5879
  this.consecutiveFailures = 0;
5832
- this.currentAdIndex++;
5833
5880
  this.startContinuousFetching(baseVastUrl);
5834
5881
  if (!this.preloadPoolActive) {
5835
5882
  this.startPreloadPool(baseVastUrl, []);
@@ -5838,44 +5885,61 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5838
5885
  4,
5839
5886
  this.adPlayer.play()
5840
5887
  ];
5841
- case 6:
5888
+ case 10:
5842
5889
  _state.sent();
5890
+ this.markAdStarted();
5843
5891
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5844
5892
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5845
5893
  }
5846
5894
  adVolume1 = currentMuted ? 0 : currentVolume;
5847
5895
  this.adPlayer.setAdVolume(adVolume1);
5848
- _state.label = 7;
5849
- case 7:
5896
+ _state.label = 11;
5897
+ case 11:
5850
5898
  return [
5851
5899
  3,
5852
- 14
5900
+ 20
5853
5901
  ];
5854
- case 8:
5902
+ case 12:
5855
5903
  error = _state.sent();
5856
5904
  if (this.config.debugAdTiming) {
5857
5905
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F First ad request failed:", error);
5858
5906
  }
5859
5907
  if (!!usePreloadedAd) return [
5860
5908
  3,
5861
- 12
5909
+ 18
5862
5910
  ];
5863
5911
  fallbackPreloaded = this.getPreloadedAd();
5864
5912
  if (!fallbackPreloaded) return [
5865
5913
  3,
5866
- 12
5914
+ 18
5867
5915
  ];
5868
5916
  if (this.config.debugAdTiming) {
5869
5917
  console.log("[CONTINUOUS-FETCH] \uD83D\uDD04 First ad failed, using preloaded fallback");
5870
5918
  }
5871
- _state.label = 9;
5872
- case 9:
5919
+ _state.label = 13;
5920
+ case 13:
5873
5921
  _state.trys.push([
5874
- 9,
5875
- 11,
5922
+ 13,
5923
+ 17,
5876
5924
  ,
5877
- 12
5925
+ 18
5878
5926
  ]);
5927
+ if (!!this.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
5928
+ 3,
5929
+ 15
5930
+ ];
5931
+ this.rejectLoadedAdForDuration(fallbackPreloaded.adController);
5932
+ this.startContinuousFetching(baseVastUrl);
5933
+ return [
5934
+ 4,
5935
+ this.tryNextAvailableAdWithRateLimit()
5936
+ ];
5937
+ case 14:
5938
+ _state.sent();
5939
+ return [
5940
+ 2
5941
+ ];
5942
+ case 15:
5879
5943
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
5880
5944
  preservedVolume1 = this.adPlayer.getOriginalVolume();
5881
5945
  this.adPlayer.destroy();
@@ -5885,7 +5949,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5885
5949
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
5886
5950
  this.adPlayer.resume();
5887
5951
  this.consecutiveFailures = 0;
5888
- this.currentAdIndex++;
5889
5952
  this.startContinuousFetching(baseVastUrl);
5890
5953
  if (!this.preloadPoolActive) {
5891
5954
  this.preloadPoolActive = true;
@@ -5895,8 +5958,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5895
5958
  4,
5896
5959
  this.adPlayer.play()
5897
5960
  ];
5898
- case 10:
5961
+ case 16:
5899
5962
  _state.sent();
5963
+ this.markAdStarted();
5900
5964
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
5901
5965
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
5902
5966
  }
@@ -5905,16 +5969,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5905
5969
  return [
5906
5970
  2
5907
5971
  ];
5908
- case 11:
5972
+ case 17:
5909
5973
  fallbackError = _state.sent();
5910
5974
  if (this.config.debugAdTiming) {
5911
5975
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded fallback also failed:", fallbackError);
5912
5976
  }
5913
5977
  return [
5914
5978
  3,
5915
- 12
5979
+ 18
5916
5980
  ];
5917
- case 12:
5981
+ case 18:
5918
5982
  if (this.isTemporaryAdError(error)) {
5919
5983
  this.temporaryFailureUrls.set(firstAdUrl, Date.now());
5920
5984
  if (this.config.debugAdTiming) {
@@ -5932,13 +5996,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5932
5996
  4,
5933
5997
  this.tryNextAvailableAdWithRateLimit()
5934
5998
  ];
5935
- case 13:
5999
+ case 19:
5936
6000
  _state.sent();
5937
6001
  return [
5938
6002
  3,
5939
- 14
6003
+ 20
5940
6004
  ];
5941
- case 14:
6005
+ case 20:
5942
6006
  return [
5943
6007
  2
5944
6008
  ];
@@ -5979,10 +6043,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5979
6043
  return _ts_generator(this, function(_state) {
5980
6044
  switch(_state.label){
5981
6045
  case 0:
5982
- remaining = _this.getRemainingAdMs();
6046
+ remaining = Math.min(_this.getWallClockRemainingAdMs(), _this.getDurationBudgetRemainingMs());
5983
6047
  if (remaining <= _this.getMinRemainingMsToStartAd()) {
5984
6048
  if (_this.config.debugAdTiming) {
5985
- console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (".concat(remaining, "ms), stopping URL generation"));
6049
+ console.log("[CONTINUOUS-FETCH] ⏹️ Not enough ad break time left (wall=".concat(_this.getWallClockRemainingAdMs(), "ms, budget=").concat(_this.getDurationBudgetRemainingMs(), "ms), stopping URL generation"));
5986
6050
  }
5987
6051
  return [
5988
6052
  2,
@@ -6064,7 +6128,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6064
6128
  });
6065
6129
  }
6066
6130
  _this.adRequestQueue.push(newAdUrl);
6067
- _this.totalAdsInBreak++;
6068
6131
  generationDelay = _this.consecutiveFailures > 0 ? Math.min(1e3 * Math.pow(2, _this.consecutiveFailures), 5e3) : 500;
6069
6132
  return [
6070
6133
  4,
@@ -6233,7 +6296,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6233
6296
  preloaded = this.getPreloadedAd();
6234
6297
  if (!preloaded) return [
6235
6298
  3,
6236
- 4
6299
+ 6
6237
6300
  ];
6238
6301
  if (this.config.debugAdTiming) {
6239
6302
  console.log("[CONTINUOUS-FETCH] \uD83C\uDFAF Using preloaded ad from pool (".concat(this.preloadPool.length, " remaining in pool)"));
@@ -6247,10 +6310,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6247
6310
  case 1:
6248
6311
  _state.trys.push([
6249
6312
  1,
6250
- 3,
6313
+ 5,
6251
6314
  ,
6252
- 4
6315
+ 6
6253
6316
  ]);
6317
+ if (!!this.ensureLoadedAdFitsBudget(preloaded.adController)) return [
6318
+ 3,
6319
+ 3
6320
+ ];
6321
+ this.rejectLoadedAdForDuration(preloaded.adController);
6322
+ return [
6323
+ 4,
6324
+ this.tryNextAvailableAdWithRateLimit()
6325
+ ];
6326
+ case 2:
6327
+ _state.sent();
6328
+ return [
6329
+ 2
6330
+ ];
6331
+ case 3:
6254
6332
  preservedMuted = this.adPlayer.getOriginalMutedState();
6255
6333
  preservedVolume = this.adPlayer.getOriginalVolume();
6256
6334
  this.adPlayer.destroy();
@@ -6259,15 +6337,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6259
6337
  this.attachImaEventListeners();
6260
6338
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
6261
6339
  this.adPlayer.resume();
6262
- this.currentAdIndex++;
6263
- this.totalAdRequestsInBreak++;
6264
- this.lastAdRequestTime = Date.now();
6265
6340
  return [
6266
6341
  4,
6267
6342
  this.adPlayer.play()
6268
6343
  ];
6269
- case 2:
6344
+ case 4:
6270
6345
  _state.sent();
6346
+ this.markAdStarted();
6271
6347
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
6272
6348
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
6273
6349
  }
@@ -6276,25 +6352,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6276
6352
  return [
6277
6353
  2
6278
6354
  ];
6279
- case 3:
6355
+ case 5:
6280
6356
  error = _state.sent();
6281
6357
  if (this.config.debugAdTiming) {
6282
6358
  console.warn("[CONTINUOUS-FETCH] \u26A0\uFE0F Preloaded ad failed to play:", error);
6283
6359
  }
6284
6360
  return [
6285
6361
  3,
6286
- 4
6362
+ 6
6287
6363
  ];
6288
- case 4:
6364
+ case 6:
6289
6365
  if (!(this.adRequestQueue.length > 0)) return [
6290
6366
  3,
6291
- 10
6367
+ 14
6292
6368
  ];
6293
6369
  nextAdUrl = this.adRequestQueue.shift();
6294
6370
  if (!nextAdUrl) {
6295
6371
  return [
6296
6372
  3,
6297
- 4
6373
+ 6
6298
6374
  ];
6299
6375
  }
6300
6376
  if (this.failedVastUrls.has(nextAdUrl)) {
@@ -6303,7 +6379,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6303
6379
  }
6304
6380
  return [
6305
6381
  3,
6306
- 4
6382
+ 6
6307
6383
  ];
6308
6384
  }
6309
6385
  if (this.isUrlInCooldown(nextAdUrl)) {
@@ -6312,40 +6388,51 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6312
6388
  }
6313
6389
  return [
6314
6390
  3,
6315
- 4
6391
+ 6
6316
6392
  ];
6317
6393
  }
6318
6394
  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)"));
6395
+ 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
6396
  }
6321
- this.currentAdIndex++;
6322
- this.totalAdRequestsInBreak++;
6323
- this.lastAdRequestTime = Date.now();
6324
- _state.label = 5;
6325
- case 5:
6397
+ _state.label = 7;
6398
+ case 7:
6326
6399
  _state.trys.push([
6327
- 5,
6328
6400
  7,
6401
+ 9,
6329
6402
  ,
6330
- 9
6403
+ 13
6331
6404
  ]);
6332
6405
  return [
6333
6406
  4,
6334
6407
  this.playSingleAd(nextAdUrl)
6335
6408
  ];
6336
- case 6:
6409
+ case 8:
6337
6410
  _state.sent();
6338
6411
  this.consecutiveFailures = 0;
6339
6412
  return [
6340
6413
  3,
6341
- 9
6414
+ 13
6342
6415
  ];
6343
- case 7:
6416
+ case 9:
6344
6417
  error1 = _state.sent();
6345
6418
  errorMessage = error1.message;
6346
6419
  if (this.config.debugAdTiming) {
6347
6420
  console.log("[CONTINUOUS-FETCH] \u274C Ad request failed:", errorMessage);
6348
6421
  }
6422
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6423
+ 3,
6424
+ 11
6425
+ ];
6426
+ return [
6427
+ 4,
6428
+ this.tryNextAvailableAdWithRateLimit()
6429
+ ];
6430
+ case 10:
6431
+ _state.sent();
6432
+ return [
6433
+ 2
6434
+ ];
6435
+ case 11:
6349
6436
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6350
6437
  this.consecutiveFailures++;
6351
6438
  }
@@ -6353,21 +6440,21 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6353
6440
  4,
6354
6441
  this.tryNextAvailableAdWithRateLimit()
6355
6442
  ];
6356
- case 8:
6443
+ case 12:
6357
6444
  _state.sent();
6358
6445
  return [
6359
6446
  3,
6360
- 9
6447
+ 13
6361
6448
  ];
6362
- case 9:
6449
+ case 13:
6363
6450
  return [
6364
6451
  2
6365
6452
  ];
6366
- case 10:
6453
+ case 14:
6367
6454
  maxRetries = 3;
6368
6455
  if (!(this.continuousFetchingActive && retryCount < maxRetries && remaining > 2e3)) return [
6369
6456
  3,
6370
- 13
6457
+ 17
6371
6458
  ];
6372
6459
  if (this.config.debugAdTiming) {
6373
6460
  console.log("[CONTINUOUS-FETCH] ⏳ Queue empty, waiting for URLs... (".concat(retryCount + 1, "/").concat(maxRetries, ")"));
@@ -6378,18 +6465,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6378
6465
  return setTimeout(resolve, 500);
6379
6466
  })
6380
6467
  ];
6381
- case 11:
6468
+ case 15:
6382
6469
  _state.sent();
6383
6470
  return [
6384
6471
  4,
6385
6472
  this.tryNextAvailableAd(retryCount + 1)
6386
6473
  ];
6387
- case 12:
6474
+ case 16:
6388
6475
  _state.sent();
6389
6476
  return [
6390
6477
  2
6391
6478
  ];
6392
- case 13:
6479
+ case 17:
6393
6480
  if (!this.isShowingPlaceholder && remaining > 1e3) {
6394
6481
  this.showPlaceholderAndWaitForAds();
6395
6482
  } else {
@@ -6452,7 +6539,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6452
6539
  case 1:
6453
6540
  if (!(i < maxChecks)) return [
6454
6541
  3,
6455
- 10
6542
+ 12
6456
6543
  ];
6457
6544
  return [
6458
6545
  4,
@@ -6473,7 +6560,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6473
6560
  }
6474
6561
  return [
6475
6562
  3,
6476
- 10
6563
+ 12
6477
6564
  ];
6478
6565
  }
6479
6566
  if (!this.hasTimeToStartAnotherAd()) {
@@ -6482,19 +6569,19 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6482
6569
  }
6483
6570
  return [
6484
6571
  3,
6485
- 10
6572
+ 12
6486
6573
  ];
6487
6574
  }
6488
6575
  _state.label = 3;
6489
6576
  case 3:
6490
6577
  if (!(this.adRequestQueue.length > 0)) return [
6491
6578
  3,
6492
- 9
6579
+ 11
6493
6580
  ];
6494
6581
  if (!this.hasTimeToStartAnotherAd()) {
6495
6582
  return [
6496
6583
  3,
6497
- 9
6584
+ 11
6498
6585
  ];
6499
6586
  }
6500
6587
  nextAdUrl = this.adRequestQueue.shift();
@@ -6521,16 +6608,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6521
6608
  }
6522
6609
  this.isShowingPlaceholder = false;
6523
6610
  this.adPlayer.hidePlaceholder();
6524
- this.currentAdIndex++;
6525
- this.totalAdRequestsInBreak++;
6526
- this.lastAdRequestTime = Date.now();
6527
6611
  _state.label = 4;
6528
6612
  case 4:
6529
6613
  _state.trys.push([
6530
6614
  4,
6531
6615
  6,
6532
6616
  ,
6533
- 8
6617
+ 10
6534
6618
  ]);
6535
6619
  return [
6536
6620
  4,
@@ -6541,11 +6625,25 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6541
6625
  this.consecutiveFailures = 0;
6542
6626
  return [
6543
6627
  3,
6544
- 8
6628
+ 10
6545
6629
  ];
6546
6630
  case 6:
6547
6631
  error = _state.sent();
6548
6632
  errorMessage = error.message;
6633
+ if (!errorMessage.includes("ad_duration_exceeds_budget")) return [
6634
+ 3,
6635
+ 8
6636
+ ];
6637
+ return [
6638
+ 4,
6639
+ this.tryNextAvailableAdWithRateLimit()
6640
+ ];
6641
+ case 7:
6642
+ _state.sent();
6643
+ return [
6644
+ 2
6645
+ ];
6646
+ case 8:
6549
6647
  if (!errorMessage.includes("permanently failed") && !errorMessage.includes("in cooldown")) {
6550
6648
  this.consecutiveFailures++;
6551
6649
  }
@@ -6553,23 +6651,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6553
6651
  4,
6554
6652
  this.tryNextAvailableAdWithRateLimit()
6555
6653
  ];
6556
- case 7:
6654
+ case 9:
6557
6655
  _state.sent();
6558
6656
  return [
6559
6657
  3,
6560
- 8
6658
+ 10
6561
6659
  ];
6562
- case 8:
6660
+ case 10:
6563
6661
  return [
6564
6662
  2
6565
6663
  ];
6566
- case 9:
6664
+ case 11:
6567
6665
  i++;
6568
6666
  return [
6569
6667
  3,
6570
6668
  1
6571
6669
  ];
6572
- case 10:
6670
+ case 12:
6573
6671
  if (this.config.debugAdTiming) {
6574
6672
  console.log("[CONTINUOUS-FETCH] \u23F0 Placeholder timeout, ending ad break");
6575
6673
  }
@@ -7041,6 +7139,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7041
7139
  2
7042
7140
  ];
7043
7141
  }
7142
+ if (!this.ensureLoadedAdFitsBudget()) {
7143
+ this.rejectLoadedAdForDuration();
7144
+ this.recreateAdController();
7145
+ throw new Error("ad_duration_exceeds_budget");
7146
+ }
7044
7147
  _state.label = 4;
7045
7148
  case 4:
7046
7149
  _state.trys.push([
@@ -7056,6 +7159,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7056
7159
  ];
7057
7160
  case 5:
7058
7161
  _state.sent();
7162
+ this.markAdStarted();
7059
7163
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7060
7164
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7061
7165
  }
@@ -7087,6 +7191,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7087
7191
  ,
7088
7192
  10
7089
7193
  ]);
7194
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback.adController)) {
7195
+ this.rejectLoadedAdForDuration(preloadedFallback.adController);
7196
+ throw playError;
7197
+ }
7090
7198
  this.clearAdFailsafeTimer();
7091
7199
  preservedMuted = this.adPlayer.getOriginalMutedState();
7092
7200
  preservedVolume = this.adPlayer.getOriginalVolume();
@@ -7097,14 +7205,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7097
7205
  this.adPlayer.updateOriginalMutedState(preservedMuted, preservedVolume);
7098
7206
  this.adPlayer.resume();
7099
7207
  this.consecutiveFailures = 0;
7100
- this.currentAdIndex++;
7101
- this.totalAdRequestsInBreak++;
7102
7208
  return [
7103
7209
  4,
7104
7210
  this.adPlayer.play()
7105
7211
  ];
7106
7212
  case 8:
7107
7213
  _state.sent();
7214
+ this.markAdStarted();
7108
7215
  if (this.expectedAdBreakDurationMs != null && this.adStopTimerId == null) {
7109
7216
  this.scheduleAdStopCountdown(this.getRemainingAdMs());
7110
7217
  }
@@ -7165,6 +7272,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7165
7272
  ,
7166
7273
  16
7167
7274
  ]);
7275
+ if (!this.ensureLoadedAdFitsBudget(preloadedFallback1.adController)) {
7276
+ this.rejectLoadedAdForDuration(preloadedFallback1.adController);
7277
+ throw error;
7278
+ }
7168
7279
  this.clearAdRequestWatchdog();
7169
7280
  this.clearAdFailsafeTimer();
7170
7281
  preservedMuted1 = this.adPlayer.getOriginalMutedState();
@@ -7176,14 +7287,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7176
7287
  this.adPlayer.updateOriginalMutedState(preservedMuted1, preservedVolume1);
7177
7288
  this.adPlayer.resume();
7178
7289
  this.consecutiveFailures = 0;
7179
- this.currentAdIndex++;
7180
- this.totalAdRequestsInBreak++;
7181
7290
  return [
7182
7291
  4,
7183
7292
  this.adPlayer.play()
7184
7293
  ];
7185
7294
  case 14:
7186
7295
  _state.sent();
7296
+ this.markAdStarted();
7187
7297
  this.adPlayer.setAdVolume(this.getAdAudioVolume());
7188
7298
  return [
7189
7299
  2
@@ -7244,6 +7354,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7244
7354
  this.adRequestQueue = [];
7245
7355
  this.inAdBreak = false;
7246
7356
  this.adDetectSentForCurrentBreak = false;
7357
+ this.adBreakPlayedDurationMs = 0;
7247
7358
  this.activeScte35BreakKey = void 0;
7248
7359
  this.scheduledScte35BreakKey = void 0;
7249
7360
  this.expectedAdBreakDurationMs = void 0;
@@ -7433,13 +7544,117 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7433
7544
  }
7434
7545
  },
7435
7546
  {
7436
- key: "getRemainingAdMs",
7437
- value: function getRemainingAdMs() {
7438
- if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) return 0;
7547
+ key: "getWallClockRemainingAdMs",
7548
+ value: function getWallClockRemainingAdMs() {
7549
+ if (this.expectedAdBreakDurationMs == null || this.currentAdBreakStartWallClockMs == null) {
7550
+ return 0;
7551
+ }
7439
7552
  var elapsed = Date.now() - this.currentAdBreakStartWallClockMs;
7440
7553
  return Math.max(0, this.expectedAdBreakDurationMs - elapsed);
7441
7554
  }
7442
7555
  },
7556
+ {
7557
+ key: "getDurationBudgetRemainingMs",
7558
+ value: function getDurationBudgetRemainingMs() {
7559
+ if (this.expectedAdBreakDurationMs == null) {
7560
+ return 0;
7561
+ }
7562
+ return Math.max(0, this.expectedAdBreakDurationMs - this.adBreakPlayedDurationMs);
7563
+ }
7564
+ },
7565
+ {
7566
+ key: "getLoadedAdDurationMs",
7567
+ value: function getLoadedAdDurationMs() {
7568
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7569
+ var _adController_getLoadedAdDuration;
7570
+ var durationSec = (_adController_getLoadedAdDuration = adController.getLoadedAdDuration) === null || _adController_getLoadedAdDuration === void 0 ? void 0 : _adController_getLoadedAdDuration.call(adController);
7571
+ if (durationSec == null || durationSec <= 0) {
7572
+ return 0;
7573
+ }
7574
+ return Math.round(durationSec * 1e3);
7575
+ }
7576
+ },
7577
+ {
7578
+ key: "adFitsRemainingBudget",
7579
+ value: function adFitsRemainingBudget(durationMs) {
7580
+ var adController = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : this.adPlayer;
7581
+ if (durationMs <= 0) {
7582
+ return true;
7583
+ }
7584
+ return durationMs <= this.getDurationBudgetRemainingMs();
7585
+ }
7586
+ },
7587
+ {
7588
+ key: "logAdDurationDecision",
7589
+ value: function logAdDurationDecision(accepted, durationMs) {
7590
+ var adController = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : this.adPlayer;
7591
+ var _ref;
7592
+ var _adController_getLoadedAdId;
7593
+ if (!this.config.debugAdTiming) {
7594
+ return;
7595
+ }
7596
+ var remainingBudget = this.getDurationBudgetRemainingMs();
7597
+ var adId = (_ref = (_adController_getLoadedAdId = adController.getLoadedAdId) === null || _adController_getLoadedAdId === void 0 ? void 0 : _adController_getLoadedAdId.call(adController)) !== null && _ref !== void 0 ? _ref : "unknown";
7598
+ if (accepted) {
7599
+ console.log("[AD-DURATION] Accepted ad id=".concat(adId, ", duration=").concat(durationMs, "ms, budget remaining after play=").concat(Math.max(0, remainingBudget - durationMs), "ms"));
7600
+ } else {
7601
+ console.log("[AD-DURATION] Rejected ad id=".concat(adId, ", duration=").concat(durationMs, "ms exceeds remaining budget=").concat(remainingBudget, "ms — requesting another ad"));
7602
+ }
7603
+ }
7604
+ },
7605
+ {
7606
+ key: "recordPlayedAdDuration",
7607
+ value: function recordPlayedAdDuration() {
7608
+ var durationMs = this.getLoadedAdDurationMs();
7609
+ if (durationMs <= 0) {
7610
+ return;
7611
+ }
7612
+ this.adBreakPlayedDurationMs += durationMs;
7613
+ if (this.config.debugAdTiming) {
7614
+ var _this_expectedAdBreakDurationMs;
7615
+ 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"));
7616
+ }
7617
+ }
7618
+ },
7619
+ {
7620
+ key: "ensureLoadedAdFitsBudget",
7621
+ value: function ensureLoadedAdFitsBudget() {
7622
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7623
+ var durationMs = this.getLoadedAdDurationMs(adController);
7624
+ var fits = this.adFitsRemainingBudget(durationMs, adController);
7625
+ this.logAdDurationDecision(fits, durationMs, adController);
7626
+ return fits;
7627
+ }
7628
+ },
7629
+ {
7630
+ key: "rejectLoadedAdForDuration",
7631
+ value: function rejectLoadedAdForDuration() {
7632
+ var adController = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.adPlayer;
7633
+ try {
7634
+ adController.destroy();
7635
+ } catch (unused) {}
7636
+ }
7637
+ },
7638
+ {
7639
+ key: "markAdStarted",
7640
+ value: function markAdStarted() {
7641
+ this.currentAdIndex++;
7642
+ this.totalAdsInBreak = this.currentAdIndex;
7643
+ this.totalAdRequestsInBreak++;
7644
+ this.lastAdRequestTime = Date.now();
7645
+ }
7646
+ },
7647
+ {
7648
+ key: "getRemainingAdMs",
7649
+ value: function getRemainingAdMs() {
7650
+ var wallClockRemaining = this.getWallClockRemainingAdMs();
7651
+ var budgetRemaining = this.getDurationBudgetRemainingMs();
7652
+ if (this.expectedAdBreakDurationMs == null) {
7653
+ return wallClockRemaining;
7654
+ }
7655
+ return Math.min(wallClockRemaining, budgetRemaining);
7656
+ }
7657
+ },
7443
7658
  {
7444
7659
  key: "getMinRemainingMsToStartAd",
7445
7660
  value: function getMinRemainingMsToStartAd() {
@@ -7453,7 +7668,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7453
7668
  {
7454
7669
  key: "hasTimeToStartAnotherAd",
7455
7670
  value: function hasTimeToStartAnotherAd() {
7456
- return this.getRemainingAdMs() > this.getMinRemainingMsToStartAd();
7671
+ var minRemaining = this.getMinRemainingMsToStartAd();
7672
+ return this.getWallClockRemainingAdMs() > minRemaining && this.getDurationBudgetRemainingMs() > minRemaining;
7457
7673
  }
7458
7674
  },
7459
7675
  {