stormcloud-video-player 0.8.24 → 0.8.26

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 (44) hide show
  1. package/dist/stormcloud-vp.min.js +1 -1
  2. package/lib/index.cjs +669 -126
  3. package/lib/index.cjs.map +1 -1
  4. package/lib/index.d.cts +10 -1
  5. package/lib/index.d.ts +10 -1
  6. package/lib/index.js +666 -127
  7. package/lib/index.js.map +1 -1
  8. package/lib/player/AdBreakOrchestrator.cjs +228 -48
  9. package/lib/player/AdBreakOrchestrator.cjs.map +1 -1
  10. package/lib/player/AdBreakOrchestrator.d.cts +5 -1
  11. package/lib/player/AdConfigManager.cjs +81 -4
  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 +665 -126
  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 +665 -126
  27. package/lib/players/HlsPlayer.cjs.map +1 -1
  28. package/lib/players/HlsPlayer.d.cts +1 -1
  29. package/lib/players/index.cjs +665 -126
  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-DNiBmPKK.d.cts → types-Xgz2_W1C.d.cts} +7 -0
  35. package/lib/ui/StormcloudVideoPlayer.cjs +665 -126
  36. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  37. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  38. package/lib/utils/tracking.cjs +22 -1
  39. package/lib/utils/tracking.cjs.map +1 -1
  40. package/lib/utils/tracking.d.cts +3 -2
  41. package/lib/utils/vastMacros.cjs +14 -1
  42. package/lib/utils/vastMacros.cjs.map +1 -1
  43. package/lib/utils/vastMacros.d.cts +3 -0
  44. 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
  }
@@ -1990,6 +2262,16 @@ function normalizeBoolean(value) {
1990
2262
  }
1991
2263
  // src/utils/tracking.ts
1992
2264
  var cachedBrowserId = null;
