stormcloud-video-player 0.8.25 → 0.8.27

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.
Files changed (48) hide show
  1. package/dist/stormcloud-vp.min.js +1 -1
  2. package/lib/index.cjs +714 -128
  3. package/lib/index.cjs.map +1 -1
  4. package/lib/index.d.cts +7 -0
  5. package/lib/index.d.ts +7 -0
  6. package/lib/index.js +714 -128
  7. package/lib/index.js.map +1 -1
  8. package/lib/player/AdBreakOrchestrator.cjs +278 -49
  9. package/lib/player/AdBreakOrchestrator.cjs.map +1 -1
  10. package/lib/player/AdBreakOrchestrator.d.cts +4 -1
  11. package/lib/player/AdConfigManager.cjs +144 -14
  12. package/lib/player/AdConfigManager.cjs.map +1 -1
  13. package/lib/player/AdConfigManager.d.cts +12 -1
  14. package/lib/player/AdPreloadPool.d.cts +1 -1
  15. package/lib/player/AdTimingService.cjs +7 -0
  16. package/lib/player/AdTimingService.cjs.map +1 -1
  17. package/lib/player/AdTimingService.d.cts +2 -1
  18. package/lib/player/HlsEngine.d.cts +1 -1
  19. package/lib/player/PlayerControls.d.cts +1 -1
  20. package/lib/player/Scte35CueManager.d.cts +1 -1
  21. package/lib/player/Scte35Parser.d.cts +1 -1
  22. package/lib/player/StormcloudVideoPlayer.cjs +714 -128
  23. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  24. package/lib/player/StormcloudVideoPlayer.d.cts +2 -1
  25. package/lib/player/playerTypes.d.cts +1 -1
  26. package/lib/players/HlsPlayer.cjs +714 -128
  27. package/lib/players/HlsPlayer.cjs.map +1 -1
  28. package/lib/players/HlsPlayer.d.cts +1 -1
  29. package/lib/players/index.cjs +714 -128
  30. package/lib/players/index.cjs.map +1 -1
  31. package/lib/sdk/hlsAdPlayer.cjs +343 -71
  32. package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
  33. package/lib/sdk/hlsAdPlayer.d.cts +1 -1
  34. package/lib/{types-Bz4aRmzc.d.cts → types-Xgz2_W1C.d.cts} +6 -0
  35. package/lib/ui/StormcloudVideoPlayer.cjs +714 -128
  36. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  37. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  38. package/lib/utils/ctvVastSignals.cjs +80 -8
  39. package/lib/utils/ctvVastSignals.cjs.map +1 -1
  40. package/lib/utils/tracking.cjs +80 -8
  41. package/lib/utils/tracking.cjs.map +1 -1
  42. package/lib/utils/tracking.d.cts +1 -1
  43. package/lib/utils/vastEnvironmentSignals.cjs +83 -13
  44. package/lib/utils/vastEnvironmentSignals.cjs.map +1 -1
  45. package/lib/utils/vastMacros.cjs +15 -1
  46. package/lib/utils/vastMacros.cjs.map +1 -1
  47. package/lib/utils/vastMacros.d.cts +3 -0
  48. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -382,6 +382,8 @@ function createHlsAdPlayer(contentVideo, options) {
382
382
  var adHls;
383
383
  var adContainerEl;
384
384
  var currentAd;
385
+ var podAds = [];
386
+ var podIndex = 0;
385
387
  var sessionId;
386
388
  var preloadedAds = /* @__PURE__ */ new Map();
387
389
  var preloadingAds = /* @__PURE__ */ new Map();
@@ -546,14 +548,14 @@ function createHlsAdPlayer(contentVideo, options) {
546
548
  error: []
547
549
  };
548
550
  }
