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
@@ -361,6 +361,8 @@ function createHlsAdPlayer(contentVideo, options) {
361
361
  var adHls;
362
362
  var adContainerEl;
363
363
  var currentAd;
364
+ var podAds = [];
365
+ var podIndex = 0;
364
366
  var sessionId;
365
367
  var preloadedAds = /* @__PURE__ */ new Map();
366
368
  var preloadingAds = /* @__PURE__ */ new Map();
@@ -525,14 +527,14 @@ function createHlsAdPlayer(contentVideo, options) {
525
527
  error: []
526
528
  };
527
529
  }
528
- function extractTrackingUrls(xmlDoc) {
530
+ function extractTrackingUrls(scope) {
529
531
  var trackingUrls = createEmptyTrackingUrls();
530
- xmlDoc.querySelectorAll("Impression").forEach(function(el) {
532
+ scope.querySelectorAll("Impression").forEach(function(el) {
531
533
  var _el_textContent;
532
534
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
533
535
  if (url) trackingUrls.impression.push(url);
534
536
  });
535
- xmlDoc.querySelectorAll("Tracking").forEach(function(el) {
537
+ scope.querySelectorAll("Tracking").forEach(function(el) {
536
538
  var _el_textContent;
537
539
  var event = el.getAttribute("event");
538
540
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
@@ -543,7 +545,7 @@ function createHlsAdPlayer(contentVideo, options) {
543
545
  }
544
546
  }
545
547
  });
546
- xmlDoc.querySelectorAll("Error").forEach(function(el) {
548
+ scope.querySelectorAll("Error").forEach(function(el) {
547
549
  var _el_textContent;
548
550
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
549
551
  if (url) trackingUrls.error.push(url);
@@ -653,6 +655,75 @@ function createHlsAdPlayer(contentVideo, options) {
653
655
  };
654
656
  }
655
657
  }
658
+ function parseAdElement(adElement) {
659
+ try {
660
+ var _adElement_querySelector, _adElement_querySelector_textContent, _adElement_querySelector1, _adElement_querySelector_textContent1, _adElement_querySelector2, _adElement_querySelector3;
661
+ var adId = adElement.getAttribute("id") || "unknown";
662
+ var title = ((_adElement_querySelector = adElement.querySelector("AdTitle")) === null || _adElement_querySelector === void 0 ? void 0 : _adElement_querySelector.textContent) || "Ad";
663
+ 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();
664
+ 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();
665
+ if (wrapperUri) {
666
+ return {
667
+ type: "wrapper",
668
+ vastAdTagUri: wrapperUri,
669
+ trackingUrls: extractTrackingUrls(adElement),
670
+ clickThrough: clickThrough
671
+ };
672
+ }
673
+ var isNoAdAvailable = adId === "empty" || title.toLowerCase().includes("no ad available") || title.toLowerCase() === "no ad available";
674
+ var durationText = ((_adElement_querySelector3 = adElement.querySelector("Duration")) === null || _adElement_querySelector3 === void 0 ? void 0 : _adElement_querySelector3.textContent) || "00:00:30";
675
+ var durationParts = durationText.split(":");
676
+ var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
677
+ var mediaFileElements = adElement.querySelectorAll("MediaFile");
678
+ var mediaFiles = [];
679
+ mediaFileElements.forEach(function(mf) {
680
+ var _mf_textContent;
681
+ var type = mf.getAttribute("type") || "";
682
+ var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
683
+ var width = mf.getAttribute("width") || "";
684
+ var height = mf.getAttribute("height") || "";
685
+ if (!url) return;
686
+ var bitrateAttr = mf.getAttribute("bitrate");
687
+ var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
688
+ var mediaFile = {
689
+ url: url,
690
+ type: type,
691
+ width: parseInt(width || "1920", 10),
692
+ height: parseInt(height || "1080", 10),
693
+ bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
694
+ };
695
+ if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
696
+ mediaFiles.push(mediaFile);
697
+ }
698
+ });
699
+ if (mediaFiles.length === 0) {
700
+ if (isNoAdAvailable) {
701
+ console.warn("[HlsAdPlayer] Pod <Ad> indicates no ad available");
702
+ } else {
703
+ console.warn("[HlsAdPlayer] No supported media files in pod <Ad>");
704
+ }
705
+ return {
706
+ type: "empty"
707
+ };
708
+ }
709
+ return {
710
+ type: "inline",
711
+ ad: {
712
+ id: adId,
713
+ title: title,
714
+ duration: duration,
715
+ mediaFiles: mediaFiles,
716
+ trackingUrls: extractTrackingUrls(adElement),
717
+ clickThrough: clickThrough
718
+ }
719
+ };
720
+ } catch (error) {
721
+ console.error("[HlsAdPlayer] Error parsing pod <Ad> element:", error);
722
+ return {
723
+ type: "empty"
724
+ };
725
+ }
726
+ }
656
727
  function fetchVastXml(vastTagUrl) {
657
728
  return _async_to_generator(function() {
658
729
  var response;
@@ -729,6 +800,146 @@ function createHlsAdPlayer(contentVideo, options) {
729
800
  });
730
801
  }).apply(this, arguments);
731
802
  }
803
+ function fetchAndParsePod(vastTagUrl) {
804
+ return _async_to_generator(function() {
805
+ var vastXml, parser, xmlDoc, adElements, ads, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, adElement, parsed, resolved, error, err;
806
+ return _ts_generator(this, function(_state) {
807
+ switch(_state.label){
808
+ case 0:
809
+ return [
810
+ 4,
811
+ fetchVastXml(vastTagUrl)
812
+ ];
813
+ case 1:
814
+ vastXml = _state.sent();
815
+ console.log("[HlsAdPlayer] Pod VAST XML received");
816
+ parser = new DOMParser();
817
+ xmlDoc = parser.parseFromString(vastXml, "text/xml");
818
+ if (xmlDoc.querySelector("parsererror")) {
819
+ console.error("[HlsAdPlayer] Pod VAST XML parsing error (malformed)");
820
+ return [
821
+ 2,
822
+ []
823
+ ];
824
+ }
825
+ adElements = Array.from(xmlDoc.querySelectorAll("Ad"));
826
+ if (adElements.length === 0) {
827
+ console.warn("[HlsAdPlayer] No <Ad> elements found in pod VAST response");
828
+ return [
829
+ 2,
830
+ []
831
+ ];
832
+ }
833
+ adElements.sort(function(a, b) {
834
+ var aSeq = parseInt(a.getAttribute("sequence") || "", 10);
835
+ var bSeq = parseInt(b.getAttribute("sequence") || "", 10);
836
+ var aVal = Number.isFinite(aSeq) ? aSeq : Number.MAX_SAFE_INTEGER;
837
+ var bVal = Number.isFinite(bSeq) ? bSeq : Number.MAX_SAFE_INTEGER;
838
+ return aVal - bVal;
839
+ });
840
+ ads = [];
841
+ _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
842
+ _state.label = 2;
843
+ case 2:
844
+ _state.trys.push([
845
+ 2,
846
+ 10,
847
+ 11,
848
+ 12
849
+ ]);
850
+ _iterator = adElements[Symbol.iterator]();
851
+ _state.label = 3;
852
+ case 3:
853
+ if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [
854
+ 3,
855
+ 9
856
+ ];
857
+ adElement = _step.value;
858
+ parsed = parseAdElement(adElement);
859
+ if (!(parsed.type === "inline")) return [
860
+ 3,
861
+ 4
862
+ ];
863
+ ads.push(parsed.ad);
864
+ return [
865
+ 3,
866
+ 8
867
+ ];
868
+ case 4:
869
+ if (!(parsed.type === "wrapper")) return [
870
+ 3,
871
+ 8
872
+ ];
873
+ _state.label = 5;
874
+ case 5:
875
+ _state.trys.push([
876
+ 5,
877
+ 7,
878
+ ,
879
+ 8
880
+ ]);
881
+ return [
882
+ 4,
883
+ fetchAndParseVastAd(parsed.vastAdTagUri, 1, parsed.trackingUrls)
884
+ ];
885
+ case 6:
886
+ resolved = _state.sent();
887
+ if (resolved) {
888
+ ads.push(resolved);
889
+ }
890
+ return [
891
+ 3,
892
+ 8
893
+ ];
894
+ case 7:
895
+ error = _state.sent();
896
+ console.warn("[HlsAdPlayer] Failed to resolve wrapper <Ad> in pod:", error);
897
+ return [
898
+ 3,
899
+ 8
900
+ ];
901
+ case 8:
902
+ _iteratorNormalCompletion = true;
903
+ return [
904
+ 3,
905
+ 3
906
+ ];
907
+ case 9:
908
+ return [
909
+ 3,
910
+ 12
911
+ ];
912
+ case 10:
913
+ err = _state.sent();
914
+ _didIteratorError = true;
915
+ _iteratorError = err;
916
+ return [
917
+ 3,
918
+ 12
919
+ ];
920
+ case 11:
921
+ try {
922
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
923
+ _iterator.return();
924
+ }
925
+ } finally{
926
+ if (_didIteratorError) {
927
+ throw _iteratorError;
928
+ }
929
+ }
930
+ return [
931
+ 7
932
+ ];
933
+ case 12:
934
+ console.log("[HlsAdPlayer] Parsed ".concat(ads.length, " ad(s) from pod VAST response"));
935
+ return [
936
+ 2,
937
+ ads
938
+ ];
939
+ }
940
+ });
941
+ })();
942
+ }
732
943
  function createAdVideoElement() {
733
944
  var video = document.createElement("video");
734
945
  video.style.position = "absolute";
@@ -773,6 +984,9 @@ function createHlsAdPlayer(contentVideo, options) {
773
984
  trackingFired.complete = true;
774
985
  fireTrackingPixels(currentAd.trackingUrls.complete);
775
986
  console.log("[HlsAdPlayer] Ad completed");
987
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
988
+ return;
989
+ }
776
990
  handleAdComplete();
777
991
  });
778
992
  adVideoElement.addEventListener("error", function(e) {
@@ -781,6 +995,9 @@ function createHlsAdPlayer(contentVideo, options) {
781
995
  if (currentAd) {
782
996
  fireTrackingPixels(currentAd.trackingUrls.error);
783
997
  }
998
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
999
+ return;
1000
+ }
784
1001
  handleAdError();
785
1002
  });
786
1003
  adVideoElement.addEventListener("volumechange", function() {
@@ -809,6 +1026,94 @@ function createHlsAdPlayer(contentVideo, options) {
809
1026
  delete contentVideo.dataset.stormcloudAdPlaying;
810
1027
  }
811
1028
  }
1029
+ function resetQuartileTracking(preserveImpression) {
1030
+ trackingFired = {
1031
+ impression: preserveImpression ? trackingFired.impression : false,
1032
+ start: false,
1033
+ firstQuartile: false,
1034
+ midpoint: false,
1035
+ thirdQuartile: false,
1036
+ complete: false
1037
+ };
1038
+ }
1039
+ function loadCurrentAdMedia() {
1040
+ if (!adVideoElement || !currentAd) {
1041
+ throw new Error("No ad video element or ad to load");
1042
+ }
1043
+ var mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1044
+ if (!mediaFile) {
1045
+ throw new Error("No media file available for ad");
1046
+ }
1047
+ var isHlsAd = isHlsMediaFile(mediaFile);
1048
+ console.log("[HlsAdPlayer] Loading ".concat(isHlsAd ? "HLS" : "progressive", " ad (pod ").concat(podIndex + 1, "/").concat(podAds.length || 1, ") from: ").concat(mediaFile.url));
1049
+ if (isHlsAd && import_hls.default.isSupported()) {
1050
+ if (adHls) {
1051
+ adHls.destroy();
1052
+ }
1053
+ adHls = new import_hls.default({
1054
+ enableWorker: true,
1055
+ lowLatencyMode: false
1056
+ });
1057
+ adHls.loadSource(mediaFile.url);
1058
+ adHls.attachMedia(adVideoElement);
1059
+ adHls.on(import_hls.default.Events.MANIFEST_PARSED, function() {
1060
+ console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1061
+ adVideoElement.play().catch(function(error) {
1062
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1063
+ handleAdError();
1064
+ });
1065
+ });
1066
+ adHls.on(import_hls.default.Events.ERROR, function(_event, data) {
1067
+ console.error("[HlsAdPlayer] HLS error:", data);
1068
+ if (data.fatal) {
1069
+ handleAdError();
1070
+ }
1071
+ });
1072
+ } else if (isHlsAd && adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1073
+ adVideoElement.src = mediaFile.url;
1074
+ adVideoElement.play().catch(function(error) {
1075
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1076
+ handleAdError();
1077
+ });
1078
+ } else if (!isHlsAd && isProgressiveMediaFile(mediaFile)) {
1079
+ if (adHls) {
1080
+ adHls.destroy();
1081
+ adHls = void 0;
1082
+ }
1083
+ adVideoElement.src = mediaFile.url;
1084
+ adVideoElement.load();
1085
+ adVideoElement.play().catch(function(error) {
1086
+ console.error("[HlsAdPlayer] Error starting progressive ad playback:", error);
1087
+ handleAdError();
1088
+ });
1089
+ } else {
1090
+ throw new Error("Unsupported ad media file type: ".concat(mediaFile.type));
1091
+ }
1092
+ }
1093
+ function advanceToNextPodAd() {
1094
+ if (podIndex >= podAds.length - 1) {
1095
+ return false;
1096
+ }
1097
+ podIndex++;
1098
+ currentAd = podAds[podIndex];
1099
+ resetQuartileTracking(false);
1100
+ if (currentAd) {
1101
+ fireTrackingPixels(currentAd.trackingUrls.impression);
1102
+ trackingFired.impression = true;
1103
+ console.log("[HlsAdPlayer] Advancing to next pod ad (".concat(podIndex + 1, "/").concat(podAds.length, "): ").concat(currentAd.title, ", duration: ").concat(currentAd.duration, "s"));
1104
+ }
1105
+ try {
1106
+ loadCurrentAdMedia();
1107
+ emit("pod_ad_started", {
1108
+ index: podIndex,
1109
+ total: podAds.length
1110
+ });
1111
+ return true;
1112
+ } catch (error) {
1113
+ console.error("[HlsAdPlayer] Failed to load next pod ad:", error);
1114
+ return false;
1115
+ }
1116
+ }
812
1117
  function handleAdComplete() {
813
1118
  console.log("[HlsAdPlayer] Handling ad completion");
814
1119
  adPlaying = false;
@@ -862,7 +1167,7 @@ function createHlsAdPlayer(contentVideo, options) {
862
1167
  },
863
1168
  requestAds: function requestAds(vastTagUrl) {
864
1169
  return _async_to_generator(function() {
865
- var ad, error;
1170
+ var ads, preloaded, error;
866
1171
  return _ts_generator(this, function(_state) {
867
1172
  switch(_state.label){
868
1173
  case 0:
@@ -883,12 +1188,18 @@ function createHlsAdPlayer(contentVideo, options) {
883
1188
  6
884
1189
  ]);
885
1190
  sessionId = generateSessionId();
1191
+ ads = [];
886
1192
  if (!preloadedAds.has(vastTagUrl)) return [
887
1193
  3,
888
1194
  2
889
1195
  ];
890
- ad = preloadedAds.get(vastTagUrl);
1196
+ preloaded = preloadedAds.get(vastTagUrl);
891
1197
  preloadedAds.delete(vastTagUrl);
1198
+ if (preloaded) {
1199
+ ads = [
1200
+ preloaded
1201
+ ];
1202
+ }
892
1203
  console.log("[HlsAdPlayer] Using preloaded VAST response:", vastTagUrl);
893
1204
  return [
894
1205
  3,
@@ -897,13 +1208,13 @@ function createHlsAdPlayer(contentVideo, options) {
897
1208
  case 2:
898
1209
  return [
899
1210
  4,
900
- fetchAndParseVastAd(vastTagUrl)
1211
+ fetchAndParsePod(vastTagUrl)
901
1212
  ];
902
1213
  case 3:
903
- ad = _state.sent();
1214
+ ads = _state.sent();
904
1215
  _state.label = 4;
905
1216
  case 4:
906
- if (!ad) {
1217
+ if (!ads.length) {
907
1218
  console.warn("[HlsAdPlayer] No ads available from VAST response");
908
1219
  emit("ad_error");
909
1220
  return [
@@ -911,10 +1222,14 @@ function createHlsAdPlayer(contentVideo, options) {
911
1222
  Promise.reject(new Error("No ads available from VAST response"))
912
1223
  ];
913
1224
  }
914
- currentAd = ad;
915
- console.log("[HlsAdPlayer] Ad parsed: ".concat(ad.title, ", duration: ").concat(ad.duration, "s"));
916
- fireTrackingPixels(ad.trackingUrls.impression);
917
- trackingFired.impression = true;
1225
+ podAds = ads;
1226
+ podIndex = 0;
1227
+ currentAd = ads[0];
1228
+ 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"));
1229
+ if (currentAd) {
1230
+ fireTrackingPixels(currentAd.trackingUrls.impression);
1231
+ trackingFired.impression = true;
1232
+ }
918
1233
  return [
919
1234
  2,
920
1235
  Promise.resolve()
@@ -937,7 +1252,7 @@ function createHlsAdPlayer(contentVideo, options) {
937
1252
  },
938
1253
  play: function play() {
939
1254
  return _async_to_generator(function() {
940
- var contentVolume, mediaFile, isHlsAd;
1255
+ var contentVolume;
941
1256
  return _ts_generator(this, function(_state) {
942
1257
  if (!currentAd) {
943
1258
  console.warn("[HlsAdPlayer] Cannot play: No ad loaded (no ads available)");
@@ -953,14 +1268,7 @@ function createHlsAdPlayer(contentVideo, options) {
953
1268
  adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.appendChild(adVideoElement);
954
1269
  setupAdEventListeners();
955
1270
  }
956
- trackingFired = {
957
- impression: trackingFired.impression,
958
- start: false,
959
- firstQuartile: false,
960
- midpoint: false,
961
- thirdQuartile: false,
962
- complete: false
963
- };
1271
+ resetQuartileTracking(true);
964
1272
  contentVolume = contentVideo.volume;
965
1273
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
966
1274
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
@@ -978,60 +1286,12 @@ function createHlsAdPlayer(contentVideo, options) {
978
1286
  adVideoElement.muted = originalMutedState;
979
1287
  console.log("[HlsAdPlayer] Set ad video volume to ".concat(adVideoElement.volume, ", muted: ").concat(adVideoElement.muted, ", originalMutedState: ").concat(originalMutedState, ", contentVolume: ").concat(contentVolume));
980
1288
  }
981
- mediaFile = selectBestMediaFile(currentAd.mediaFiles);
982
- if (!mediaFile) {
983
- throw new Error("No media file available for ad");
984
- }
985
1289
  if (adContainerEl) {
986
1290
  adContainerEl.style.display = "flex";
987
1291
  adContainerEl.style.pointerEvents = "auto";
988
1292
  }
989
1293
  emit("content_pause");
990
- isHlsAd = isHlsMediaFile(mediaFile);
991
- console.log("[HlsAdPlayer] Loading ".concat(isHlsAd ? "HLS" : "progressive", " ad from: ").concat(mediaFile.url));
992
- if (isHlsAd && import_hls.default.isSupported()) {
993
- if (adHls) {
994
- adHls.destroy();
995
- }
996
- adHls = new import_hls.default({
997
- enableWorker: true,
998
- lowLatencyMode: false
999
- });
1000
- adHls.loadSource(mediaFile.url);
1001
- adHls.attachMedia(adVideoElement);
1002
- adHls.on(import_hls.default.Events.MANIFEST_PARSED, function() {
1003
- console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1004
- adVideoElement.play().catch(function(error) {
1005
- console.error("[HlsAdPlayer] Error starting ad playback:", error);
1006
- handleAdError();
1007
- });
1008
- });
1009
- adHls.on(import_hls.default.Events.ERROR, function(event, data) {
1010
- console.error("[HlsAdPlayer] HLS error:", data);
1011
- if (data.fatal) {
1012
- handleAdError();
1013
- }
1014
- });
1015
- } else if (isHlsAd && adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1016
- adVideoElement.src = mediaFile.url;
1017
- adVideoElement.play().catch(function(error) {
1018
- console.error("[HlsAdPlayer] Error starting ad playback:", error);
1019
- handleAdError();
1020
- });
1021
- } else if (!isHlsAd && isProgressiveMediaFile(mediaFile)) {
1022
- if (adHls) {
1023
- adHls.destroy();
1024
- adHls = void 0;
1025
- }
1026
- adVideoElement.src = mediaFile.url;
1027
- adVideoElement.load();
1028
- adVideoElement.play().catch(function(error) {
1029
- console.error("[HlsAdPlayer] Error starting progressive ad playback:", error);
1030
- handleAdError();
1031
- });
1032
- } else {
1033
- throw new Error("Unsupported ad media file type: ".concat(mediaFile.type));
1034
- }
1294
+ loadCurrentAdMedia();
1035
1295
  return [
1036
1296
  2,
1037
1297
  Promise.resolve()
@@ -1106,6 +1366,8 @@ function createHlsAdPlayer(contentVideo, options) {
1106
1366
  adVideoElement.src = "";
1107
1367
  }
1108
1368
  currentAd = void 0;
1369
+ podAds = [];
1370
+ podIndex = 0;
1109
1371
  return [
1110
1372
  2
1111
1373
  ];
@@ -1155,6 +1417,8 @@ function createHlsAdPlayer(contentVideo, options) {
1155
1417
  }
1156
1418
  adContainerEl = void 0;
1157
1419
  currentAd = void 0;
1420
+ podAds = [];
1421
+ podIndex = 0;
1158
1422
  listeners.clear();
1159
1423
  preloadedAds.clear();
1160
1424
  preloadingAds.clear();
@@ -1246,6 +1510,14 @@ function createHlsAdPlayer(contentVideo, options) {
1246
1510
  },
1247
1511
  getLoadedAdId: function getLoadedAdId() {
1248
1512
  return currentAd === null || currentAd === void 0 ? void 0 : currentAd.id;
1513
+ },
1514
+ getPodAdCount: function getPodAdCount() {
1515
+ return podAds.length;
1516
+ },
1517
+ getPodTotalDurationMs: function getPodTotalDurationMs() {
1518
+ return podAds.reduce(function(total, ad) {
1519
+ return total + (ad.duration > 0 ? ad.duration * 1e3 : 0);
1520
+ }, 0);
1249
1521
  }
1250
1522
  };
1251
1523
  }
@@ -1625,38 +1897,108 @@ function getOrCreateStoredUuid(storageKey) {
1625
1897
  if (typeof window === "undefined") {
1626
1898
  return void 0;
1627
1899
  }
1628
- var existing = readStoredString(storageKey);
1900
+ var existing = readPersistentId(storageKey);
1629
1901
  if (existing) {
1902
+ writePersistentId(storageKey, existing);
1630
1903
  return existing;
1631
1904
  }
1632
1905
  var id = createUuid();
1633
- try {
1634
- var _window_localStorage;
1635
- (_window_localStorage = window.localStorage) === null || _window_localStorage === void 0 ? void 0 : _window_localStorage.setItem(storageKey, id);
1636
- } catch (unused) {}
1906
+ writePersistentId(storageKey, id);
1637
1907
  return id;
1638
1908
  }
1639
1909
  function getOrCreateCtvSessionId() {
1640
- var existing = readStoredString(CTV_SESSION_STORAGE_KEY);
1910
+ var existing = readPersistentId(CTV_SESSION_STORAGE_KEY);
1641
1911
  if (existing) {
1912
+ writePersistentId(CTV_SESSION_STORAGE_KEY, existing);
1642
1913
  return existing;
1643
1914
  }
1644
1915
  var sessionId = createUuid();
1916
+ writePersistentId(CTV_SESSION_STORAGE_KEY, sessionId);
1917
+ return sessionId;
1918
+ }
1919
+ function readPersistentId(storageKey) {
1920
+ return readStoredString(storageKey) || readCookie(storageKey);
1921
+ }
1922
+ function writePersistentId(storageKey, value) {
1645
1923
  try {
1646
1924
  var _window_localStorage;
1647
- (_window_localStorage = window.localStorage) === null || _window_localStorage === void 0 ? void 0 : _window_localStorage.setItem(CTV_SESSION_STORAGE_KEY, sessionId);
1925
+ (_window_localStorage = window.localStorage) === null || _window_localStorage === void 0 ? void 0 : _window_localStorage.setItem(storageKey, value);
1926
+ } catch (unused) {}
1927
+ writeCookie(storageKey, value);
1928
+ }
1929
+ var PERSISTENT_ID_COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5;
1930
+ function readCookie(name) {
1931
+ if (typeof document === "undefined") {
1932
+ return void 0;
1933
+ }
1934
+ try {
1935
+ var prefix = encodeURIComponent(name) + "=";
1936
+ var parts = document.cookie ? document.cookie.split(";") : [];
1937
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1938
+ try {
1939
+ for(var _iterator = parts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1940
+ var part = _step.value;
1941
+ var entry = part.trim();
1942
+ if (entry.startsWith(prefix)) {
1943
+ return decodeURIComponent(entry.slice(prefix.length)) || void 0;
1944
+ }
1945
+ }
1946
+ } catch (err) {
1947
+ _didIteratorError = true;
1948
+ _iteratorError = err;
1949
+ } finally{
1950
+ try {
1951
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1952
+ _iterator.return();
1953
+ }
1954
+ } finally{
1955
+ if (_didIteratorError) {
1956
+ throw _iteratorError;
1957
+ }
1958
+ }
1959
+ }
1960
+ } catch (unused) {}
1961
+ return void 0;
1962
+ }
1963
+ function writeCookie(name, value) {
1964
+ if (typeof document === "undefined") {
1965
+ return;
1966
+ }
1967
+ try {
1968
+ document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + ";path=/;max-age=" + PERSISTENT_ID_COOKIE_MAX_AGE + ";SameSite=Lax";
1648
1969
  } catch (unused) {}
1649
- return sessionId;
1650
1970
  }
1651
1971
  function createUuid() {
1972
+ var _bytes_, _bytes_1;
1652
1973
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1653
1974
  return crypto.randomUUID();
1654
1975
  }
1655
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(char) {
1656
- var value = Math.floor(Math.random() * 16);
1657
- var nibble = char === "x" ? value : value & 3 | 8;
1658
- return nibble.toString(16);
1659
- });
1976
+ var bytes = new Uint8Array(16);
1977
+ if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
1978
+ crypto.getRandomValues(bytes);
1979
+ } else {
1980
+ var seed = Date.now() ^ Math.floor(performanceNow() * 1e3);
1981
+ for(var i = 0; i < 16; i++){
1982
+ seed = seed * 1103515245 + 12345 & 2147483647;
1983
+ bytes[i] = (seed ^ Math.floor(Math.random() * 256)) & 255;
1984
+ }
1985
+ }
1986
+ bytes[6] = ((_bytes_ = bytes[6]) !== null && _bytes_ !== void 0 ? _bytes_ : 0) & 15 | 64;
1987
+ bytes[8] = ((_bytes_1 = bytes[8]) !== null && _bytes_1 !== void 0 ? _bytes_1 : 0) & 63 | 128;
1988
+ var hex = "";
1989
+ for(var i1 = 0; i1 < 16; i1++){
1990
+ var _bytes_i;
1991
+ hex += ((_bytes_i = bytes[i1]) !== null && _bytes_i !== void 0 ? _bytes_i : 0).toString(16).padStart(2, "0");
1992
+ }
1993
+ return hex.slice(0, 8) + "-" + hex.slice(8, 12) + "-" + hex.slice(12, 16) + "-" + hex.slice(16, 20) + "-" + hex.slice(20, 32);
1994
+ }
1995
+ function performanceNow() {
1996
+ try {
1997
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
1998
+ return performance.now();
1999
+ }
2000
+ } catch (unused) {}
2001
+ return 0;
1660
2002
  }
1661
2003
  function readPlatformNativeDeviceId() {
1662
2004
  if (typeof window === "undefined") return {};
@@ -3074,7 +3416,20 @@ function applyVastMacros(baseUrl, ctx) {
3074
3416
  if (ctx.pod != null) {
3075
3417
  params.set("pod", String(ctx.pod));
3076
3418
  }
3077
- if (ctx.adPosition != null) {
3419
+ var optimizedPodMode = ctx.podMaxAds != null || ctx.podMinDurationMs != null || ctx.podMaxDurationMs != null;
3420
+ if (optimizedPodMode) {
3421
+ params.delete("ppos");
3422
+ params.delete("lip");
3423
+ if (ctx.podMinDurationMs != null) {
3424
+ params.set("pmnd", String(Math.max(0, Math.round(ctx.podMinDurationMs))));
3425
+ }
3426
+ if (ctx.podMaxDurationMs != null) {
3427
+ params.set("pmxd", String(Math.max(0, Math.round(ctx.podMaxDurationMs))));
3428
+ }
3429
+ if (ctx.podMaxAds != null) {
3430
+ params.set("pmad", String(Math.max(1, Math.round(ctx.podMaxAds))));
3431
+ }
3432
+ } else if (ctx.adPosition != null) {
3078
3433
  params.set("ppos", String(ctx.adPosition));
3079
3434
  }
3080
3435
  var requestUrl = ctx.contentUrl || ctx.pageUrl;
@@ -4416,6 +4771,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
4416
4771
  this.podAssignedByPrefetch = false;
4417
4772
  this.adRequestPositionInBreak = 0;
4418
4773
  this.lastHeartbeatTime = 0;
4774
+ this.defaultPodMaxAds = 6;
4419
4775
  this.config = config;
4420
4776
  this.video = video;
4421
4777
  }
@@ -4871,6 +5227,52 @@ var AdConfigManager = /*#__PURE__*/ function() {
4871
5227
  return urls;
4872
5228
  }
4873
5229
  },
5230
+ {
5231
+ key: "resolvePodParams",
5232
+ value: function resolvePodParams(breakDurationMs) {
5233
+ var maxDurationMs = breakDurationMs != null && breakDurationMs > 0 ? breakDurationMs : this.config.podMaxDurationMs;
5234
+ var maxAds = this.config.podMaxAds && this.config.podMaxAds > 0 ? this.config.podMaxAds : this.apiNumberAds && this.apiNumberAds > 0 ? this.apiNumberAds : this.defaultPodMaxAds;
5235
+ var minDurationMs = this.config.podExactDuration && maxDurationMs != null ? maxDurationMs : 0;
5236
+ return {
5237
+ maxAds: maxAds,
5238
+ minDurationMs: minDurationMs,
5239
+ maxDurationMs: maxDurationMs
5240
+ };
5241
+ }
5242
+ },
5243
+ {
5244
+ key: "generatePodVastUrl",
5245
+ value: function generatePodVastUrl(baseUrl, podParams, adPlayer, palNonce, inAdBreak) {
5246
+ var _podParams_maxDurationMs;
5247
+ var adWillPlayMuted = inAdBreak ? adPlayer.getOriginalMutedState() : this.video.muted;
5248
+ var envSignals = resolveVastEnvironmentSignals(!!this.config.ctvAdRequest);
5249
+ var urlWithMacros = applyVastMacros(baseUrl, {
5250
+ correlator: generateCorrelator(),
5251
+ streamCorrelator: this.streamCorrelator,
5252
+ pod: this.podCounter > 0 ? this.podCounter : void 0,
5253
+ podMaxAds: podParams.maxAds,
5254
+ podMinDurationMs: podParams.minDurationMs,
5255
+ podMaxDurationMs: podParams.maxDurationMs,
5256
+ isCtv: envSignals.isCtv,
5257
+ contentUrl: envSignals.contentUrl,
5258
+ pageUrl: !envSignals.isCtv && typeof window !== "undefined" ? window.location.href : void 0,
5259
+ adWillPlayMuted: adWillPlayMuted,
5260
+ adWillAutoPlay: !!this.config.autoplay,
5261
+ appId: envSignals.appId,
5262
+ appName: envSignals.appName,
5263
+ sessionId: envSignals.sessionId,
5264
+ deviceId: envSignals.deviceId,
5265
+ deviceIdType: envSignals.deviceIdType,
5266
+ limitAdTracking: envSignals.limitAdTracking,
5267
+ deviceTypeHint: envSignals.deviceTypeHint,
5268
+ adTest: this.config.adTest,
5269
+ consent: this.consentSignals
5270
+ });
5271
+ var finalUrl = palNonce.injectNonce(urlWithMacros);
5272
+ 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);
5273
+ return finalUrl;
5274
+ }
5275
+ },
4874
5276
  {
4875
5277
  // --- Tracking ---
4876
5278
  key: "initializeTracking",
@@ -5176,6 +5578,13 @@ var AdTimingService = /*#__PURE__*/ function() {
5176
5578
  this.lastAdRequestTime = Date.now();
5177
5579
  }
5178
5580
  },
5581
+ {
5582
+ key: "notePodAdStarted",
5583
+ value: function notePodAdStarted() {
5584
+ this.currentAdIndex++;
5585
+ this.totalAdsInBreak = this.currentAdIndex;
5586
+ }
5587
+ },
5179
5588
  {
5180
5589
  key: "getRemainingAdMs",
5181
5590
  value: function getRemainingAdMs() {
@@ -6684,13 +7093,26 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6684
7093
  }
6685
7094
  },
6686
7095
  {
6687
- // ───────────────────────────────────────────────────────────────
7096
+ key: "optimizedPodsEnabled",
7097
+ get: // ───────────────────────────────────────────────────────────────
6688
7098
  // IMA event listeners
6689
7099
  // ───────────────────────────────────────────────────────────────
7100
+ function get() {
7101
+ return !!this.host.config.optimizedPods;
7102
+ }
7103
+ },
7104
+ {
6690
7105
  key: "attachImaEventListeners",
6691
7106
  value: function attachImaEventListeners() {
6692
7107
  var _this = this;
6693
7108
  var adPlayer = this.host.getAdPlayer();
7109
+ adPlayer.on("pod_ad_started", function(payload) {
7110
+ _this.timing.notePodAdStarted();
7111
+ if (_this.debug) {
7112
+ var _ref;
7113
+ 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 : "?"));
7114
+ }
7115
+ });
6694
7116
  adPlayer.on("all_ads_completed", function() {
6695
7117
  sendAdImpressionTracking(_this.host.config.licenseKey, {
6696
7118
  source: "hls",
@@ -6848,7 +7270,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6848
7270
  // ───────────────────────────────────────────────────────────────
6849
7271
  function handleAdStart(_marker) {
6850
7272
  return _async_to_generator(function() {
6851
- var scheduled, tags, baseVastUrl, adBreakDurationMs, mode, currentMuted, currentVolume, firstAdUrl, usePreloadedAd, preloadedController, preloaded, firstAdUrlArray, adPlayer, preservedMuted, preservedVolume, adVolume, adPlayer1, adVolume1, error, fallbackPreloaded, ap, adVolume2, fallbackError;
7273
+ 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;
6852
7274
  return _ts_generator(this, function(_state) {
6853
7275
  switch(_state.label){
6854
7276
  case 0:
@@ -6895,6 +7317,26 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6895
7317
  if (this.timing.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
6896
7318
  this.timing.expectedAdBreakDurationMs = adBreakDurationMs;
6897
7319
  }
7320
+ if (!this.optimizedPodsEnabled) return [
7321
+ 3,
7322
+ 2
7323
+ ];
7324
+ return [
7325
+ 4,
7326
+ this.startOptimizedPod(baseVastUrl, adBreakDurationMs, currentMuted, currentVolume)
7327
+ ];
7328
+ case 1:
7329
+ podPlayed = _state.sent();
7330
+ if (podPlayed) {
7331
+ return [
7332
+ 2
7333
+ ];
7334
+ }
7335
+ if (this.debug) {
7336
+ console.log("[POD] Optimized pod unavailable, falling back to sequential single-ad path");
7337
+ }
7338
+ _state.label = 2;
7339
+ case 2:
6898
7340
  usePreloadedAd = false;
6899
7341
  preloaded = this.preloadPool.getPreloadedAd();
6900
7342
  if (preloaded) {
@@ -6936,21 +7378,21 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6936
7378
  if (this.debug) {
6937
7379
  console.log("[CONTINUOUS-FETCH] First VAST URL:", firstAdUrl);
6938
7380
  }
6939
- _state.label = 1;
6940
- case 1:
7381
+ _state.label = 3;
7382
+ case 3:
6941
7383
  _state.trys.push([
6942
- 1,
6943
- 12,
7384
+ 3,
7385
+ 14,
6944
7386
  ,
6945
- 20
7387
+ 22
6946
7388
  ]);
6947
7389
  if (!(usePreloadedAd && preloadedController)) return [
6948
7390
  3,
6949
- 5
7391
+ 7
6950
7392
  ];
6951
7393
  if (!!this.timing.ensureLoadedAdFitsBudget(preloadedController)) return [
6952
7394
  3,
6953
- 3
7395
+ 5
6954
7396
  ];
6955
7397
  this.timing.rejectLoadedAdForDuration(preloadedController);
6956
7398
  this.startContinuousFetching(baseVastUrl);
@@ -6958,12 +7400,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6958
7400
  4,
6959
7401
  this.tryNextAvailableAdWithRateLimit()
6960
7402
  ];
6961
- case 2:
7403
+ case 4:
6962
7404
  _state.sent();
6963
7405
  return [
6964
7406
  2
6965
7407
  ];
6966
- case 3:
7408
+ case 5:
6967
7409
  adPlayer = this.host.getAdPlayer();
6968
7410
  preservedMuted = adPlayer.getOriginalMutedState();
6969
7411
  preservedVolume = adPlayer.getOriginalVolume();
@@ -6988,7 +7430,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6988
7430
  4,
6989
7431
  preloadedController.play()
6990
7432
  ];
6991
- case 4:
7433
+ case 6:
6992
7434
  _state.sent();
6993
7435
  this.timing.markAdStarted();
6994
7436
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -6998,22 +7440,22 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6998
7440
  preloadedController.setAdVolume(adVolume);
6999
7441
  return [
7000
7442
  3,
7001
- 11
7443
+ 13
7002
7444
  ];
7003
- case 5:
7445
+ case 7:
7004
7446
  adPlayer1 = this.host.getAdPlayer();
7005
7447
  return [
7006
7448
  4,
7007
7449
  this.timing.enforceGlobalRateLimit()
7008
7450
  ];
7009
- case 6:
7451
+ case 8:
7010
7452
  _state.sent();
7011
7453
  this.timing.lastAdRequestTime = Date.now();
7012
7454
  return [
7013
7455
  4,
7014
7456
  adPlayer1.requestAds(firstAdUrl)
7015
7457
  ];
7016
- case 7:
7458
+ case 9:
7017
7459
  _state.sent();
7018
7460
  sendAdLoadedTracking(this.host.config.licenseKey, {
7019
7461
  source: "hls",
@@ -7025,7 +7467,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7025
7467
  }
7026
7468
  if (!!this.timing.ensureLoadedAdFitsBudget(adPlayer1)) return [
7027
7469
  3,
7028
- 9
7470
+ 11
7029
7471
  ];
7030
7472
  this.timing.rejectLoadedAdForDuration(adPlayer1);
7031
7473
  this.recreateAdController();
@@ -7034,12 +7476,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7034
7476
  4,
7035
7477
  this.tryNextAvailableAdWithRateLimit()
7036
7478
  ];
7037
- case 8:
7479
+ case 10:
7038
7480
  _state.sent();
7039
7481
  return [
7040
7482
  2
7041
7483
  ];
7042
- case 9:
7484
+ case 11:
7043
7485
  this.timing.consecutiveFailures = 0;
7044
7486
  this.startContinuousFetching(baseVastUrl);
7045
7487
  if (!this.preloadPool.active) {
@@ -7049,7 +7491,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7049
7491
  4,
7050
7492
  adPlayer1.play()
7051
7493
  ];
7052
- case 10:
7494
+ case 12:
7053
7495
  _state.sent();
7054
7496
  this.timing.markAdStarted();
7055
7497
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7057,40 +7499,40 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7057
7499
  }
7058
7500
  adVolume1 = currentMuted ? 0 : currentVolume;
7059
7501
  adPlayer1.setAdVolume(adVolume1);
7060
- _state.label = 11;
7061
- case 11:
7502
+ _state.label = 13;
7503
+ case 13:
7062
7504
  return [
7063
7505
  3,
7064
- 20
7506
+ 22
7065
7507
  ];
7066
- case 12:
7508
+ case 14:
7067
7509
  error = _state.sent();
7068
7510
  if (this.debug) {
7069
7511
  console.warn("[CONTINUOUS-FETCH] First ad request failed:", error);
7070
7512
  }
7071
7513
  if (!!usePreloadedAd) return [
7072
7514
  3,
7073
- 18
7515
+ 20
7074
7516
  ];
7075
7517
  fallbackPreloaded = this.preloadPool.getPreloadedAd();
7076
7518
  if (!fallbackPreloaded) return [
7077
7519
  3,
7078
- 18
7520
+ 20
7079
7521
  ];
7080
7522
  if (this.debug) {
7081
7523
  console.log("[CONTINUOUS-FETCH] First ad failed, using preloaded fallback");
7082
7524
  }
7083
- _state.label = 13;
7084
- case 13:
7525
+ _state.label = 15;
7526
+ case 15:
7085
7527
  _state.trys.push([
7086
- 13,
7087
- 17,
7528
+ 15,
7529
+ 19,
7088
7530
  ,
7089
- 18
7531
+ 20
7090
7532
  ]);
7091
7533
  if (!!this.timing.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
7092
7534
  3,
7093
- 15
7535
+ 17
7094
7536
  ];
7095
7537
  this.timing.rejectLoadedAdForDuration(fallbackPreloaded.adController);
7096
7538
  this.startContinuousFetching(baseVastUrl);
@@ -7098,12 +7540,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7098
7540
  4,
7099
7541
  this.tryNextAvailableAdWithRateLimit()
7100
7542
  ];
7101
- case 14:
7543
+ case 16:
7102
7544
  _state.sent();
7103
7545
  return [
7104
7546
  2
7105
7547
  ];
7106
- case 15:
7548
+ case 17:
7107
7549
  this.swapToPreloadedAdPlayer(fallbackPreloaded.adController);
7108
7550
  this.timing.consecutiveFailures = 0;
7109
7551
  this.startContinuousFetching(baseVastUrl);
@@ -7116,7 +7558,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7116
7558
  4,
7117
7559
  ap.play()
7118
7560
  ];
7119
- case 16:
7561
+ case 18:
7120
7562
  _state.sent();
7121
7563
  this.timing.markAdStarted();
7122
7564
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7127,16 +7569,16 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7127
7569
  return [
7128
7570
  2
7129
7571
  ];
7130
- case 17:
7572
+ case 19:
7131
7573
  fallbackError = _state.sent();
7132
7574
  if (this.debug) {
7133
7575
  console.warn("[CONTINUOUS-FETCH] Preloaded fallback also failed:", fallbackError);
7134
7576
  }
7135
7577
  return [
7136
7578
  3,
7137
- 18
7579
+ 20
7138
7580
  ];
7139
- case 18:
7581
+ case 20:
7140
7582
  if (this.timing.isTemporaryAdError(error)) {
7141
7583
  this.timing.temporaryFailureUrls.set(firstAdUrl, Date.now());
7142
7584
  if (this.debug) {
@@ -7154,13 +7596,137 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7154
7596
  4,
7155
7597
  this.tryNextAvailableAdWithRateLimit()
7156
7598
  ];
7157
- case 19:
7599
+ case 21:
7158
7600
  _state.sent();
7159
7601
  return [
7160
7602
  3,
7161
- 20
7603
+ 22
7162
7604
  ];
7163
- case 20:
7605
+ case 22:
7606
+ return [
7607
+ 2
7608
+ ];
7609
+ }
7610
+ });
7611
+ }).call(this);
7612
+ }
7613
+ },
7614
+ {
7615
+ key: "startOptimizedPod",
7616
+ value: // ───────────────────────────────────────────────────────────────
7617
+ // Optimized pod (single multi-ad request)
7618
+ // ───────────────────────────────────────────────────────────────
7619
+ function startOptimizedPod(baseVastUrl, breakDurationMs, currentMuted, currentVolume) {
7620
+ return _async_to_generator(function() {
7621
+ var podUrl, adPlayer, requestToken, _ref, _adPlayer_getPodAdCount, podCount, error;
7622
+ return _ts_generator(this, function(_state) {
7623
+ switch(_state.label){
7624
+ case 0:
7625
+ if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {
7626
+ podUrl = this.pendingAdBreak.vastUrls[0];
7627
+ this.clearPendingAdBreak();
7628
+ } else {
7629
+ podUrl = this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);
7630
+ }
7631
+ if (!podUrl) {
7632
+ return [
7633
+ 2,
7634
+ false
7635
+ ];
7636
+ }
7637
+ this.continuousFetchingActive = false;
7638
+ this.recreateAdController();
7639
+ adPlayer = this.host.getAdPlayer();
7640
+ requestToken = ++this.timing.adRequestTokenCounter;
7641
+ this.timing.activeAdRequestToken = requestToken;
7642
+ this.timing.startAdRequestWatchdog(requestToken);
7643
+ _state.label = 1;
7644
+ case 1:
7645
+ _state.trys.push([
7646
+ 1,
7647
+ 5,
7648
+ ,
7649
+ 6
7650
+ ]);
7651
+ return [
7652
+ 4,
7653
+ this.timing.enforceGlobalRateLimit()
7654
+ ];
7655
+ case 2:
7656
+ _state.sent();
7657
+ this.timing.lastAdRequestTime = Date.now();
7658
+ return [
7659
+ 4,
7660
+ adPlayer.requestAds(podUrl)
7661
+ ];
7662
+ case 3:
7663
+ _state.sent();
7664
+ this.timing.clearAdRequestWatchdog();
7665
+ if (this.timing.activeAdRequestToken !== requestToken) {
7666
+ return [
7667
+ 2,
7668
+ true
7669
+ ];
7670
+ }
7671
+ podCount = (_ref = (_adPlayer_getPodAdCount = adPlayer.getPodAdCount) === null || _adPlayer_getPodAdCount === void 0 ? void 0 : _adPlayer_getPodAdCount.call(adPlayer)) !== null && _ref !== void 0 ? _ref : 1;
7672
+ if (podCount < 1) {
7673
+ return [
7674
+ 2,
7675
+ false
7676
+ ];
7677
+ }
7678
+ if (this.debug) {
7679
+ 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"));
7680
+ }
7681
+ sendAdLoadedTracking(this.host.config.licenseKey, {
7682
+ source: "hls",
7683
+ vastUrl: podUrl,
7684
+ timestamp: /* @__PURE__ */ new Date().toISOString()
7685
+ }, this.analyticsContext).catch(function() {});
7686
+ this.timing.startAdFailsafeTimer(requestToken);
7687
+ return [
7688
+ 4,
7689
+ adPlayer.play()
7690
+ ];
7691
+ case 4:
7692
+ _state.sent();
7693
+ this.timing.markAdStarted();
7694
+ if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
7695
+ this.timing.scheduleAdStopAtBreakBoundary();
7696
+ }
7697
+ adPlayer.setAdVolume(currentMuted ? 0 : currentVolume);
7698
+ this.timing.consecutiveFailures = 0;
7699
+ if (podCount === 1) {
7700
+ this.continuousFetchingActive = true;
7701
+ this.startContinuousFetching(baseVastUrl);
7702
+ if (!this.preloadPool.active) {
7703
+ this.preloadPool.startPreloadPool(baseVastUrl, []);
7704
+ }
7705
+ }
7706
+ return [
7707
+ 2,
7708
+ true
7709
+ ];
7710
+ case 5:
7711
+ error = _state.sent();
7712
+ this.timing.clearAdRequestWatchdog();
7713
+ this.timing.clearAdFailsafeTimer();
7714
+ if (this.timing.activeAdRequestToken === requestToken) {
7715
+ this.timing.activeAdRequestToken = null;
7716
+ }
7717
+ if (this.debug) {
7718
+ console.warn("[POD] Optimized pod request/play failed:", error);
7719
+ }
7720
+ if (this.timing.isTemporaryAdError(error)) {
7721
+ this.timing.temporaryFailureUrls.set(podUrl, Date.now());
7722
+ }
7723
+ this.continuousFetchingActive = true;
7724
+ this.recreateAdController();
7725
+ return [
7726
+ 2,
7727
+ false
7728
+ ];
7729
+ case 6:
7164
7730
  return [
7165
7731
  2
7166
7732
  ];
@@ -8242,6 +8808,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8242
8808
  },
8243
8809
  generateVastUrls: function generateVastUrls(base, count) {
8244
8810
  return _this.generateVastUrls(base, count);
8811
+ },
8812
+ generatePodVastUrl: function generatePodVastUrl(base, breakDurationMs) {
8813
+ return _this.generatePodVastUrl(base, breakDurationMs);
8245
8814
  }
8246
8815
  }, this.timing, this.preloadPool, this.adConfig, this.cueManager, this.placeholder);
8247
8816
  }
@@ -8274,6 +8843,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8274
8843
  return this.adConfig.generateVastUrlsWithCorrelators(baseUrl, count, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8275
8844
  }
8276
8845
  },
8846
+ {
8847
+ key: "generatePodVastUrl",
8848
+ value: function generatePodVastUrl(baseUrl, breakDurationMs) {
8849
+ var podParams = this.adConfig.resolvePodParams(breakDurationMs);
8850
+ return this.adConfig.generatePodVastUrl(baseUrl, podParams, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8851
+ }
8852
+ },
8277
8853
  {
8278
8854
  key: "load",
8279
8855
  value: // ───────────────────────────────────────────────────────────────
@@ -8662,7 +9238,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8662
9238
  }
8663
9239
  this.adConfig.beginNewAdPod();
8664
9240
  this.adConfig.podAssignedByPrefetch = true;
8665
- var generatedUrls = this.generateVastUrls(baseVastUrl, 1);
9241
+ var optimizedPods = !!this.config.optimizedPods;
9242
+ var breakDurationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : void 0;
9243
+ var generatedUrls = optimizedPods ? [
9244
+ this.generatePodVastUrl(baseVastUrl, breakDurationMs)
9245
+ ] : this.generateVastUrls(baseVastUrl, 1);
8666
9246
  this.adBreak.pendingAdBreak = _object_spread_props(_object_spread({
8667
9247
  marker: marker
8668
9248
  }, fragmentSn !== void 0 ? {
@@ -8684,7 +9264,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8684
9264
  }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8685
9265
  if (this.debug) {
8686
9266
  console.log("[PREFETCH] Starting ad prefetch for upcoming ad break");
8687
- console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
9267
+ console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " ").concat(optimizedPods ? "pod" : "VAST", " URL(s)"));
9268
+ }
9269
+ if (optimizedPods) {
9270
+ if (this.debug) {
9271
+ console.log("[PREFETCH] Optimized pods enabled - skipping single-ad preload pool");
9272
+ }
9273
+ return;
8688
9274
  }
8689
9275
  this.preloadPool.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
8690
9276
  }