2265
+ function buildAnalyticsContext(config) {
2266
+ var context = {};
2267
+ if (config.debugAdTiming) {
2268
+ context.debugAdTiming = true;
2269
+ }
2270
+ if (config.isLiveStream !== void 0) {
2271
+ context.inputStreamType = config.isLiveStream ? "live" : "vod";
2272
+ }
2273
+ return context;
2274
+ }
1993
2275
  function getClientInfo() {
1994
2276
  var _screen, _screen1, _screen2, _screen3, _screen_orientation, _screen4, _screen5, _window, _window1, _window_screen_orientation, _window_screen, _navigator_languages;
1995
2277
  var ua = navigator.userAgent;
@@ -2248,19 +2530,26 @@ function canPublish(licenseKey) {
2248
2530
  }
2249
2531
  function buildPlayerMetricEvent() {
2250
2532
  return _async_to_generator(function() {
2251
- var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
2533
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, debugAdTiming, playerId, captureAt;
2252
2534
  var _arguments = arguments;
2253
2535
  return _ts_generator(this, function(_state) {
2254
2536
  switch(_state.label){
2255
2537
  case 0:
2256
2538
  context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2257
2539
  clientInfo = getClientInfo();
2540
+ debugAdTiming = !!context.debugAdTiming;
2541
+ if (debugAdTiming) {
2542
+ console.log("[StormcloudVideoPlayer] clientInfo: ", JSON.stringify(clientInfo, null, 2));
2543
+ }
2258
2544
  return [
2259
2545
  4,
2260
2546
  getBrowserID(clientInfo)
2261
2547
  ];
2262
2548
  case 1:
2263
2549
  playerId = _state.sent();
2550
+ if (debugAdTiming) {
2551
+ console.log("[StormcloudVideoPlayer] playerId: ", playerId);
2552
+ }
2264
2553
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
2265
2554
  return [
2266
2555
  2,
@@ -3104,7 +3393,20 @@ function applyVastMacros(baseUrl, ctx) {
3104
3393
  if (ctx.pod != null) {
3105
3394
  params.set("pod", String(ctx.pod));
3106
3395
  }
3107
- if (ctx.adPosition != null) {
3396
+ var optimizedPodMode = ctx.podMaxAds != null || ctx.podMinDurationMs != null || ctx.podMaxDurationMs != null;
3397
+ if (optimizedPodMode) {
3398
+ params.delete("ppos");
3399
+ params.delete("lip");
3400
+ if (ctx.podMinDurationMs != null) {
3401
+ params.set("pmnd", String(Math.max(0, Math.round(ctx.podMinDurationMs))));
3402
+ }
3403
+ if (ctx.podMaxDurationMs != null) {
3404
+ params.set("pmxd", String(Math.max(0, Math.round(ctx.podMaxDurationMs))));
3405
+ }
3406
+ if (ctx.podMaxAds != null) {
3407
+ params.set("pmad", String(Math.max(1, Math.round(ctx.podMaxAds))));
3408
+ }
3409
+ } else if (ctx.adPosition != null) {
3108
3410
  params.set("ppos", String(ctx.adPosition));
3109
3411
  }
3110
3412
  var requestUrl = ctx.contentUrl || ctx.pageUrl;
@@ -4472,6 +4774,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
4472
4774
  this.podAssignedByPrefetch = false;
4473
4775
  this.adRequestPositionInBreak = 0;
4474
4776
  this.lastHeartbeatTime = 0;
4777
+ this.defaultPodMaxAds = 6;
4475
4778
  this.config = config;
4476
4779
  this.video = video;
4477
4780
  }
@@ -4927,12 +5230,58 @@ var AdConfigManager = /*#__PURE__*/ function() {
4927
5230
  return urls;
4928
5231
  }
4929
5232
  },
5233
+ {
5234
+ key: "resolvePodParams",
5235
+ value: function resolvePodParams(breakDurationMs) {
5236
+ var maxDurationMs = breakDurationMs != null && breakDurationMs > 0 ? breakDurationMs : this.config.podMaxDurationMs;
5237
+ var maxAds = this.config.podMaxAds && this.config.podMaxAds > 0 ? this.config.podMaxAds : this.apiNumberAds && this.apiNumberAds > 0 ? this.apiNumberAds : this.defaultPodMaxAds;
5238
+ var minDurationMs = this.config.podExactDuration && maxDurationMs != null ? maxDurationMs : 0;
5239
+ return {
5240
+ maxAds: maxAds,
5241
+ minDurationMs: minDurationMs,
5242
+ maxDurationMs: maxDurationMs
5243
+ };
5244
+ }
5245
+ },
5246
+ {
5247
+ key: "generatePodVastUrl",
5248
+ value: function generatePodVastUrl(baseUrl, podParams, adPlayer, palNonce, inAdBreak) {
5249
+ var _podParams_maxDurationMs;
5250
+ var adWillPlayMuted = inAdBreak ? adPlayer.getOriginalMutedState() : this.video.muted;
5251
+ var envSignals = resolveVastEnvironmentSignals(!!this.config.ctvAdRequest);
5252
+ var urlWithMacros = applyVastMacros(baseUrl, {
5253
+ correlator: generateCorrelator(),
5254
+ streamCorrelator: this.streamCorrelator,
5255
+ pod: this.podCounter > 0 ? this.podCounter : void 0,
5256
+ podMaxAds: podParams.maxAds,
5257
+ podMinDurationMs: podParams.minDurationMs,
5258
+ podMaxDurationMs: podParams.maxDurationMs,
5259
+ isCtv: envSignals.isCtv,
5260
+ contentUrl: envSignals.contentUrl,
5261
+ pageUrl: !envSignals.isCtv && typeof window !== "undefined" ? window.location.href : void 0,
5262
+ adWillPlayMuted: adWillPlayMuted,
5263
+ adWillAutoPlay: !!this.config.autoplay,
5264
+ appId: envSignals.appId,
5265
+ appName: envSignals.appName,
5266
+ sessionId: envSignals.sessionId,
5267
+ deviceId: envSignals.deviceId,
5268
+ deviceIdType: envSignals.deviceIdType,
5269
+ limitAdTracking: envSignals.limitAdTracking,
5270
+ deviceTypeHint: envSignals.deviceTypeHint,
5271
+ adTest: this.config.adTest,
5272
+ consent: this.consentSignals
5273
+ });
5274
+ var finalUrl = palNonce.injectNonce(urlWithMacros);
5275
+ 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);
5276
+ return finalUrl;
5277
+ }
5278
+ },
4930
5279
  {
4931
5280
  // --- Tracking ---
4932
5281
  key: "initializeTracking",
4933
5282
  value: function initializeTracking() {
4934
5283
  var _this = this;
4935
- sendInitialTracking(this.config.licenseKey).then(function() {
5284
+ sendInitialTracking(this.config.licenseKey, buildAnalyticsContext(this.config)).then(function() {
4936
5285
  _this.heartbeatInterval = window.setInterval(function() {
4937
5286
  _this.sendHeartbeatIfNeeded();
4938
5287
  }, 5e3);
@@ -4953,7 +5302,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
4953
5302
  var now = Date.now();
4954
5303
  if (!this.lastHeartbeatTime || now - this.lastHeartbeatTime > 3e4) {
4955
5304
  this.lastHeartbeatTime = now;
4956
- sendHeartbeat(this.config.licenseKey).catch(function(error) {
5305
+ sendHeartbeat(this.config.licenseKey, buildAnalyticsContext(this.config)).catch(function(error) {
4957
5306
  if (_this.debug) {
4958
5307
  console.warn("[StormcloudVideoPlayer] Failed to send heartbeat:", error);
4959
5308
  }
@@ -5233,6 +5582,13 @@ var AdTimingService = /*#__PURE__*/ function() {
5233
5582
  this.lastAdRequestTime = Date.now();
5234
5583
  }
5235
5584
  },
5585
+ {
5586
+ key: "notePodAdStarted",
5587
+ value: function notePodAdStarted() {
5588
+ this.currentAdIndex++;
5589
+ this.totalAdsInBreak = this.currentAdIndex;
5590
+ }
5591
+ },
5236
5592
  {
5237
5593
  key: "getRemainingAdMs",
5238
5594
  value: function getRemainingAdMs() {
@@ -6739,19 +7095,38 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6739
7095
  }
6740
7096
  },
6741
7097
  {
6742
- // ───────────────────────────────────────────────────────────────
7098
+ key: "analyticsContext",
7099
+ get: function get() {
7100
+ return buildAnalyticsContext(this.host.config);
7101
+ }
7102
+ },
7103
+ {
7104
+ key: "optimizedPodsEnabled",
7105
+ get: // ───────────────────────────────────────────────────────────────
6743
7106
  // IMA event listeners
6744
7107
  // ───────────────────────────────────────────────────────────────
7108
+ function get() {
7109
+ return !!this.host.config.optimizedPods;
7110
+ }
7111
+ },
7112
+ {
6745
7113
  key: "attachImaEventListeners",
6746
7114
  value: function attachImaEventListeners() {
6747
7115
  var _this = this;
6748
7116
  var adPlayer = this.host.getAdPlayer();
7117
+ adPlayer.on("pod_ad_started", function(payload) {
7118
+ _this.timing.notePodAdStarted();
7119
+ if (_this.debug) {
7120
+ var _ref;
7121
+ 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 : "?"));
7122
+ }
7123
+ });
6749
7124
  adPlayer.on("all_ads_completed", function() {
6750
7125
  sendAdImpressionTracking(_this.host.config.licenseKey, {
6751
7126
  source: "hls",
6752
7127
  adIndex: _this.timing.currentAdIndex,
6753
7128
  timestamp: /* @__PURE__ */ new Date().toISOString()
6754
- }).catch(function() {});
7129
+ }, _this.analyticsContext).catch(function() {});
6755
7130
  _this.timing.recordPlayedAdDuration(_this.host.getAdPlayer());
6756
7131
  var remaining = _this.timing.getRemainingAdMs();
6757
7132
  _this.timing.consecutiveFailures = 0;
@@ -6813,7 +7188,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6813
7188
  source: "hls",
6814
7189
  adIndex: _this.timing.currentAdIndex,
6815
7190
  timestamp: /* @__PURE__ */ new Date().toISOString()
6816
- }).catch(function() {});
7191
+ }, _this.analyticsContext).catch(function() {});
6817
7192
  if (!_this.host.video.muted) {
6818
7193
  _this.host.video.muted = true;
6819
7194
  if (_this.debug) {
@@ -6903,7 +7278,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6903
7278
  // ───────────────────────────────────────────────────────────────
6904
7279
  function handleAdStart(_marker) {
6905
7280
  return _async_to_generator(function() {
6906
- var scheduled, tags, baseVastUrl, adBreakDurationMs, mode, currentMuted, currentVolume, firstAdUrl, usePreloadedAd, preloadedController, preloaded, firstAdUrlArray, adPlayer, preservedMuted, preservedVolume, adVolume, adPlayer1, adVolume1, error, fallbackPreloaded, ap, adVolume2, fallbackError;
7281
+ 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;
6907
7282
  return _ts_generator(this, function(_state) {
6908
7283
  switch(_state.label){
6909
7284
  case 0:
@@ -6950,6 +7325,26 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6950
7325
  if (this.timing.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
6951
7326
  this.timing.expectedAdBreakDurationMs = adBreakDurationMs;
6952
7327
  }
7328
+ if (!this.optimizedPodsEnabled) return [
7329
+ 3,
7330
+ 2
7331
+ ];
7332
+ return [
7333
+ 4,
7334
+ this.startOptimizedPod(baseVastUrl, adBreakDurationMs, currentMuted, currentVolume)
7335
+ ];
7336
+ case 1:
7337
+ podPlayed = _state.sent();
7338
+ if (podPlayed) {
7339
+ return [
7340
+ 2
7341
+ ];
7342
+ }
7343
+ if (this.debug) {
7344
+ console.log("[POD] Optimized pod unavailable, falling back to sequential single-ad path");
7345
+ }
7346
+ _state.label = 2;
7347
+ case 2:
6953
7348
  usePreloadedAd = false;
6954
7349
  preloaded = this.preloadPool.getPreloadedAd();
6955
7350
  if (preloaded) {
@@ -6960,7 +7355,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6960
7355
  source: "hls",
6961
7356
  vastUrl: firstAdUrl,
6962
7357
  timestamp: /* @__PURE__ */ new Date().toISOString()
6963
- }).catch(function() {});
7358
+ }, this.analyticsContext).catch(function() {});
6964
7359
  if (this.debug) {
6965
7360
  console.log("[CONTINUOUS-FETCH] Using preloaded ad from pool (preloaded in advance, ready immediately!)");
6966
7361
  }
@@ -6991,21 +7386,21 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6991
7386
  if (this.debug) {
6992
7387
  console.log("[CONTINUOUS-FETCH] First VAST URL:", firstAdUrl);
6993
7388
  }
6994
- _state.label = 1;
6995
- case 1:
7389
+ _state.label = 3;
7390
+ case 3:
6996
7391
  _state.trys.push([
6997
- 1,
6998
- 12,
7392
+ 3,
7393
+ 14,
6999
7394
  ,
7000
- 20
7395
+ 22
7001
7396
  ]);
7002
7397
  if (!(usePreloadedAd && preloadedController)) return [
7003
7398
  3,
7004
- 5
7399
+ 7
7005
7400
  ];
7006
7401
  if (!!this.timing.ensureLoadedAdFitsBudget(preloadedController)) return [
7007
7402
  3,
7008
- 3
7403
+ 5
7009
7404
  ];
7010
7405
  this.timing.rejectLoadedAdForDuration(preloadedController);
7011
7406
  this.startContinuousFetching(baseVastUrl);
@@ -7013,12 +7408,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7013
7408
  4,
7014
7409
  this.tryNextAvailableAdWithRateLimit()
7015
7410
  ];
7016
- case 2:
7411
+ case 4:
7017
7412
  _state.sent();
7018
7413
  return [
7019
7414
  2
7020
7415
  ];
7021
- case 3:
7416
+ case 5:
7022
7417
  adPlayer = this.host.getAdPlayer();
7023
7418
  preservedMuted = adPlayer.getOriginalMutedState();
7024
7419
  preservedVolume = adPlayer.getOriginalVolume();
@@ -7043,7 +7438,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7043
7438
  4,
7044
7439
  preloadedController.play()
7045
7440
  ];
7046
- case 4:
7441
+ case 6:
7047
7442
  _state.sent();
7048
7443
  this.timing.markAdStarted();
7049
7444
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7053,34 +7448,34 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7053
7448
  preloadedController.setAdVolume(adVolume);
7054
7449
  return [
7055
7450
  3,
7056
- 11
7451
+ 13
7057
7452
  ];
7058
- case 5:
7453
+ case 7:
7059
7454
  adPlayer1 = this.host.getAdPlayer();
7060
7455
  return [
7061
7456
  4,
7062
7457
  this.timing.enforceGlobalRateLimit()
7063
7458
  ];
7064
- case 6:
7459
+ case 8:
7065
7460
  _state.sent();
7066
7461
  this.timing.lastAdRequestTime = Date.now();
7067
7462
  return [
7068
7463
  4,
7069
7464
  adPlayer1.requestAds(firstAdUrl)
7070
7465
  ];
7071
- case 7:
7466
+ case 9:
7072
7467
  _state.sent();
7073
7468
  sendAdLoadedTracking(this.host.config.licenseKey, {
7074
7469
  source: "hls",
7075
7470
  vastUrl: firstAdUrl,
7076
7471
  timestamp: /* @__PURE__ */ new Date().toISOString()
7077
- }).catch(function() {});
7472
+ }, this.analyticsContext).catch(function() {});
7078
7473
  if (this.debug) {
7079
7474
  console.log("[CONTINUOUS-FETCH] First ad request successful, starting playback");
7080
7475
  }
7081
7476
  if (!!this.timing.ensureLoadedAdFitsBudget(adPlayer1)) return [
7082
7477
  3,
7083
- 9
7478
+ 11
7084
7479
  ];
7085
7480
  this.timing.rejectLoadedAdForDuration(adPlayer1);
7086
7481
  this.recreateAdController();
@@ -7089,12 +7484,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7089
7484
  4,
7090
7485
  this.tryNextAvailableAdWithRateLimit()
7091
7486
  ];
7092
- case 8:
7487
+ case 10:
7093
7488
  _state.sent();
7094
7489
  return [
7095
7490
  2
7096
7491
  ];
7097
- case 9:
7492
+ case 11:
7098
7493
  this.timing.consecutiveFailures = 0;
7099
7494
  this.startContinuousFetching(baseVastUrl);
7100
7495
  if (!this.preloadPool.active) {
@@ -7104,7 +7499,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7104
7499
  4,
7105
7500
  adPlayer1.play()
7106
7501
  ];
7107
- case 10:
7502
+ case 12:
7108
7503
  _state.sent();
7109
7504
  this.timing.markAdStarted();
7110
7505
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7112,40 +7507,40 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7112
7507
  }
7113
7508
  adVolume1 = currentMuted ? 0 : currentVolume;
7114
7509
  adPlayer1.setAdVolume(adVolume1);
7115
- _state.label = 11;
7116
- case 11:
7510
+ _state.label = 13;
7511
+ case 13:
7117
7512
  return [
7118
7513
  3,
7119
- 20
7514
+ 22
7120
7515
  ];
7121
- case 12:
7516
+ case 14:
7122
7517
  error = _state.sent();
7123
7518
  if (this.debug) {
7124
7519
  console.warn("[CONTINUOUS-FETCH] First ad request failed:", error);
7125
7520
  }
7126
7521
  if (!!usePreloadedAd) return [
7127
7522
  3,
7128
- 18
7523
+ 20
7129
7524
  ];
7130
7525
  fallbackPreloaded = this.preloadPool.getPreloadedAd();
7131
7526
  if (!fallbackPreloaded) return [
7132
7527
  3,
7133
- 18
7528
+ 20
7134
7529
  ];
7135
7530
  if (this.debug) {
7136
7531
  console.log("[CONTINUOUS-FETCH] First ad failed, using preloaded fallback");
7137
7532
  }
7138
- _state.label = 13;
7139
- case 13:
7533
+ _state.label = 15;
7534
+ case 15:
7140
7535
  _state.trys.push([
7141
- 13,
7142
- 17,
7536
+ 15,
7537
+ 19,
7143
7538
  ,
7144
- 18
7539
+ 20
7145
7540
  ]);
7146
7541
  if (!!this.timing.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
7147
7542
  3,
7148
- 15
7543
+ 17
7149
7544
  ];
7150
7545
  this.timing.rejectLoadedAdForDuration(fallbackPreloaded.adController);
7151
7546
  this.startContinuousFetching(baseVastUrl);
@@ -7153,12 +7548,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7153
7548
  4,
7154
7549
  this.tryNextAvailableAdWithRateLimit()
7155
7550
  ];
7156
- case 14:
7551
+ case 16:
7157
7552
  _state.sent();
7158
7553
  return [
7159
7554
  2
7160
7555
  ];
7161
- case 15:
7556
+ case 17:
7162
7557
  this.swapToPreloadedAdPlayer(fallbackPreloaded.adController);
7163
7558
  this.timing.consecutiveFailures = 0;
7164
7559
  this.startContinuousFetching(baseVastUrl);
@@ -7171,7 +7566,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7171
7566
  4,
7172
7567
  ap.play()
7173
7568
  ];
7174
- case 16:
7569
+ case 18:
7175
7570
  _state.sent();
7176
7571
  this.timing.markAdStarted();
7177
7572
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7182,16 +7577,16 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7182
7577
  return [
7183
7578
  2
7184
7579
  ];
7185
- case 17:
7580
+ case 19:
7186
7581
  fallbackError = _state.sent();
7187
7582
  if (this.debug) {
7188
7583
  console.warn("[CONTINUOUS-FETCH] Preloaded fallback also failed:", fallbackError);
7189
7584
  }
7190
7585
  return [
7191
7586
  3,
7192
- 18
7587
+ 20
7193
7588
  ];
7194
- case 18:
7589
+ case 20:
7195
7590
  if (this.timing.isTemporaryAdError(error)) {
7196
7591
  this.timing.temporaryFailureUrls.set(firstAdUrl, Date.now());
7197
7592
  if (this.debug) {
@@ -7209,13 +7604,137 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7209
7604
  4,
7210
7605
  this.tryNextAvailableAdWithRateLimit()
7211
7606
  ];
7212
- case 19:
7607
+ case 21:
7213
7608
  _state.sent();
7214
7609
  return [
7215
7610
  3,
7216
- 20
7611
+ 22
7217
7612
  ];
7218
- case 20:
7613
+ case 22:
7614
+ return [
7615
+ 2
7616
+ ];
7617
+ }
7618
+ });
7619
+ }).call(this);
7620
+ }
7621
+ },
7622
+ {
7623
+ key: "startOptimizedPod",
7624
+ value: // ───────────────────────────────────────────────────────────────
7625
+ // Optimized pod (single multi-ad request)
7626
+ // ───────────────────────────────────────────────────────────────
7627
+ function startOptimizedPod(baseVastUrl, breakDurationMs, currentMuted, currentVolume) {
7628
+ return _async_to_generator(function() {
7629
+ var podUrl, adPlayer, requestToken, _ref, _adPlayer_getPodAdCount, podCount, error;
7630
+ return _ts_generator(this, function(_state) {
7631
+ switch(_state.label){
7632
+ case 0:
7633
+ if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {
7634
+ podUrl = this.pendingAdBreak.vastUrls[0];
7635
+ this.clearPendingAdBreak();
7636
+ } else {
7637
+ podUrl = this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);
7638
+ }
7639
+ if (!podUrl) {
7640
+ return [
7641
+ 2,
7642
+ false
7643
+ ];
7644
+ }
7645
+ this.continuousFetchingActive = false;
7646
+ this.recreateAdController();
7647
+ adPlayer = this.host.getAdPlayer();
7648
+ requestToken = ++this.timing.adRequestTokenCounter;
7649
+ this.timing.activeAdRequestToken = requestToken;
7650
+ this.timing.startAdRequestWatchdog(requestToken);
7651
+ _state.label = 1;
7652
+ case 1:
7653
+ _state.trys.push([
7654
+ 1,
7655
+ 5,
7656
+ ,
7657
+ 6
7658
+ ]);
7659
+ return [
7660
+ 4,
7661
+ this.timing.enforceGlobalRateLimit()
7662
+ ];
7663
+ case 2:
7664
+ _state.sent();
7665
+ this.timing.lastAdRequestTime = Date.now();
7666
+ return [
7667
+ 4,
7668
+ adPlayer.requestAds(podUrl)
7669
+ ];
7670
+ case 3:
7671
+ _state.sent();
7672
+ this.timing.clearAdRequestWatchdog();
7673
+ if (this.timing.activeAdRequestToken !== requestToken) {
7674
+ return [
7675
+ 2,
7676
+ true
7677
+ ];
7678
+ }
7679
+ podCount = (_ref = (_adPlayer_getPodAdCount = adPlayer.getPodAdCount) === null || _adPlayer_getPodAdCount === void 0 ? void 0 : _adPlayer_getPodAdCount.call(adPlayer)) !== null && _ref !== void 0 ? _ref : 1;
7680
+ if (podCount < 1) {
7681
+ return [
7682
+ 2,
7683
+ false
7684
+ ];
7685
+ }
7686
+ if (this.debug) {
7687
+ 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"));
7688
+ }
7689
+ sendAdLoadedTracking(this.host.config.licenseKey, {
7690
+ source: "hls",
7691
+ vastUrl: podUrl,
7692
+ timestamp: /* @__PURE__ */ new Date().toISOString()
7693
+ }, this.analyticsContext).catch(function() {});
7694
+ this.timing.startAdFailsafeTimer(requestToken);
7695
+ return [
7696
+ 4,
7697
+ adPlayer.play()
7698
+ ];
7699
+ case 4:
7700
+ _state.sent();
7701
+ this.timing.markAdStarted();
7702
+ if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
7703
+ this.timing.scheduleAdStopAtBreakBoundary();
7704
+ }
7705
+ adPlayer.setAdVolume(currentMuted ? 0 : currentVolume);
7706
+ this.timing.consecutiveFailures = 0;
7707
+ if (podCount === 1) {
7708
+ this.continuousFetchingActive = true;
7709
+ this.startContinuousFetching(baseVastUrl);
7710
+ if (!this.preloadPool.active) {
7711
+ this.preloadPool.startPreloadPool(baseVastUrl, []);
7712
+ }
7713
+ }
7714
+ return [
7715
+ 2,
7716
+ true
7717
+ ];
7718
+ case 5:
7719
+ error = _state.sent();
7720
+ this.timing.clearAdRequestWatchdog();
7721
+ this.timing.clearAdFailsafeTimer();
7722
+ if (this.timing.activeAdRequestToken === requestToken) {
7723
+ this.timing.activeAdRequestToken = null;
7724
+ }
7725
+ if (this.debug) {
7726
+ console.warn("[POD] Optimized pod request/play failed:", error);
7727
+ }
7728
+ if (this.timing.isTemporaryAdError(error)) {
7729
+ this.timing.temporaryFailureUrls.set(podUrl, Date.now());
7730
+ }
7731
+ this.continuousFetchingActive = true;
7732
+ this.recreateAdController();
7733
+ return [
7734
+ 2,
7735
+ false
7736
+ ];
7737
+ case 6:
7219
7738
  return [
7220
7739
  2
7221
7740
  ];
@@ -7538,7 +8057,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7538
8057
  source: "hls",
7539
8058
  vastUrl: preloaded.vastUrl,
7540
8059
  timestamp: /* @__PURE__ */ new Date().toISOString()
7541
- }).catch(function() {});
8060
+ }, this.analyticsContext).catch(function() {});
7542
8061
  _state.label = 1;
7543
8062
  case 1:
7544
8063
  _state.trys.push([
@@ -7771,7 +8290,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7771
8290
  source: "hls",
7772
8291
  vastUrl: vastTagUrl,
7773
8292
  timestamp: /* @__PURE__ */ new Date().toISOString()
7774
- }).catch(function() {});
8293
+ }, this.analyticsContext).catch(function() {});
7775
8294
  this.timing.clearAdRequestWatchdog();
7776
8295
  if (this.timing.activeAdRequestToken !== requestToken) return [
7777
8296
  2
@@ -8298,6 +8817,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8298
8817
  },
8299
8818
  generateVastUrls: function generateVastUrls(base, count) {
8300
8819
  return _this.generateVastUrls(base, count);
8820
+ },
8821
+ generatePodVastUrl: function generatePodVastUrl(base, breakDurationMs) {
8822
+ return _this.generatePodVastUrl(base, breakDurationMs);
8301
8823
  }
8302
8824
  }, this.timing, this.preloadPool, this.adConfig, this.cueManager, this.placeholder);
8303
8825
  }