549
- function extractTrackingUrls(xmlDoc) {
551
+ function extractTrackingUrls(scope) {
550
552
  var trackingUrls = createEmptyTrackingUrls();
551
- xmlDoc.querySelectorAll("Impression").forEach(function(el) {
553
+ scope.querySelectorAll("Impression").forEach(function(el) {
552
554
  var _el_textContent;
553
555
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
554
556
  if (url) trackingUrls.impression.push(url);
555
557
  });
556
- xmlDoc.querySelectorAll("Tracking").forEach(function(el) {
558
+ scope.querySelectorAll("Tracking").forEach(function(el) {
557
559
  var _el_textContent;
558
560
  var event = el.getAttribute("event");
559
561
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
@@ -564,7 +566,7 @@ function createHlsAdPlayer(contentVideo, options) {
564
566
  }
565
567
  }
566
568
  });
567
- xmlDoc.querySelectorAll("Error").forEach(function(el) {
569
+ scope.querySelectorAll("Error").forEach(function(el) {
568
570
  var _el_textContent;
569
571
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
570
572
  if (url) trackingUrls.error.push(url);
@@ -674,6 +676,75 @@ function createHlsAdPlayer(contentVideo, options) {
674
676
  };
675
677
  }
676
678
  }
679
+ function parseAdElement(adElement) {
680
+ try {
681
+ var _adElement_querySelector, _adElement_querySelector_textContent, _adElement_querySelector1, _adElement_querySelector_textContent1, _adElement_querySelector2, _adElement_querySelector3;
682
+ var adId = adElement.getAttribute("id") || "unknown";
683
+ var title = ((_adElement_querySelector = adElement.querySelector("AdTitle")) === null || _adElement_querySelector === void 0 ? void 0 : _adElement_querySelector.textContent) || "Ad";
684
+ var clickThrough = (_adElement_querySelector1 = adElement.querySelector("ClickThrough")) === null || _adElement_querySelector1 === void 0 ? void 0 : (_adElement_querySelector_textContent = _adElement_querySelector1.textContent) === null || _adElement_querySelector_textContent === void 0 ? void 0 : _adElement_querySelector_textContent.trim();
685
+ var wrapperUri = (_adElement_querySelector2 = adElement.querySelector("VASTAdTagURI")) === null || _adElement_querySelector2 === void 0 ? void 0 : (_adElement_querySelector_textContent1 = _adElement_querySelector2.textContent) === null || _adElement_querySelector_textContent1 === void 0 ? void 0 : _adElement_querySelector_textContent1.trim();
686
+ if (wrapperUri) {
687
+ return {
688
+ type: "wrapper",
689
+ vastAdTagUri: wrapperUri,
690
+ trackingUrls: extractTrackingUrls(adElement),
691
+ clickThrough: clickThrough
692
+ };
693
+ }
694
+ var isNoAdAvailable = adId === "empty" || title.toLowerCase().includes("no ad available") || title.toLowerCase() === "no ad available";
695
+ var durationText = ((_adElement_querySelector3 = adElement.querySelector("Duration")) === null || _adElement_querySelector3 === void 0 ? void 0 : _adElement_querySelector3.textContent) || "00:00:30";
696
+ var durationParts = durationText.split(":");
697
+ var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
698
+ var mediaFileElements = adElement.querySelectorAll("MediaFile");
699
+ var mediaFiles = [];
700
+ mediaFileElements.forEach(function(mf) {
701
+ var _mf_textContent;
702
+ var type = mf.getAttribute("type") || "";
703
+ var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
704
+ var width = mf.getAttribute("width") || "";
705
+ var height = mf.getAttribute("height") || "";
706
+ if (!url) return;
707
+ var bitrateAttr = mf.getAttribute("bitrate");
708
+ var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
709
+ var mediaFile = {
710
+ url: url,
711
+ type: type,
712
+ width: parseInt(width || "1920", 10),
713
+ height: parseInt(height || "1080", 10),
714
+ bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
715
+ };
716
+ if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
717
+ mediaFiles.push(mediaFile);
718
+ }
719
+ });
720
+ if (mediaFiles.length === 0) {
721
+ if (isNoAdAvailable) {
722
+ console.warn("[HlsAdPlayer] Pod <Ad> indicates no ad available");
723
+ } else {
724
+ console.warn("[HlsAdPlayer] No supported media files in pod <Ad>");
725
+ }
726
+ return {
727
+ type: "empty"
728
+ };
729
+ }
730
+ return {
731
+ type: "inline",
732
+ ad: {
733
+ id: adId,
734
+ title: title,
735
+ duration: duration,
736
+ mediaFiles: mediaFiles,
737
+ trackingUrls: extractTrackingUrls(adElement),
738
+ clickThrough: clickThrough
739
+ }
740
+ };
741
+ } catch (error) {
742
+ console.error("[HlsAdPlayer] Error parsing pod <Ad> element:", error);
743
+ return {
744
+ type: "empty"
745
+ };
746
+ }
747
+ }
677
748
  function fetchVastXml(vastTagUrl) {
678
749
  return _async_to_generator(function() {
679
750
  var response;
@@ -750,6 +821,146 @@ function createHlsAdPlayer(contentVideo, options) {
750
821
  });
751
822
  }).apply(this, arguments);
752
823
  }
824
+ function fetchAndParsePod(vastTagUrl) {
825
+ return _async_to_generator(function() {
826
+ var vastXml, parser, xmlDoc, adElements, ads, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, adElement, parsed, resolved, error, err;
827
+ return _ts_generator(this, function(_state) {
828
+ switch(_state.label){
829
+ case 0:
830
+ return [
831
+ 4,
832
+ fetchVastXml(vastTagUrl)
833
+ ];
834
+ case 1:
835
+ vastXml = _state.sent();
836
+ console.log("[HlsAdPlayer] Pod VAST XML received");
837
+ parser = new DOMParser();
838
+ xmlDoc = parser.parseFromString(vastXml, "text/xml");
839
+ if (xmlDoc.querySelector("parsererror")) {
840
+ console.error("[HlsAdPlayer] Pod VAST XML parsing error (malformed)");
841
+ return [
842
+ 2,
843
+ []
844
+ ];
845
+ }
846
+ adElements = Array.from(xmlDoc.querySelectorAll("Ad"));
847
+ if (adElements.length === 0) {
848
+ console.warn("[HlsAdPlayer] No <Ad> elements found in pod VAST response");
849
+ return [
850
+ 2,
851
+ []
852
+ ];
853
+ }
854
+ adElements.sort(function(a, b) {
855
+ var aSeq = parseInt(a.getAttribute("sequence") || "", 10);
856
+ var bSeq = parseInt(b.getAttribute("sequence") || "", 10);
857
+ var aVal = Number.isFinite(aSeq) ? aSeq : Number.MAX_SAFE_INTEGER;
858
+ var bVal = Number.isFinite(bSeq) ? bSeq : Number.MAX_SAFE_INTEGER;
859
+ return aVal - bVal;
860
+ });
861
+ ads = [];
862
+ _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
863
+ _state.label = 2;
864
+ case 2:
865
+ _state.trys.push([
866
+ 2,
867
+ 10,
868
+ 11,
869
+ 12
870
+ ]);
871
+ _iterator = adElements[Symbol.iterator]();
872
+ _state.label = 3;
873
+ case 3:
874
+ if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [
875
+ 3,
876
+ 9
877
+ ];
878
+ adElement = _step.value;
879
+ parsed = parseAdElement(adElement);
880
+ if (!(parsed.type === "inline")) return [
881
+ 3,
882
+ 4
883
+ ];
884
+ ads.push(parsed.ad);
885
+ return [
886
+ 3,
887
+ 8
888
+ ];
889
+ case 4:
890
+ if (!(parsed.type === "wrapper")) return [
891
+ 3,
892
+ 8
893
+ ];
894
+ _state.label = 5;
895
+ case 5:
896
+ _state.trys.push([
897
+ 5,
898
+ 7,
899
+ ,
900
+ 8
901
+ ]);
902
+ return [
903
+ 4,
904
+ fetchAndParseVastAd(parsed.vastAdTagUri, 1, parsed.trackingUrls)
905
+ ];
906
+ case 6:
907
+ resolved = _state.sent();
908
+ if (resolved) {
909
+ ads.push(resolved);
910
+ }
911
+ return [
912
+ 3,
913
+ 8
914
+ ];
915
+ case 7:
916
+ error = _state.sent();
917
+ console.warn("[HlsAdPlayer] Failed to resolve wrapper <Ad> in pod:", error);
918
+ return [
919
+ 3,
920
+ 8
921
+ ];
922
+ case 8:
923
+ _iteratorNormalCompletion = true;
924
+ return [
925
+ 3,
926
+ 3
927
+ ];
928
+ case 9:
929
+ return [
930
+ 3,
931
+ 12
932
+ ];
933
+ case 10:
934
+ err = _state.sent();
935
+ _didIteratorError = true;
936
+ _iteratorError = err;
937
+ return [
938
+ 3,
939
+ 12
940
+ ];
941
+ case 11:
942
+ try {
943
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
944
+ _iterator.return();
945
+ }
946
+ } finally{
947
+ if (_didIteratorError) {
948
+ throw _iteratorError;
949
+ }
950
+ }
951
+ return [
952
+ 7
953
+ ];
954
+ case 12:
955
+ console.log("[HlsAdPlayer] Parsed ".concat(ads.length, " ad(s) from pod VAST response"));
956
+ return [
957
+ 2,
958
+ ads
959
+ ];
960
+ }
961
+ });
962
+ })();
963
+ }
753
964
  function createAdVideoElement() {
754
965
  var video = document.createElement("video");
755
966
  video.style.position = "absolute";
@@ -794,6 +1005,9 @@ function createHlsAdPlayer(contentVideo, options) {
794
1005
  trackingFired.complete = true;
795
1006
  fireTrackingPixels(currentAd.trackingUrls.complete);
796
1007
  console.log("[HlsAdPlayer] Ad completed");
1008
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1009
+ return;
1010
+ }
797
1011
  handleAdComplete();
798
1012
  });
799
1013
  adVideoElement.addEventListener("error", function(e) {
@@ -802,6 +1016,9 @@ function createHlsAdPlayer(contentVideo, options) {
802
1016
  if (currentAd) {
803
1017
  fireTrackingPixels(currentAd.trackingUrls.error);
804
1018
  }
1019
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1020
+ return;
1021
+ }
805
1022
  handleAdError();
806
1023
  });
807
1024
  adVideoElement.addEventListener("volumechange", function() {
@@ -830,6 +1047,94 @@ function createHlsAdPlayer(contentVideo, options) {
830
1047
  delete contentVideo.dataset.stormcloudAdPlaying;
831
1048
  }
832
1049
  }
1050
+ function resetQuartileTracking(preserveImpression) {
1051
+ trackingFired = {
1052
+ impression: preserveImpression ? trackingFired.impression : false,
1053
+ start: false,
1054
+ firstQuartile: false,
1055
+ midpoint: false,
1056
+ thirdQuartile: false,
1057
+ complete: false
1058
+ };
1059
+ }
1060
+ function loadCurrentAdMedia() {
1061
+ if (!adVideoElement || !currentAd) {
1062
+ throw new Error("No ad video element or ad to load");
1063
+ }
1064
+ var mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1065
+ if (!mediaFile) {
1066
+ throw new Error("No media file available for ad");
1067
+ }
1068
+ var isHlsAd = isHlsMediaFile(mediaFile);
1069
+ console.log("[HlsAdPlayer] Loading ".concat(isHlsAd ? "HLS" : "progressive", " ad (pod ").concat(podIndex + 1, "/").concat(podAds.length || 1, ") from: ").concat(mediaFile.url));
1070
+ if (isHlsAd && Hls.isSupported()) {
1071
+ if (adHls) {
1072
+ adHls.destroy();
1073
+ }
1074
+ adHls = new Hls({
1075
+ enableWorker: true,
1076
+ lowLatencyMode: false
1077
+ });
1078
+ adHls.loadSource(mediaFile.url);
1079
+ adHls.attachMedia(adVideoElement);
1080
+ adHls.on(Hls.Events.MANIFEST_PARSED, function() {
1081
+ console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1082
+ adVideoElement.play().catch(function(error) {
1083
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1084
+ handleAdError();
1085
+ });
1086
+ });
1087
+ adHls.on(Hls.Events.ERROR, function(_event, data) {
1088
+ console.error("[HlsAdPlayer] HLS error:", data);
1089
+ if (data.fatal) {
1090
+ handleAdError();
1091
+ }
1092
+ });
1093
+ } else if (isHlsAd && adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1094
+ adVideoElement.src = mediaFile.url;
1095
+ adVideoElement.play().catch(function(error) {
1096
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1097
+ handleAdError();
1098
+ });
1099
+ } else if (!isHlsAd && isProgressiveMediaFile(mediaFile)) {
1100
+ if (adHls) {
1101
+ adHls.destroy();
1102
+ adHls = void 0;
1103
+ }
1104
+ adVideoElement.src = mediaFile.url;
1105
+ adVideoElement.load();
1106
+ adVideoElement.play().catch(function(error) {
1107
+ console.error("[HlsAdPlayer] Error starting progressive ad playback:", error);
1108
+ handleAdError();
1109
+ });
1110
+ } else {
1111
+ throw new Error("Unsupported ad media file type: ".concat(mediaFile.type));
1112
+ }
1113
+ }
1114
+ function advanceToNextPodAd() {
1115
+ if (podIndex >= podAds.length - 1) {
1116
+ return false;
1117
+ }
1118
+ podIndex++;
1119
+ currentAd = podAds[podIndex];
1120
+ resetQuartileTracking(false);
1121
+ if (currentAd) {
1122
+ fireTrackingPixels(currentAd.trackingUrls.impression);
1123
+ trackingFired.impression = true;
1124
+ console.log("[HlsAdPlayer] Advancing to next pod ad (".concat(podIndex + 1, "/").concat(podAds.length, "): ").concat(currentAd.title, ", duration: ").concat(currentAd.duration, "s"));
1125
+ }
1126
+ try {
1127
+ loadCurrentAdMedia();
1128
+ emit("pod_ad_started", {
1129
+ index: podIndex,
1130
+ total: podAds.length
1131
+ });
1132
+ return true;
1133
+ } catch (error) {
1134
+ console.error("[HlsAdPlayer] Failed to load next pod ad:", error);
1135
+ return false;
1136
+ }
1137
+ }
833
1138
  function handleAdComplete() {
834
1139
  console.log("[HlsAdPlayer] Handling ad completion");
835
1140
  adPlaying = false;
@@ -883,7 +1188,7 @@ function createHlsAdPlayer(contentVideo, options) {
883
1188
  },
884
1189
  requestAds: function requestAds(vastTagUrl) {
885
1190
  return _async_to_generator(function() {
886
- var ad, error;
1191
+ var ads, preloaded, error;
887
1192
  return _ts_generator(this, function(_state) {
888
1193
  switch(_state.label){
889
1194
  case 0:
@@ -904,12 +1209,18 @@ function createHlsAdPlayer(contentVideo, options) {
904
1209
  6
905
1210
  ]);
906
1211
  sessionId = generateSessionId();
1212
+ ads = [];
907
1213
  if (!preloadedAds.has(vastTagUrl)) return [
908
1214
  3,
909
1215
  2
910
1216
  ];
911
- ad = preloadedAds.get(vastTagUrl);
1217
+ preloaded = preloadedAds.get(vastTagUrl);
912
1218
  preloadedAds.delete(vastTagUrl);
1219
+ if (preloaded) {
1220
+ ads = [
1221
+ preloaded
1222
+ ];
1223
+ }
913
1224
  console.log("[HlsAdPlayer] Using preloaded VAST response:", vastTagUrl);
914
1225
  return [
915
1226
  3,
@@ -918,13 +1229,13 @@ function createHlsAdPlayer(contentVideo, options) {
918
1229
  case 2:
919
1230
  return [
920
1231
  4,
921
- fetchAndParseVastAd(vastTagUrl)
1232
+ fetchAndParsePod(vastTagUrl)
922
1233
  ];
923
1234
  case 3:
924
- ad = _state.sent();
1235
+ ads = _state.sent();
925
1236
  _state.label = 4;
926
1237
  case 4:
927
- if (!ad) {
1238
+ if (!ads.length) {
928
1239
  console.warn("[HlsAdPlayer] No ads available from VAST response");
929
1240
  emit("ad_error");
930
1241
  return [
@@ -932,10 +1243,14 @@ function createHlsAdPlayer(contentVideo, options) {
932
1243
  Promise.reject(new Error("No ads available from VAST response"))
933
1244
  ];
934
1245
  }
935
- currentAd = ad;
936
- console.log("[HlsAdPlayer] Ad parsed: ".concat(ad.title, ", duration: ").concat(ad.duration, "s"));
937
- fireTrackingPixels(ad.trackingUrls.impression);
938
- trackingFired.impression = true;
1246
+ podAds = ads;
1247
+ podIndex = 0;
1248
+ currentAd = ads[0];
1249
+ console.log("[HlsAdPlayer] Pod parsed: ".concat(ads.length, ' ad(s), first="').concat(currentAd === null || currentAd === void 0 ? void 0 : currentAd.title, '", duration=').concat(currentAd === null || currentAd === void 0 ? void 0 : currentAd.duration, "s"));
1250
+ if (currentAd) {
1251
+ fireTrackingPixels(currentAd.trackingUrls.impression);
1252
+ trackingFired.impression = true;
1253
+ }
939
1254
  return [
940
1255
  2,
941
1256
  Promise.resolve()
@@ -958,7 +1273,7 @@ function createHlsAdPlayer(contentVideo, options) {
958
1273
  },
959
1274
  play: function play() {
960
1275
  return _async_to_generator(function() {
961
- var contentVolume, mediaFile, isHlsAd;
1276
+ var contentVolume;
962
1277
  return _ts_generator(this, function(_state) {
963
1278
  if (!currentAd) {
964
1279
  console.warn("[HlsAdPlayer] Cannot play: No ad loaded (no ads available)");
@@ -974,14 +1289,7 @@ function createHlsAdPlayer(contentVideo, options) {
974
1289
  adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.appendChild(adVideoElement);
975
1290
  setupAdEventListeners();
976
1291
  }
977
- trackingFired = {
978
- impression: trackingFired.impression,
979
- start: false,
980
- firstQuartile: false,
981
- midpoint: false,
982
- thirdQuartile: false,
983
- complete: false
984
- };
1292
+ resetQuartileTracking(true);
985
1293
  contentVolume = contentVideo.volume;
986
1294
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
987
1295
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
@@ -999,60 +1307,12 @@ function createHlsAdPlayer(contentVideo, options) {
999
1307
  adVideoElement.muted = originalMutedState;
1000
1308
  console.log("[HlsAdPlayer] Set ad video volume to ".concat(adVideoElement.volume, ", muted: ").concat(adVideoElement.muted, ", originalMutedState: ").concat(originalMutedState, ", contentVolume: ").concat(contentVolume));
1001
1309
  }
1002
- mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1003
- if (!mediaFile) {
1004
- throw new Error("No media file available for ad");
1005
- }
1006
1310
  if (adContainerEl) {
1007
1311
  adContainerEl.style.display = "flex";
1008
1312
  adContainerEl.style.pointerEvents = "auto";
1009
1313
  }
1010
1314
  emit("content_pause");
1011
- isHlsAd = isHlsMediaFile(mediaFile);
1012
- console.log("[HlsAdPlayer] Loading ".concat(isHlsAd ? "HLS" : "progressive", " ad from: ").concat(mediaFile.url));
1013
- if (isHlsAd && Hls.isSupported()) {
1014
- if (adHls) {
1015
- adHls.destroy();
1016
- }
1017
- adHls = new Hls({
1018
- enableWorker: true,
1019
- lowLatencyMode: false
1020
- });
1021
- adHls.loadSource(mediaFile.url);
1022
- adHls.attachMedia(adVideoElement);
1023
- adHls.on(Hls.Events.MANIFEST_PARSED, function() {
1024
- console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1025
- adVideoElement.play().catch(function(error) {
1026
- console.error("[HlsAdPlayer] Error starting ad playback:", error);
1027
- handleAdError();
1028
- });
1029
- });
1030
- adHls.on(Hls.Events.ERROR, function(event, data) {
1031
- console.error("[HlsAdPlayer] HLS error:", data);
1032
- if (data.fatal) {
1033
- handleAdError();
1034
- }
1035
- });
1036
- } else if (isHlsAd && adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1037
- adVideoElement.src = mediaFile.url;
1038
- adVideoElement.play().catch(function(error) {
1039
- console.error("[HlsAdPlayer] Error starting ad playback:", error);
1040
- handleAdError();
1041
- });
1042
- } else if (!isHlsAd && isProgressiveMediaFile(mediaFile)) {
1043
- if (adHls) {
1044
- adHls.destroy();
1045
- adHls = void 0;
1046
- }
1047
- adVideoElement.src = mediaFile.url;
1048
- adVideoElement.load();
1049
- adVideoElement.play().catch(function(error) {
1050
- console.error("[HlsAdPlayer] Error starting progressive ad playback:", error);
1051
- handleAdError();
1052
- });
1053
- } else {
1054
- throw new Error("Unsupported ad media file type: ".concat(mediaFile.type));
1055
- }
1315
+ loadCurrentAdMedia();
1056
1316
  return [
1057
1317
  2,
1058
1318
  Promise.resolve()
@@ -1127,6 +1387,8 @@ function createHlsAdPlayer(contentVideo, options) {
1127
1387
  adVideoElement.src = "";
1128
1388
  }
1129
1389
  currentAd = void 0;
1390
+ podAds = [];
1391
+ podIndex = 0;
1130
1392
  return [
1131
1393
  2
1132
1394
  ];
@@ -1176,6 +1438,8 @@ function createHlsAdPlayer(contentVideo, options) {
1176
1438
  }
1177
1439
  adContainerEl = void 0;
1178
1440
  currentAd = void 0;
1441
+ podAds = [];
1442
+ podIndex = 0;
1179
1443
  listeners.clear();
1180
1444
  preloadedAds.clear();
1181
1445
  preloadingAds.clear();
@@ -1267,6 +1531,14 @@ function createHlsAdPlayer(contentVideo, options) {
1267
1531
  },
1268
1532
  getLoadedAdId: function getLoadedAdId() {
1269
1533
  return currentAd === null || currentAd === void 0 ? void 0 : currentAd.id;
1534
+ },
1535
+ getPodAdCount: function getPodAdCount() {
1536
+ return podAds.length;
1537
+ },
1538
+ getPodTotalDurationMs: function getPodTotalDurationMs() {
1539
+ return podAds.reduce(function(total, ad) {
1540
+ return total + (ad.duration > 0 ? ad.duration * 1e3 : 0);
1541
+ }, 0);
1270
1542
  }
1271
1543
  };
1272
1544
  }
@@ -1670,38 +1942,108 @@ function getOrCreateStoredUuid(storageKey) {
1670
1942
  if (typeof window === "undefined") {
1671
1943
  return void 0;
1672
1944
  }
1673
- var existing = readStoredString(storageKey);
1945
+ var existing = readPersistentId(storageKey);
1674
1946
  if (existing) {
1947
+ writePersistentId(storageKey, existing);
1675
1948
  return existing;
1676
1949
  }
1677
1950
  var id = createUuid();
1678
- try {
1679
- var _window_localStorage;
1680
- (_window_localStorage = window.localStorage) === null || _window_localStorage === void 0 ? void 0 : _window_localStorage.setItem(storageKey, id);
1681
- } catch (unused) {}
1951
+ writePersistentId(storageKey, id);
1682
1952
  return id;
1683
1953
  }
1684
1954
  function getOrCreateCtvSessionId() {
1685
- var existing = readStoredString(CTV_SESSION_STORAGE_KEY);
1955
+ var existing = readPersistentId(CTV_SESSION_STORAGE_KEY);
1686
1956
  if (existing) {
1957
+ writePersistentId(CTV_SESSION_STORAGE_KEY, existing);
1687
1958
  return existing;
1688
1959
  }
1689
1960
  var sessionId = createUuid();
1961
+ writePersistentId(CTV_SESSION_STORAGE_KEY, sessionId);
1962
+ return sessionId;
1963
+ }
1964
+ function readPersistentId(storageKey) {
1965
+ return readStoredString(storageKey) || readCookie(storageKey);
1966
+ }
1967
+ function writePersistentId(storageKey, value) {
1690
1968
  try {
1691
1969
  var _window_localStorage;
1692
- (_window_localStorage = window.localStorage) === null || _window_localStorage === void 0 ? void 0 : _window_localStorage.setItem(CTV_SESSION_STORAGE_KEY, sessionId);
1970
+ (_window_localStorage = window.localStorage) === null || _window_localStorage === void 0 ? void 0 : _window_localStorage.setItem(storageKey, value);
1971
+ } catch (unused) {}
1972
+ writeCookie(storageKey, value);
1973
+ }
1974
+ var PERSISTENT_ID_COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5;
1975
+ function readCookie(name) {
1976
+ if (typeof document === "undefined") {
1977
+ return void 0;
1978
+ }
1979
+ try {
1980
+ var prefix = encodeURIComponent(name) + "=";
1981
+ var parts = document.cookie ? document.cookie.split(";") : [];
1982
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1983
+ try {
1984
+ for(var _iterator = parts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1985
+ var part = _step.value;
1986
+ var entry = part.trim();
1987
+ if (entry.startsWith(prefix)) {
1988
+ return decodeURIComponent(entry.slice(prefix.length)) || void 0;
1989
+ }
1990
+ }
1991
+ } catch (err) {
1992
+ _didIteratorError = true;
1993
+ _iteratorError = err;
1994
+ } finally{
1995
+ try {
1996
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1997
+ _iterator.return();
1998
+ }
1999
+ } finally{
2000
+ if (_didIteratorError) {
2001
+ throw _iteratorError;
2002
+ }
2003
+ }
2004
+ }
2005
+ } catch (unused) {}
2006
+ return void 0;
2007
+ }
2008
+ function writeCookie(name, value) {
2009
+ if (typeof document === "undefined") {
2010
+ return;
2011
+ }
2012
+ try {
2013
+ document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + ";path=/;max-age=" + PERSISTENT_ID_COOKIE_MAX_AGE + ";SameSite=Lax";
1693
2014
  } catch (unused) {}
1694
- return sessionId;
1695
2015
  }
1696
2016
  function createUuid() {
2017
+ var _bytes_, _bytes_1;
1697
2018
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1698
2019
  return crypto.randomUUID();
1699
2020
  }
1700
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(char) {
1701
- var value = Math.floor(Math.random() * 16);
1702
- var nibble = char === "x" ? value : value & 3 | 8;
1703
- return nibble.toString(16);
1704
- });
2021
+ var bytes = new Uint8Array(16);
2022
+ if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
2023
+ crypto.getRandomValues(bytes);
2024
+ } else {
2025
+ var seed = Date.now() ^ Math.floor(performanceNow() * 1e3);
2026
+ for(var i = 0; i < 16; i++){
2027
+ seed = seed * 1103515245 + 12345 & 2147483647;
2028
+ bytes[i] = (seed ^ Math.floor(Math.random() * 256)) & 255;
2029
+ }
2030
+ }
2031
+ bytes[6] = ((_bytes_ = bytes[6]) !== null && _bytes_ !== void 0 ? _bytes_ : 0) & 15 | 64;
2032
+ bytes[8] = ((_bytes_1 = bytes[8]) !== null && _bytes_1 !== void 0 ? _bytes_1 : 0) & 63 | 128;
2033
+ var hex = "";
2034
+ for(var i1 = 0; i1 < 16; i1++){
2035
+ var _bytes_i;
2036
+ hex += ((_bytes_i = bytes[i1]) !== null && _bytes_i !== void 0 ? _bytes_i : 0).toString(16).padStart(2, "0");
2037
+ }
2038
+ return hex.slice(0, 8) + "-" + hex.slice(8, 12) + "-" + hex.slice(12, 16) + "-" + hex.slice(16, 20) + "-" + hex.slice(20, 32);
2039
+ }
2040
+ function performanceNow() {
2041
+ try {
2042
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
2043
+ return performance.now();
2044
+ }
2045
+ } catch (unused) {}
2046
+ return 0;
1705
2047
  }
1706
2048
  function readPlatformNativeDeviceId() {
1707
2049
  if (typeof window === "undefined") return {};
@@ -3121,7 +3463,20 @@ function applyVastMacros(baseUrl, ctx) {
3121
3463
  if (ctx.pod != null) {
3122
3464
  params.set("pod", String(ctx.pod));
3123
3465
  }
3124
- if (ctx.adPosition != null) {
3466
+ var optimizedPodMode = ctx.podMaxAds != null || ctx.podMinDurationMs != null || ctx.podMaxDurationMs != null;
3467
+ if (optimizedPodMode) {
3468
+ params.delete("ppos");
3469
+ params.delete("lip");
3470
+ if (ctx.podMinDurationMs != null) {
3471
+ params.set("pmnd", String(Math.max(0, Math.round(ctx.podMinDurationMs))));
3472
+ }
3473
+ if (ctx.podMaxDurationMs != null) {
3474
+ params.set("pmxd", String(Math.max(0, Math.round(ctx.podMaxDurationMs))));
3475
+ }
3476
+ if (ctx.podMaxAds != null) {
3477
+ params.set("pmad", String(Math.max(1, Math.round(ctx.podMaxAds))));
3478
+ }
3479
+ } else if (ctx.adPosition != null) {
3125
3480
  params.set("ppos", String(ctx.adPosition));
3126
3481
  }
3127
3482
  var requestUrl = ctx.contentUrl || ctx.pageUrl;
@@ -4489,6 +4844,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
4489
4844
  this.podAssignedByPrefetch = false;
4490
4845
  this.adRequestPositionInBreak = 0;
4491
4846
  this.lastHeartbeatTime = 0;
4847
+ this.defaultPodMaxAds = 6;
4492
4848
  this.config = config;
4493
4849
  this.video = video;
4494
4850
  }
@@ -4944,6 +5300,52 @@ var AdConfigManager = /*#__PURE__*/ function() {
4944
5300
  return urls;
4945
5301
  }
4946
5302
  },
5303
+ {
5304
+ key: "resolvePodParams",
5305
+ value: function resolvePodParams(breakDurationMs) {
5306
+ var maxDurationMs = breakDurationMs != null && breakDurationMs > 0 ? breakDurationMs : this.config.podMaxDurationMs;
5307
+ var maxAds = this.config.podMaxAds && this.config.podMaxAds > 0 ? this.config.podMaxAds : this.apiNumberAds && this.apiNumberAds > 0 ? this.apiNumberAds : this.defaultPodMaxAds;
5308
+ var minDurationMs = this.config.podExactDuration && maxDurationMs != null ? maxDurationMs : 0;
5309
+ return {
5310
+ maxAds: maxAds,
5311
+ minDurationMs: minDurationMs,
5312
+ maxDurationMs: maxDurationMs
5313
+ };
5314
+ }
5315
+ },
5316
+ {
5317
+ key: "generatePodVastUrl",
5318
+ value: function generatePodVastUrl(baseUrl, podParams, adPlayer, palNonce, inAdBreak) {
5319
+ var _podParams_maxDurationMs;
5320
+ var adWillPlayMuted = inAdBreak ? adPlayer.getOriginalMutedState() : this.video.muted;
5321
+ var envSignals = resolveVastEnvironmentSignals(!!this.config.ctvAdRequest);
5322
+ var urlWithMacros = applyVastMacros(baseUrl, {
5323
+ correlator: generateCorrelator(),
5324
+ streamCorrelator: this.streamCorrelator,
5325
+ pod: this.podCounter > 0 ? this.podCounter : void 0,
5326
+ podMaxAds: podParams.maxAds,
5327
+ podMinDurationMs: podParams.minDurationMs,
5328
+ podMaxDurationMs: podParams.maxDurationMs,
5329
+ isCtv: envSignals.isCtv,
5330
+ contentUrl: envSignals.contentUrl,
5331
+ pageUrl: !envSignals.isCtv && typeof window !== "undefined" ? window.location.href : void 0,
5332
+ adWillPlayMuted: adWillPlayMuted,
5333
+ adWillAutoPlay: !!this.config.autoplay,
5334
+ appId: envSignals.appId,
5335
+ appName: envSignals.appName,
5336
+ sessionId: envSignals.sessionId,
5337
+ deviceId: envSignals.deviceId,
5338
+ deviceIdType: envSignals.deviceIdType,
5339
+ limitAdTracking: envSignals.limitAdTracking,
5340
+ deviceTypeHint: envSignals.deviceTypeHint,
5341
+ adTest: this.config.adTest,
5342
+ consent: this.consentSignals
5343
+ });
5344
+ var finalUrl = palNonce.injectNonce(urlWithMacros);
5345
+ console.log("[StormcloudVideoPlayer] Resolved optimized-pod request URL (pod=".concat(this.podCounter, ", pmad=").concat(podParams.maxAds, ", pmnd=").concat(podParams.minDurationMs, ", pmxd=").concat((_podParams_maxDurationMs = podParams.maxDurationMs) !== null && _podParams_maxDurationMs !== void 0 ? _podParams_maxDurationMs : "?", "):"), finalUrl);
5346
+ return finalUrl;
5347
+ }
5348
+ },
4947
5349
  {
4948
5350
  // --- Tracking ---
4949
5351
  key: "initializeTracking",
@@ -5250,6 +5652,13 @@ var AdTimingService = /*#__PURE__*/ function() {
5250
5652
  this.lastAdRequestTime = Date.now();
5251
5653
  }
5252
5654
  },
5655
+ {
5656
+ key: "notePodAdStarted",
5657
+ value: function notePodAdStarted() {
5658
+ this.currentAdIndex++;
5659
+ this.totalAdsInBreak = this.currentAdIndex;
5660
+ }
5661
+ },
5253
5662
  {
5254
5663
  key: "getRemainingAdMs",
5255
5664
  value: function getRemainingAdMs() {
@@ -6762,13 +7171,26 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6762
7171
  }
6763
7172
  },
6764
7173
  {
6765
- // ───────────────────────────────────────────────────────────────
7174
+ key: "optimizedPodsEnabled",
7175
+ get: // ───────────────────────────────────────────────────────────────
6766
7176
  // IMA event listeners
6767
7177
  // ───────────────────────────────────────────────────────────────
7178
+ function get() {
7179
+ return !!this.host.config.optimizedPods;
7180
+ }
7181
+ },
7182
+ {
6768
7183
  key: "attachImaEventListeners",
6769
7184
  value: function attachImaEventListeners() {
6770
7185
  var _this = this;
6771
7186
  var adPlayer = this.host.getAdPlayer();
7187
+ adPlayer.on("pod_ad_started", function(payload) {
7188
+ _this.timing.notePodAdStarted();
7189
+ if (_this.debug) {
7190
+ var _ref;
7191
+ console.log("[POD] Playing pod ad ".concat((payload === null || payload === void 0 ? void 0 : payload.index) != null ? payload.index + 1 : "?", "/").concat((_ref = payload === null || payload === void 0 ? void 0 : payload.total) !== null && _ref !== void 0 ? _ref : "?"));
7192
+ }
7193
+ });
6772
7194
  adPlayer.on("all_ads_completed", function() {
6773
7195
  sendAdImpressionTracking(_this.host.config.licenseKey, {
6774
7196
  source: "hls",
@@ -6926,7 +7348,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6926
7348
  // ───────────────────────────────────────────────────────────────
6927
7349
  function handleAdStart(_marker) {
6928
7350
  return _async_to_generator(function() {
6929
- var scheduled, tags, baseVastUrl, adBreakDurationMs, mode, currentMuted, currentVolume, firstAdUrl, usePreloadedAd, preloadedController, preloaded, firstAdUrlArray, adPlayer, preservedMuted, preservedVolume, adVolume, adPlayer1, adVolume1, error, fallbackPreloaded, ap, adVolume2, fallbackError;
7351
+ var scheduled, tags, baseVastUrl, adBreakDurationMs, mode, currentMuted, currentVolume, podPlayed, firstAdUrl, usePreloadedAd, preloadedController, preloaded, firstAdUrlArray, adPlayer, preservedMuted, preservedVolume, adVolume, adPlayer1, adVolume1, error, fallbackPreloaded, ap, adVolume2, fallbackError;
6930
7352
  return _ts_generator(this, function(_state) {
6931
7353
  switch(_state.label){
6932
7354
  case 0:
@@ -6973,6 +7395,26 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6973
7395
  if (this.timing.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
6974
7396
  this.timing.expectedAdBreakDurationMs = adBreakDurationMs;
6975
7397
  }
7398
+ if (!this.optimizedPodsEnabled) return [
7399
+ 3,
7400
+ 2
7401
+ ];
7402
+ return [
7403
+ 4,
7404
+ this.startOptimizedPod(baseVastUrl, adBreakDurationMs, currentMuted, currentVolume)
7405
+ ];
7406
+ case 1:
7407
+ podPlayed = _state.sent();
7408
+ if (podPlayed) {
7409
+ return [
7410
+ 2
7411
+ ];
7412
+ }
7413
+ if (this.debug) {
7414
+ console.log("[POD] Optimized pod unavailable, falling back to sequential single-ad path");
7415
+ }
7416
+ _state.label = 2;
7417
+ case 2:
6976
7418
  usePreloadedAd = false;
6977
7419
  preloaded = this.preloadPool.getPreloadedAd();
6978
7420
  if (preloaded) {
@@ -7014,21 +7456,21 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7014
7456
  if (this.debug) {
7015
7457
  console.log("[CONTINUOUS-FETCH] First VAST URL:", firstAdUrl);
7016
7458
  }
7017
- _state.label = 1;
7018
- case 1:
7459
+ _state.label = 3;
7460
+ case 3:
7019
7461
  _state.trys.push([
7020
- 1,
7021
- 12,
7462
+ 3,
7463
+ 14,
7022
7464
  ,
7023
- 20
7465
+ 22
7024
7466
  ]);
7025
7467
  if (!(usePreloadedAd && preloadedController)) return [
7026
7468
  3,
7027
- 5
7469
+ 7
7028
7470
  ];
7029
7471
  if (!!this.timing.ensureLoadedAdFitsBudget(preloadedController)) return [
7030
7472
  3,
7031
- 3
7473
+ 5
7032
7474
  ];
7033
7475
  this.timing.rejectLoadedAdForDuration(preloadedController);
7034
7476
  this.startContinuousFetching(baseVastUrl);
@@ -7036,12 +7478,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7036
7478
  4,
7037
7479
  this.tryNextAvailableAdWithRateLimit()
7038
7480
  ];
7039
- case 2:
7481
+ case 4:
7040
7482
  _state.sent();
7041
7483
  return [
7042
7484
  2
7043
7485
  ];
7044
- case 3:
7486
+ case 5:
7045
7487
  adPlayer = this.host.getAdPlayer();
7046
7488
  preservedMuted = adPlayer.getOriginalMutedState();
7047
7489
  preservedVolume = adPlayer.getOriginalVolume();
@@ -7066,7 +7508,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7066
7508
  4,
7067
7509
  preloadedController.play()
7068
7510
  ];
7069
- case 4:
7511
+ case 6:
7070
7512
  _state.sent();
7071
7513
  this.timing.markAdStarted();
7072
7514
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7076,22 +7518,22 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7076
7518
  preloadedController.setAdVolume(adVolume);
7077
7519
  return [
7078
7520
  3,
7079
- 11
7521
+ 13
7080
7522
  ];
7081
- case 5:
7523
+ case 7:
7082
7524
  adPlayer1 = this.host.getAdPlayer();
7083
7525
  return [
7084
7526
  4,
7085
7527
  this.timing.enforceGlobalRateLimit()
7086
7528
  ];
7087
- case 6:
7529
+ case 8:
7088
7530
  _state.sent();
7089
7531
  this.timing.lastAdRequestTime = Date.now();
7090
7532
  return [
7091
7533
  4,
7092
7534
  adPlayer1.requestAds(firstAdUrl)
7093
7535
  ];
7094
- case 7:
7536
+ case 9:
7095
7537
  _state.sent();
7096
7538
  sendAdLoadedTracking(this.host.config.licenseKey, {
7097
7539
  source: "hls",
@@ -7103,7 +7545,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7103
7545
  }
7104
7546
  if (!!this.timing.ensureLoadedAdFitsBudget(adPlayer1)) return [
7105
7547
  3,
7106
- 9
7548
+ 11
7107
7549
  ];
7108
7550
  this.timing.rejectLoadedAdForDuration(adPlayer1);
7109
7551
  this.recreateAdController();
@@ -7112,12 +7554,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7112
7554
  4,
7113
7555
  this.tryNextAvailableAdWithRateLimit()
7114
7556
  ];
7115
- case 8:
7557
+ case 10:
7116
7558
  _state.sent();
7117
7559
  return [
7118
7560
  2
7119
7561
  ];
7120
- case 9:
7562
+ case 11:
7121
7563
  this.timing.consecutiveFailures = 0;
7122
7564
  this.startContinuousFetching(baseVastUrl);
7123
7565
  if (!this.preloadPool.active) {
@@ -7127,7 +7569,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7127
7569
  4,
7128
7570
  adPlayer1.play()
7129
7571
  ];
7130
- case 10:
7572
+ case 12:
7131
7573
  _state.sent();
7132
7574
  this.timing.markAdStarted();
7133
7575
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7135,40 +7577,40 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7135
7577
  }
7136
7578
  adVolume1 = currentMuted ? 0 : currentVolume;
7137
7579
  adPlayer1.setAdVolume(adVolume1);
7138
- _state.label = 11;
7139
- case 11:
7580
+ _state.label = 13;
7581
+ case 13:
7140
7582
  return [
7141
7583
  3,
7142
- 20
7584
+ 22
7143
7585
  ];
7144
- case 12:
7586
+ case 14:
7145
7587
  error = _state.sent();
7146
7588
  if (this.debug) {
7147
7589
  console.warn("[CONTINUOUS-FETCH] First ad request failed:", error);
7148
7590
  }
7149
7591
  if (!!usePreloadedAd) return [
7150
7592
  3,
7151
- 18
7593
+ 20
7152
7594
  ];
7153
7595
  fallbackPreloaded = this.preloadPool.getPreloadedAd();
7154
7596
  if (!fallbackPreloaded) return [
7155
7597
  3,
7156
- 18
7598
+ 20
7157
7599
  ];
7158
7600
  if (this.debug) {
7159
7601
  console.log("[CONTINUOUS-FETCH] First ad failed, using preloaded fallback");
7160
7602
  }
7161
- _state.label = 13;
7162
- case 13:
7603
+ _state.label = 15;
7604
+ case 15:
7163
7605
  _state.trys.push([
7164
- 13,
7165
- 17,
7606
+ 15,
7607
+ 19,
7166
7608
  ,
7167
- 18
7609
+ 20
7168
7610
  ]);
7169
7611
  if (!!this.timing.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
7170
7612
  3,
7171
- 15
7613
+ 17
7172
7614
  ];
7173
7615
  this.timing.rejectLoadedAdForDuration(fallbackPreloaded.adController);
7174
7616
  this.startContinuousFetching(baseVastUrl);
@@ -7176,12 +7618,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7176
7618
  4,
7177
7619
  this.tryNextAvailableAdWithRateLimit()
7178
7620
  ];
7179
- case 14:
7621
+ case 16:
7180
7622
  _state.sent();
7181
7623
  return [
7182
7624
  2
7183
7625
  ];
7184
- case 15:
7626
+ case 17:
7185
7627
  this.swapToPreloadedAdPlayer(fallbackPreloaded.adController);
7186
7628
  this.timing.consecutiveFailures = 0;
7187
7629
  this.startContinuousFetching(baseVastUrl);
@@ -7194,7 +7636,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7194
7636
  4,
7195
7637
  ap.play()
7196
7638
  ];
7197
- case 16:
7639
+ case 18:
7198
7640
  _state.sent();
7199
7641
  this.timing.markAdStarted();
7200
7642
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7205,16 +7647,16 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7205
7647
  return [
7206
7648
  2
7207
7649
  ];
7208
- case 17:
7650
+ case 19:
7209
7651
  fallbackError = _state.sent();
7210
7652
  if (this.debug) {
7211
7653
  console.warn("[CONTINUOUS-FETCH] Preloaded fallback also failed:", fallbackError);
7212
7654
  }
7213
7655
  return [
7214
7656
  3,
7215
- 18
7657
+ 20
7216
7658
  ];
7217
- case 18:
7659
+ case 20:
7218
7660
  if (this.timing.isTemporaryAdError(error)) {
7219
7661
  this.timing.temporaryFailureUrls.set(firstAdUrl, Date.now());
7220
7662
  if (this.debug) {
@@ -7232,13 +7674,137 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7232
7674
  4,
7233
7675
  this.tryNextAvailableAdWithRateLimit()
7234
7676
  ];
7235
- case 19:
7677
+ case 21:
7236
7678
  _state.sent();
7237
7679
  return [
7238
7680
  3,
7239
- 20
7681
+ 22
7240
7682
  ];
7241
- case 20:
7683
+ case 22:
7684
+ return [
7685
+ 2
7686
+ ];
7687
+ }
7688
+ });
7689
+ }).call(this);
7690
+ }
7691
+ },
7692
+ {
7693
+ key: "startOptimizedPod",
7694
+ value: // ───────────────────────────────────────────────────────────────
7695
+ // Optimized pod (single multi-ad request)
7696
+ // ───────────────────────────────────────────────────────────────
7697
+ function startOptimizedPod(baseVastUrl, breakDurationMs, currentMuted, currentVolume) {
7698
+ return _async_to_generator(function() {
7699
+ var podUrl, adPlayer, requestToken, _ref, _adPlayer_getPodAdCount, podCount, error;
7700
+ return _ts_generator(this, function(_state) {
7701
+ switch(_state.label){
7702
+ case 0:
7703
+ if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {
7704
+ podUrl = this.pendingAdBreak.vastUrls[0];
7705
+ this.clearPendingAdBreak();
7706
+ } else {
7707
+ podUrl = this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);
7708
+ }
7709
+ if (!podUrl) {
7710
+ return [
7711
+ 2,
7712
+ false
7713
+ ];
7714
+ }
7715
+ this.continuousFetchingActive = false;
7716
+ this.recreateAdController();
7717
+ adPlayer = this.host.getAdPlayer();
7718
+ requestToken = ++this.timing.adRequestTokenCounter;
7719
+ this.timing.activeAdRequestToken = requestToken;
7720
+ this.timing.startAdRequestWatchdog(requestToken);
7721
+ _state.label = 1;
7722
+ case 1:
7723
+ _state.trys.push([
7724
+ 1,
7725
+ 5,
7726
+ ,
7727
+ 6
7728
+ ]);
7729
+ return [
7730
+ 4,
7731
+ this.timing.enforceGlobalRateLimit()
7732
+ ];
7733
+ case 2:
7734
+ _state.sent();
7735
+ this.timing.lastAdRequestTime = Date.now();
7736
+ return [
7737
+ 4,
7738
+ adPlayer.requestAds(podUrl)
7739
+ ];
7740
+ case 3:
7741
+ _state.sent();
7742
+ this.timing.clearAdRequestWatchdog();
7743
+ if (this.timing.activeAdRequestToken !== requestToken) {
7744
+ return [
7745
+ 2,
7746
+ true
7747
+ ];
7748
+ }
7749
+ podCount = (_ref = (_adPlayer_getPodAdCount = adPlayer.getPodAdCount) === null || _adPlayer_getPodAdCount === void 0 ? void 0 : _adPlayer_getPodAdCount.call(adPlayer)) !== null && _ref !== void 0 ? _ref : 1;
7750
+ if (podCount < 1) {
7751
+ return [
7752
+ 2,
7753
+ false
7754
+ ];
7755
+ }
7756
+ if (this.debug) {
7757
+ console.log(podCount === 1 ? "[POD] Response contained a single ad; playing it and enabling sequential top-up" : "[POD] Optimized pod returned ".concat(podCount, " ads; playing pod in one controller"));
7758
+ }
7759
+ sendAdLoadedTracking(this.host.config.licenseKey, {
7760
+ source: "hls",
7761
+ vastUrl: podUrl,
7762
+ timestamp: /* @__PURE__ */ new Date().toISOString()
7763
+ }, this.analyticsContext).catch(function() {});
7764
+ this.timing.startAdFailsafeTimer(requestToken);
7765
+ return [
7766
+ 4,
7767
+ adPlayer.play()
7768
+ ];
7769
+ case 4:
7770
+ _state.sent();
7771
+ this.timing.markAdStarted();
7772
+ if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
7773
+ this.timing.scheduleAdStopAtBreakBoundary();
7774
+ }
7775
+ adPlayer.setAdVolume(currentMuted ? 0 : currentVolume);
7776
+ this.timing.consecutiveFailures = 0;
7777
+ if (podCount === 1) {
7778
+ this.continuousFetchingActive = true;
7779
+ this.startContinuousFetching(baseVastUrl);
7780
+ if (!this.preloadPool.active) {
7781
+ this.preloadPool.startPreloadPool(baseVastUrl, []);
7782
+ }
7783
+ }
7784
+ return [
7785
+ 2,
7786
+ true
7787
+ ];
7788
+ case 5:
7789
+ error = _state.sent();
7790
+ this.timing.clearAdRequestWatchdog();
7791
+ this.timing.clearAdFailsafeTimer();
7792
+ if (this.timing.activeAdRequestToken === requestToken) {
7793
+ this.timing.activeAdRequestToken = null;
7794
+ }
7795
+ if (this.debug) {
7796
+ console.warn("[POD] Optimized pod request/play failed:", error);
7797
+ }
7798
+ if (this.timing.isTemporaryAdError(error)) {
7799
+ this.timing.temporaryFailureUrls.set(podUrl, Date.now());
7800
+ }
7801
+ this.continuousFetchingActive = true;
7802
+ this.recreateAdController();
7803
+ return [
7804
+ 2,
7805
+ false
7806
+ ];
7807
+ case 6:
7242
7808
  return [
7243
7809
  2
7244
7810
  ];
@@ -8321,6 +8887,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8321
8887
  },
8322
8888
  generateVastUrls: function generateVastUrls(base, count) {
8323
8889
  return _this.generateVastUrls(base, count);
8890
+ },
8891
+ generatePodVastUrl: function generatePodVastUrl(base, breakDurationMs) {
8892
+ return _this.generatePodVastUrl(base, breakDurationMs);
8324
8893
  }
8325
8894
  }, this.timing, this.preloadPool, this.adConfig, this.cueManager, this.placeholder);
8326
8895
  }
@@ -8353,6 +8922,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8353
8922
  return this.adConfig.generateVastUrlsWithCorrelators(baseUrl, count, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8354
8923
  }
8355
8924
  },
8925
+ {
8926
+ key: "generatePodVastUrl",
8927
+ value: function generatePodVastUrl(baseUrl, breakDurationMs) {
8928
+ var podParams = this.adConfig.resolvePodParams(breakDurationMs);
8929
+ return this.adConfig.generatePodVastUrl(baseUrl, podParams, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8930
+ }
8931
+ },
8356
8932
  {
8357
8933
  key: "load",
8358
8934
  value: // ───────────────────────────────────────────────────────────────
@@ -8741,7 +9317,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8741
9317
  }
8742
9318
  this.adConfig.beginNewAdPod();
8743
9319
  this.adConfig.podAssignedByPrefetch = true;
8744
- var generatedUrls = this.generateVastUrls(baseVastUrl, 1);
9320
+ var optimizedPods = !!this.config.optimizedPods;
9321
+ var breakDurationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : void 0;
9322
+ var generatedUrls = optimizedPods ? [
9323
+ this.generatePodVastUrl(baseVastUrl, breakDurationMs)
9324
+ ] : this.generateVastUrls(baseVastUrl, 1);
8745
9325
  this.adBreak.pendingAdBreak = _object_spread_props(_object_spread({
8746
9326
  marker: marker
8747
9327
  }, fragmentSn !== void 0 ? {
@@ -8763,7 +9343,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8763
9343
  }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8764
9344
  if (this.debug) {
8765
9345
  console.log("[PREFETCH] Starting ad prefetch for upcoming ad break");
8766
- console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
9346
+ console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " ").concat(optimizedPods ? "pod" : "VAST", " URL(s)"));
9347
+ }
9348
+ if (optimizedPods) {
9349
+ if (this.debug) {
9350
+ console.log("[PREFETCH] Optimized pods enabled - skipping single-ad preload pool");
9351
+ }
9352
+ return;
8767
9353
  }
8768
9354
  this.preloadPool.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
8769
9355
  }