@@ -8330,6 +8852,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8330
8852
  return this.adConfig.generateVastUrlsWithCorrelators(baseUrl, count, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8331
8853
  }
8332
8854
  },
8855
+ {
8856
+ key: "generatePodVastUrl",
8857
+ value: function generatePodVastUrl(baseUrl, breakDurationMs) {
8858
+ var podParams = this.adConfig.resolvePodParams(breakDurationMs);
8859
+ return this.adConfig.generatePodVastUrl(baseUrl, podParams, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8860
+ }
8861
+ },
8333
8862
  {
8334
8863
  key: "load",
8335
8864
  value: // ───────────────────────────────────────────────────────────────
@@ -8564,7 +9093,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8564
9093
  sendAdDetectTracking(this.config.licenseKey, _object_spread({
8565
9094
  source: "scte35",
8566
9095
  timestamp: /* @__PURE__ */ new Date().toISOString()
8567
- }, detectPayload)).catch(function() {});
9096
+ }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8568
9097
  }
8569
9098
  var hasPrefetchedAds = this.adBreak.pendingAdBreak && this.adBreak.pendingAdBreak.vastUrls.length > 0;
8570
9099
  var durationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : ((_this_adBreak_pendingAdBreak = this.adBreak.pendingAdBreak) === null || _this_adBreak_pendingAdBreak === void 0 ? void 0 : _this_adBreak_pendingAdBreak.marker.durationSeconds) != null ? this.adBreak.pendingAdBreak.marker.durationSeconds * 1e3 : void 0;
@@ -8718,7 +9247,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8718
9247
  }
8719
9248
  this.adConfig.beginNewAdPod();
8720
9249
  this.adConfig.podAssignedByPrefetch = true;
8721
- var generatedUrls = this.generateVastUrls(baseVastUrl, 1);
9250
+ var optimizedPods = !!this.config.optimizedPods;
9251
+ var breakDurationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : void 0;
9252
+ var generatedUrls = optimizedPods ? [
9253
+ this.generatePodVastUrl(baseVastUrl, breakDurationMs)
9254
+ ] : this.generateVastUrls(baseVastUrl, 1);
8722
9255
  this.adBreak.pendingAdBreak = _object_spread_props(_object_spread({
8723
9256
  marker: marker
8724
9257
  }, fragmentSn !== void 0 ? {
@@ -8737,10 +9270,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8737
9270
  sendAdDetectTracking(this.config.licenseKey, _object_spread({
8738
9271
  source: "scte35",
8739
9272
  timestamp: /* @__PURE__ */ new Date().toISOString()
8740
- }, detectPayload)).catch(function() {});
9273
+ }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8741
9274
  if (this.debug) {
8742
9275
  console.log("[PREFETCH] Starting ad prefetch for upcoming ad break");
8743
- console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
9276
+ console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " ").concat(optimizedPods ? "pod" : "VAST", " URL(s)"));
9277
+ }
9278
+ if (optimizedPods) {
9279
+ if (this.debug) {
9280
+ console.log("[PREFETCH] Optimized pods enabled - skipping single-ad preload pool");
9281
+ }
9282
+ return;
8744
9283
  }
8745
9284
  this.preloadPool.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
8746
9285
  }
@@ -11539,5 +12078,5 @@ var createStormcloudPlayer = function createStormcloudPlayer(playerList, fallbac
11539
12078
  };
11540
12079
  var StormcloudPlayer = createStormcloudPlayer(players_default, players_default[players_default.length - 1]);
11541
12080
  var StormcloudPlayer_default = StormcloudPlayer;
11542
- export { DEFAULT_MQTT_CONFIG, IS_BROWSER, IS_GLOBAL, IS_IOS, IS_SAFARI, MQTT_CA_CERT_FILE, SUPPORTS_DASH, SUPPORTS_HLS, StormcloudPlayer_default as StormcloudPlayer, StormcloudVideoPlayer, StormcloudVideoPlayerComponent, applyMQTTConfig, buildMQTTBrokerUrl, buildPlayerTopic, canPlay, configureMQTT, createHlsAdPlayer, createPalNonceManager, createStormcloudPlayer, StormcloudVideoPlayerComponent as default, detectBrowser, disconnectMQTT, ensureMQTTClient, getBrowserConfigOverrides, getBrowserID, getClientInfo, getMQTTStatus, initMQTTClient, initializePolyfills, isMQTTConfigured, isMQTTConnected, isMQTTEnabled, isMediaStream, lazy, logBrowserInfo, merge, mqttConfig, omit, parseQuery, players_default as players, publishMQTT, randomString, sendAdDetectTracking, sendAdImpressionTracking, sendAdLoadedTracking, sendHeartbeat, sendInitialTracking, supportsFeature, supportsModernJS, supportsWebKitPresentationMode };
12081
+ export { DEFAULT_MQTT_CONFIG, IS_BROWSER, IS_GLOBAL, IS_IOS, IS_SAFARI, MQTT_CA_CERT_FILE, SUPPORTS_DASH, SUPPORTS_HLS, StormcloudPlayer_default as StormcloudPlayer, StormcloudVideoPlayer, StormcloudVideoPlayerComponent, applyMQTTConfig, buildAnalyticsContext, buildMQTTBrokerUrl, buildPlayerTopic, canPlay, configureMQTT, createHlsAdPlayer, createPalNonceManager, createStormcloudPlayer, StormcloudVideoPlayerComponent as default, detectBrowser, disconnectMQTT, ensureMQTTClient, getBrowserConfigOverrides, getBrowserID, getClientInfo, getMQTTStatus, initMQTTClient, initializePolyfills, isMQTTConfigured, isMQTTConnected, isMQTTEnabled, isMediaStream, lazy, logBrowserInfo, merge, mqttConfig, omit, parseQuery, players_default as players, publishMQTT, randomString, sendAdDetectTracking, sendAdImpressionTracking, sendAdLoadedTracking, sendHeartbeat, sendInitialTracking, supportsFeature, supportsModernJS, supportsWebKitPresentationMode };
11543
12082
  //# sourceMappingURL=index.js.map