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
@@ -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
  }
@@ -1945,6 +2217,16 @@ function normalizeBoolean(value) {
1945
2217
  }
1946
2218
  // src/utils/tracking.ts
1947
2219
  var cachedBrowserId = null;
2220
+ function buildAnalyticsContext(config) {
2221
+ var context = {};
2222
+ if (config.debugAdTiming) {
2223
+ context.debugAdTiming = true;
2224
+ }
2225
+ if (config.isLiveStream !== void 0) {
2226
+ context.inputStreamType = config.isLiveStream ? "live" : "vod";
2227
+ }
2228
+ return context;
2229
+ }
1948
2230
  function getClientInfo() {
1949
2231
  var _screen, _screen1, _screen2, _screen3, _screen_orientation, _screen4, _screen5, _window, _window1, _window_screen_orientation, _window_screen, _navigator_languages;
1950
2232
  var ua = navigator.userAgent;
@@ -2203,19 +2485,26 @@ function canPublish(licenseKey) {
2203
2485
  }
2204
2486
  function buildPlayerMetricEvent() {
2205
2487
  return _async_to_generator(function() {
2206
- var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
2488
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, debugAdTiming, playerId, captureAt;
2207
2489
  var _arguments = arguments;
2208
2490
  return _ts_generator(this, function(_state) {
2209
2491
  switch(_state.label){
2210
2492
  case 0:
2211
2493
  context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2212
2494
  clientInfo = getClientInfo();
2495
+ debugAdTiming = !!context.debugAdTiming;
2496
+ if (debugAdTiming) {
2497
+ console.log("[StormcloudVideoPlayer] clientInfo: ", JSON.stringify(clientInfo, null, 2));
2498
+ }
2213
2499
  return [
2214
2500
  4,
2215
2501
  getBrowserID(clientInfo)
2216
2502
  ];
2217
2503
  case 1:
2218
2504
  playerId = _state.sent();
2505
+ if (debugAdTiming) {
2506
+ console.log("[StormcloudVideoPlayer] playerId: ", playerId);
2507
+ }
2219
2508
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
2220
2509
  return [
2221
2510
  2,
@@ -3057,7 +3346,20 @@ function applyVastMacros(baseUrl, ctx) {
3057
3346
  if (ctx.pod != null) {
3058
3347
  params.set("pod", String(ctx.pod));
3059
3348
  }
3060
- if (ctx.adPosition != null) {
3349
+ var optimizedPodMode = ctx.podMaxAds != null || ctx.podMinDurationMs != null || ctx.podMaxDurationMs != null;
3350
+ if (optimizedPodMode) {
3351
+ params.delete("ppos");
3352
+ params.delete("lip");
3353
+ if (ctx.podMinDurationMs != null) {
3354
+ params.set("pmnd", String(Math.max(0, Math.round(ctx.podMinDurationMs))));
3355
+ }
3356
+ if (ctx.podMaxDurationMs != null) {
3357
+ params.set("pmxd", String(Math.max(0, Math.round(ctx.podMaxDurationMs))));
3358
+ }
3359
+ if (ctx.podMaxAds != null) {
3360
+ params.set("pmad", String(Math.max(1, Math.round(ctx.podMaxAds))));
3361
+ }
3362
+ } else if (ctx.adPosition != null) {
3061
3363
  params.set("ppos", String(ctx.adPosition));
3062
3364
  }
3063
3365
  var requestUrl = ctx.contentUrl || ctx.pageUrl;
@@ -4399,6 +4701,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
4399
4701
  this.podAssignedByPrefetch = false;
4400
4702
  this.adRequestPositionInBreak = 0;
4401
4703
  this.lastHeartbeatTime = 0;
4704
+ this.defaultPodMaxAds = 6;
4402
4705
  this.config = config;
4403
4706
  this.video = video;
4404
4707
  }
@@ -4854,12 +5157,58 @@ var AdConfigManager = /*#__PURE__*/ function() {
4854
5157
  return urls;
4855
5158
  }
4856
5159
  },
5160
+ {
5161
+ key: "resolvePodParams",
5162
+ value: function resolvePodParams(breakDurationMs) {
5163
+ var maxDurationMs = breakDurationMs != null && breakDurationMs > 0 ? breakDurationMs : this.config.podMaxDurationMs;
5164
+ var maxAds = this.config.podMaxAds && this.config.podMaxAds > 0 ? this.config.podMaxAds : this.apiNumberAds && this.apiNumberAds > 0 ? this.apiNumberAds : this.defaultPodMaxAds;
5165
+ var minDurationMs = this.config.podExactDuration && maxDurationMs != null ? maxDurationMs : 0;
5166
+ return {
5167
+ maxAds: maxAds,
5168
+ minDurationMs: minDurationMs,
5169
+ maxDurationMs: maxDurationMs
5170
+ };
5171
+ }
5172
+ },
5173
+ {
5174
+ key: "generatePodVastUrl",
5175
+ value: function generatePodVastUrl(baseUrl, podParams, adPlayer, palNonce, inAdBreak) {
5176
+ var _podParams_maxDurationMs;
5177
+ var adWillPlayMuted = inAdBreak ? adPlayer.getOriginalMutedState() : this.video.muted;
5178
+ var envSignals = resolveVastEnvironmentSignals(!!this.config.ctvAdRequest);
5179
+ var urlWithMacros = applyVastMacros(baseUrl, {
5180
+ correlator: generateCorrelator(),
5181
+ streamCorrelator: this.streamCorrelator,
5182
+ pod: this.podCounter > 0 ? this.podCounter : void 0,
5183
+ podMaxAds: podParams.maxAds,
5184
+ podMinDurationMs: podParams.minDurationMs,
5185
+ podMaxDurationMs: podParams.maxDurationMs,
5186
+ isCtv: envSignals.isCtv,
5187
+ contentUrl: envSignals.contentUrl,
5188
+ pageUrl: !envSignals.isCtv && typeof window !== "undefined" ? window.location.href : void 0,
5189
+ adWillPlayMuted: adWillPlayMuted,
5190
+ adWillAutoPlay: !!this.config.autoplay,
5191
+ appId: envSignals.appId,
5192
+ appName: envSignals.appName,
5193
+ sessionId: envSignals.sessionId,
5194
+ deviceId: envSignals.deviceId,
5195
+ deviceIdType: envSignals.deviceIdType,
5196
+ limitAdTracking: envSignals.limitAdTracking,
5197
+ deviceTypeHint: envSignals.deviceTypeHint,
5198
+ adTest: this.config.adTest,
5199
+ consent: this.consentSignals
5200
+ });
5201
+ var finalUrl = palNonce.injectNonce(urlWithMacros);
5202
+ 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);
5203
+ return finalUrl;
5204
+ }
5205
+ },
4857
5206
  {
4858
5207
  // --- Tracking ---
4859
5208
  key: "initializeTracking",
4860
5209
  value: function initializeTracking() {
4861
5210
  var _this = this;
4862
- sendInitialTracking(this.config.licenseKey).then(function() {
5211
+ sendInitialTracking(this.config.licenseKey, buildAnalyticsContext(this.config)).then(function() {
4863
5212
  _this.heartbeatInterval = window.setInterval(function() {
4864
5213
  _this.sendHeartbeatIfNeeded();
4865
5214
  }, 5e3);
@@ -4880,7 +5229,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
4880
5229
  var now = Date.now();
4881
5230
  if (!this.lastHeartbeatTime || now - this.lastHeartbeatTime > 3e4) {
4882
5231
  this.lastHeartbeatTime = now;
4883
- sendHeartbeat(this.config.licenseKey).catch(function(error) {
5232
+ sendHeartbeat(this.config.licenseKey, buildAnalyticsContext(this.config)).catch(function(error) {
4884
5233
  if (_this.debug) {
4885
5234
  console.warn("[StormcloudVideoPlayer] Failed to send heartbeat:", error);
4886
5235
  }
@@ -5159,6 +5508,13 @@ var AdTimingService = /*#__PURE__*/ function() {
5159
5508
  this.lastAdRequestTime = Date.now();
5160
5509
  }
5161
5510
  },
5511
+ {
5512
+ key: "notePodAdStarted",
5513
+ value: function notePodAdStarted() {
5514
+ this.currentAdIndex++;
5515
+ this.totalAdsInBreak = this.currentAdIndex;
5516
+ }
5517
+ },
5162
5518
  {
5163
5519
  key: "getRemainingAdMs",
5164
5520
  value: function getRemainingAdMs() {
@@ -6661,19 +7017,38 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6661
7017
  }
6662
7018
  },
6663
7019
  {
6664
- // ───────────────────────────────────────────────────────────────
7020
+ key: "analyticsContext",
7021
+ get: function get() {
7022
+ return buildAnalyticsContext(this.host.config);
7023
+ }
7024
+ },
7025
+ {
7026
+ key: "optimizedPodsEnabled",
7027
+ get: // ───────────────────────────────────────────────────────────────
6665
7028
  // IMA event listeners
6666
7029
  // ───────────────────────────────────────────────────────────────
7030
+ function get() {
7031
+ return !!this.host.config.optimizedPods;
7032
+ }
7033
+ },
7034
+ {
6667
7035
  key: "attachImaEventListeners",
6668
7036
  value: function attachImaEventListeners() {
6669
7037
  var _this = this;
6670
7038
  var adPlayer = this.host.getAdPlayer();
7039
+ adPlayer.on("pod_ad_started", function(payload) {
7040
+ _this.timing.notePodAdStarted();
7041
+ if (_this.debug) {
7042
+ var _ref;
7043
+ 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 : "?"));
7044
+ }
7045
+ });
6671
7046
  adPlayer.on("all_ads_completed", function() {
6672
7047
  sendAdImpressionTracking(_this.host.config.licenseKey, {
6673
7048
  source: "hls",
6674
7049
  adIndex: _this.timing.currentAdIndex,
6675
7050
  timestamp: /* @__PURE__ */ new Date().toISOString()
6676
- }).catch(function() {});
7051
+ }, _this.analyticsContext).catch(function() {});
6677
7052
  _this.timing.recordPlayedAdDuration(_this.host.getAdPlayer());
6678
7053
  var remaining = _this.timing.getRemainingAdMs();
6679
7054
  _this.timing.consecutiveFailures = 0;
@@ -6735,7 +7110,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6735
7110
  source: "hls",
6736
7111
  adIndex: _this.timing.currentAdIndex,
6737
7112
  timestamp: /* @__PURE__ */ new Date().toISOString()
6738
- }).catch(function() {});
7113
+ }, _this.analyticsContext).catch(function() {});
6739
7114
  if (!_this.host.video.muted) {
6740
7115
  _this.host.video.muted = true;
6741
7116
  if (_this.debug) {
@@ -6825,7 +7200,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6825
7200
  // ───────────────────────────────────────────────────────────────
6826
7201
  function handleAdStart(_marker) {
6827
7202
  return _async_to_generator(function() {
6828
- var scheduled, tags, baseVastUrl, adBreakDurationMs, mode, currentMuted, currentVolume, firstAdUrl, usePreloadedAd, preloadedController, preloaded, firstAdUrlArray, adPlayer, preservedMuted, preservedVolume, adVolume, adPlayer1, adVolume1, error, fallbackPreloaded, ap, adVolume2, fallbackError;
7203
+ 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;
6829
7204
  return _ts_generator(this, function(_state) {
6830
7205
  switch(_state.label){
6831
7206
  case 0:
@@ -6872,6 +7247,26 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6872
7247
  if (this.timing.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
6873
7248
  this.timing.expectedAdBreakDurationMs = adBreakDurationMs;
6874
7249
  }
7250
+ if (!this.optimizedPodsEnabled) return [
7251
+ 3,
7252
+ 2
7253
+ ];
7254
+ return [
7255
+ 4,
7256
+ this.startOptimizedPod(baseVastUrl, adBreakDurationMs, currentMuted, currentVolume)
7257
+ ];
7258
+ case 1:
7259
+ podPlayed = _state.sent();
7260
+ if (podPlayed) {
7261
+ return [
7262
+ 2
7263
+ ];
7264
+ }
7265
+ if (this.debug) {
7266
+ console.log("[POD] Optimized pod unavailable, falling back to sequential single-ad path");
7267
+ }
7268
+ _state.label = 2;
7269
+ case 2:
6875
7270
  usePreloadedAd = false;
6876
7271
  preloaded = this.preloadPool.getPreloadedAd();
6877
7272
  if (preloaded) {
@@ -6882,7 +7277,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6882
7277
  source: "hls",
6883
7278
  vastUrl: firstAdUrl,
6884
7279
  timestamp: /* @__PURE__ */ new Date().toISOString()
6885
- }).catch(function() {});
7280
+ }, this.analyticsContext).catch(function() {});
6886
7281
  if (this.debug) {
6887
7282
  console.log("[CONTINUOUS-FETCH] Using preloaded ad from pool (preloaded in advance, ready immediately!)");
6888
7283
  }
@@ -6913,21 +7308,21 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6913
7308
  if (this.debug) {
6914
7309
  console.log("[CONTINUOUS-FETCH] First VAST URL:", firstAdUrl);
6915
7310
  }
6916
- _state.label = 1;
6917
- case 1:
7311
+ _state.label = 3;
7312
+ case 3:
6918
7313
  _state.trys.push([
6919
- 1,
6920
- 12,
7314
+ 3,
7315
+ 14,
6921
7316
  ,
6922
- 20
7317
+ 22
6923
7318
  ]);
6924
7319
  if (!(usePreloadedAd && preloadedController)) return [
6925
7320
  3,
6926
- 5
7321
+ 7
6927
7322
  ];
6928
7323
  if (!!this.timing.ensureLoadedAdFitsBudget(preloadedController)) return [
6929
7324
  3,
6930
- 3
7325
+ 5
6931
7326
  ];
6932
7327
  this.timing.rejectLoadedAdForDuration(preloadedController);
6933
7328
  this.startContinuousFetching(baseVastUrl);
@@ -6935,12 +7330,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6935
7330
  4,
6936
7331
  this.tryNextAvailableAdWithRateLimit()
6937
7332
  ];
6938
- case 2:
7333
+ case 4:
6939
7334
  _state.sent();
6940
7335
  return [
6941
7336
  2
6942
7337
  ];
6943
- case 3:
7338
+ case 5:
6944
7339
  adPlayer = this.host.getAdPlayer();
6945
7340
  preservedMuted = adPlayer.getOriginalMutedState();
6946
7341
  preservedVolume = adPlayer.getOriginalVolume();
@@ -6965,7 +7360,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6965
7360
  4,
6966
7361
  preloadedController.play()
6967
7362
  ];
6968
- case 4:
7363
+ case 6:
6969
7364
  _state.sent();
6970
7365
  this.timing.markAdStarted();
6971
7366
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -6975,34 +7370,34 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6975
7370
  preloadedController.setAdVolume(adVolume);
6976
7371
  return [
6977
7372
  3,
6978
- 11
7373
+ 13
6979
7374
  ];
6980
- case 5:
7375
+ case 7:
6981
7376
  adPlayer1 = this.host.getAdPlayer();
6982
7377
  return [
6983
7378
  4,
6984
7379
  this.timing.enforceGlobalRateLimit()
6985
7380
  ];
6986
- case 6:
7381
+ case 8:
6987
7382
  _state.sent();
6988
7383
  this.timing.lastAdRequestTime = Date.now();
6989
7384
  return [
6990
7385
  4,
6991
7386
  adPlayer1.requestAds(firstAdUrl)
6992
7387
  ];
6993
- case 7:
7388
+ case 9:
6994
7389
  _state.sent();
6995
7390
  sendAdLoadedTracking(this.host.config.licenseKey, {
6996
7391
  source: "hls",
6997
7392
  vastUrl: firstAdUrl,
6998
7393
  timestamp: /* @__PURE__ */ new Date().toISOString()
6999
- }).catch(function() {});
7394
+ }, this.analyticsContext).catch(function() {});
7000
7395
  if (this.debug) {
7001
7396
  console.log("[CONTINUOUS-FETCH] First ad request successful, starting playback");
7002
7397
  }
7003
7398
  if (!!this.timing.ensureLoadedAdFitsBudget(adPlayer1)) return [
7004
7399
  3,
7005
- 9
7400
+ 11
7006
7401
  ];
7007
7402
  this.timing.rejectLoadedAdForDuration(adPlayer1);
7008
7403
  this.recreateAdController();
@@ -7011,12 +7406,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7011
7406
  4,
7012
7407
  this.tryNextAvailableAdWithRateLimit()
7013
7408
  ];
7014
- case 8:
7409
+ case 10:
7015
7410
  _state.sent();
7016
7411
  return [
7017
7412
  2
7018
7413
  ];
7019
- case 9:
7414
+ case 11:
7020
7415
  this.timing.consecutiveFailures = 0;
7021
7416
  this.startContinuousFetching(baseVastUrl);
7022
7417
  if (!this.preloadPool.active) {
@@ -7026,7 +7421,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7026
7421
  4,
7027
7422
  adPlayer1.play()
7028
7423
  ];
7029
- case 10:
7424
+ case 12:
7030
7425
  _state.sent();
7031
7426
  this.timing.markAdStarted();
7032
7427
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7034,40 +7429,40 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7034
7429
  }
7035
7430
  adVolume1 = currentMuted ? 0 : currentVolume;
7036
7431
  adPlayer1.setAdVolume(adVolume1);
7037
- _state.label = 11;
7038
- case 11:
7432
+ _state.label = 13;
7433
+ case 13:
7039
7434
  return [
7040
7435
  3,
7041
- 20
7436
+ 22
7042
7437
  ];
7043
- case 12:
7438
+ case 14:
7044
7439
  error = _state.sent();
7045
7440
  if (this.debug) {
7046
7441
  console.warn("[CONTINUOUS-FETCH] First ad request failed:", error);
7047
7442
  }
7048
7443
  if (!!usePreloadedAd) return [
7049
7444
  3,
7050
- 18
7445
+ 20
7051
7446
  ];
7052
7447
  fallbackPreloaded = this.preloadPool.getPreloadedAd();
7053
7448
  if (!fallbackPreloaded) return [
7054
7449
  3,
7055
- 18
7450
+ 20
7056
7451
  ];
7057
7452
  if (this.debug) {
7058
7453
  console.log("[CONTINUOUS-FETCH] First ad failed, using preloaded fallback");
7059
7454
  }
7060
- _state.label = 13;
7061
- case 13:
7455
+ _state.label = 15;
7456
+ case 15:
7062
7457
  _state.trys.push([
7063
- 13,
7064
- 17,
7458
+ 15,
7459
+ 19,
7065
7460
  ,
7066
- 18
7461
+ 20
7067
7462
  ]);
7068
7463
  if (!!this.timing.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
7069
7464
  3,
7070
- 15
7465
+ 17
7071
7466
  ];
7072
7467
  this.timing.rejectLoadedAdForDuration(fallbackPreloaded.adController);
7073
7468
  this.startContinuousFetching(baseVastUrl);
@@ -7075,12 +7470,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7075
7470
  4,
7076
7471
  this.tryNextAvailableAdWithRateLimit()
7077
7472
  ];
7078
- case 14:
7473
+ case 16:
7079
7474
  _state.sent();
7080
7475
  return [
7081
7476
  2
7082
7477
  ];
7083
- case 15:
7478
+ case 17:
7084
7479
  this.swapToPreloadedAdPlayer(fallbackPreloaded.adController);
7085
7480
  this.timing.consecutiveFailures = 0;
7086
7481
  this.startContinuousFetching(baseVastUrl);
@@ -7093,7 +7488,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7093
7488
  4,
7094
7489
  ap.play()
7095
7490
  ];
7096
- case 16:
7491
+ case 18:
7097
7492
  _state.sent();
7098
7493
  this.timing.markAdStarted();
7099
7494
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7104,16 +7499,16 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7104
7499
  return [
7105
7500
  2
7106
7501
  ];
7107
- case 17:
7502
+ case 19:
7108
7503
  fallbackError = _state.sent();
7109
7504
  if (this.debug) {
7110
7505
  console.warn("[CONTINUOUS-FETCH] Preloaded fallback also failed:", fallbackError);
7111
7506
  }
7112
7507
  return [
7113
7508
  3,
7114
- 18
7509
+ 20
7115
7510
  ];
7116
- case 18:
7511
+ case 20:
7117
7512
  if (this.timing.isTemporaryAdError(error)) {
7118
7513
  this.timing.temporaryFailureUrls.set(firstAdUrl, Date.now());
7119
7514
  if (this.debug) {
@@ -7131,13 +7526,137 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7131
7526
  4,
7132
7527
  this.tryNextAvailableAdWithRateLimit()
7133
7528
  ];
7134
- case 19:
7529
+ case 21:
7135
7530
  _state.sent();
7136
7531
  return [
7137
7532
  3,
7138
- 20
7533
+ 22
7139
7534
  ];
7140
- case 20:
7535
+ case 22:
7536
+ return [
7537
+ 2
7538
+ ];
7539
+ }
7540
+ });
7541
+ }).call(this);
7542
+ }
7543
+ },
7544
+ {
7545
+ key: "startOptimizedPod",
7546
+ value: // ───────────────────────────────────────────────────────────────
7547
+ // Optimized pod (single multi-ad request)
7548
+ // ───────────────────────────────────────────────────────────────
7549
+ function startOptimizedPod(baseVastUrl, breakDurationMs, currentMuted, currentVolume) {
7550
+ return _async_to_generator(function() {
7551
+ var podUrl, adPlayer, requestToken, _ref, _adPlayer_getPodAdCount, podCount, error;
7552
+ return _ts_generator(this, function(_state) {
7553
+ switch(_state.label){
7554
+ case 0:
7555
+ if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {
7556
+ podUrl = this.pendingAdBreak.vastUrls[0];
7557
+ this.clearPendingAdBreak();
7558
+ } else {
7559
+ podUrl = this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);
7560
+ }
7561
+ if (!podUrl) {
7562
+ return [
7563
+ 2,
7564
+ false
7565
+ ];
7566
+ }
7567
+ this.continuousFetchingActive = false;
7568
+ this.recreateAdController();
7569
+ adPlayer = this.host.getAdPlayer();
7570
+ requestToken = ++this.timing.adRequestTokenCounter;
7571
+ this.timing.activeAdRequestToken = requestToken;
7572
+ this.timing.startAdRequestWatchdog(requestToken);
7573
+ _state.label = 1;
7574
+ case 1:
7575
+ _state.trys.push([
7576
+ 1,
7577
+ 5,
7578
+ ,
7579
+ 6
7580
+ ]);
7581
+ return [
7582
+ 4,
7583
+ this.timing.enforceGlobalRateLimit()
7584
+ ];
7585
+ case 2:
7586
+ _state.sent();
7587
+ this.timing.lastAdRequestTime = Date.now();
7588
+ return [
7589
+ 4,
7590
+ adPlayer.requestAds(podUrl)
7591
+ ];
7592
+ case 3:
7593
+ _state.sent();
7594
+ this.timing.clearAdRequestWatchdog();
7595
+ if (this.timing.activeAdRequestToken !== requestToken) {
7596
+ return [
7597
+ 2,
7598
+ true
7599
+ ];
7600
+ }
7601
+ podCount = (_ref = (_adPlayer_getPodAdCount = adPlayer.getPodAdCount) === null || _adPlayer_getPodAdCount === void 0 ? void 0 : _adPlayer_getPodAdCount.call(adPlayer)) !== null && _ref !== void 0 ? _ref : 1;
7602
+ if (podCount < 1) {
7603
+ return [
7604
+ 2,
7605
+ false
7606
+ ];
7607
+ }
7608
+ if (this.debug) {
7609
+ 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"));
7610
+ }
7611
+ sendAdLoadedTracking(this.host.config.licenseKey, {
7612
+ source: "hls",
7613
+ vastUrl: podUrl,
7614
+ timestamp: /* @__PURE__ */ new Date().toISOString()
7615
+ }, this.analyticsContext).catch(function() {});
7616
+ this.timing.startAdFailsafeTimer(requestToken);
7617
+ return [
7618
+ 4,
7619
+ adPlayer.play()
7620
+ ];
7621
+ case 4:
7622
+ _state.sent();
7623
+ this.timing.markAdStarted();
7624
+ if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
7625
+ this.timing.scheduleAdStopAtBreakBoundary();
7626
+ }
7627
+ adPlayer.setAdVolume(currentMuted ? 0 : currentVolume);
7628
+ this.timing.consecutiveFailures = 0;
7629
+ if (podCount === 1) {
7630
+ this.continuousFetchingActive = true;
7631
+ this.startContinuousFetching(baseVastUrl);
7632
+ if (!this.preloadPool.active) {
7633
+ this.preloadPool.startPreloadPool(baseVastUrl, []);
7634
+ }
7635
+ }
7636
+ return [
7637
+ 2,
7638
+ true
7639
+ ];
7640
+ case 5:
7641
+ error = _state.sent();
7642
+ this.timing.clearAdRequestWatchdog();
7643
+ this.timing.clearAdFailsafeTimer();
7644
+ if (this.timing.activeAdRequestToken === requestToken) {
7645
+ this.timing.activeAdRequestToken = null;
7646
+ }
7647
+ if (this.debug) {
7648
+ console.warn("[POD] Optimized pod request/play failed:", error);
7649
+ }
7650
+ if (this.timing.isTemporaryAdError(error)) {
7651
+ this.timing.temporaryFailureUrls.set(podUrl, Date.now());
7652
+ }
7653
+ this.continuousFetchingActive = true;
7654
+ this.recreateAdController();
7655
+ return [
7656
+ 2,
7657
+ false
7658
+ ];
7659
+ case 6:
7141
7660
  return [
7142
7661
  2
7143
7662
  ];
@@ -7460,7 +7979,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7460
7979
  source: "hls",
7461
7980
  vastUrl: preloaded.vastUrl,
7462
7981
  timestamp: /* @__PURE__ */ new Date().toISOString()
7463
- }).catch(function() {});
7982
+ }, this.analyticsContext).catch(function() {});
7464
7983
  _state.label = 1;
7465
7984
  case 1:
7466
7985
  _state.trys.push([
@@ -7693,7 +8212,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7693
8212
  source: "hls",
7694
8213
  vastUrl: vastTagUrl,
7695
8214
  timestamp: /* @__PURE__ */ new Date().toISOString()
7696
- }).catch(function() {});
8215
+ }, this.analyticsContext).catch(function() {});
7697
8216
  this.timing.clearAdRequestWatchdog();
7698
8217
  if (this.timing.activeAdRequestToken !== requestToken) return [
7699
8218
  2
@@ -8219,6 +8738,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8219
8738
  },
8220
8739
  generateVastUrls: function generateVastUrls(base, count) {
8221
8740
  return _this.generateVastUrls(base, count);
8741
+ },
8742
+ generatePodVastUrl: function generatePodVastUrl(base, breakDurationMs) {
8743
+ return _this.generatePodVastUrl(base, breakDurationMs);
8222
8744
  }
8223
8745
  }, this.timing, this.preloadPool, this.adConfig, this.cueManager, this.placeholder);
8224
8746
  }
@@ -8251,6 +8773,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8251
8773
  return this.adConfig.generateVastUrlsWithCorrelators(baseUrl, count, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8252
8774
  }
8253
8775
  },
8776
+ {
8777
+ key: "generatePodVastUrl",
8778
+ value: function generatePodVastUrl(baseUrl, breakDurationMs) {
8779
+ var podParams = this.adConfig.resolvePodParams(breakDurationMs);
8780
+ return this.adConfig.generatePodVastUrl(baseUrl, podParams, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8781
+ }
8782
+ },
8254
8783
  {
8255
8784
  key: "load",
8256
8785
  value: // ───────────────────────────────────────────────────────────────
@@ -8485,7 +9014,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8485
9014
  sendAdDetectTracking(this.config.licenseKey, _object_spread({
8486
9015
  source: "scte35",
8487
9016
  timestamp: /* @__PURE__ */ new Date().toISOString()
8488
- }, detectPayload)).catch(function() {});
9017
+ }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8489
9018
  }
8490
9019
  var hasPrefetchedAds = this.adBreak.pendingAdBreak && this.adBreak.pendingAdBreak.vastUrls.length > 0;
8491
9020
  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;
@@ -8639,7 +9168,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8639
9168
  }
8640
9169
  this.adConfig.beginNewAdPod();
8641
9170
  this.adConfig.podAssignedByPrefetch = true;
8642
- var generatedUrls = this.generateVastUrls(baseVastUrl, 1);
9171
+ var optimizedPods = !!this.config.optimizedPods;
9172
+ var breakDurationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : void 0;
9173
+ var generatedUrls = optimizedPods ? [
9174
+ this.generatePodVastUrl(baseVastUrl, breakDurationMs)
9175
+ ] : this.generateVastUrls(baseVastUrl, 1);
8643
9176
  this.adBreak.pendingAdBreak = _object_spread_props(_object_spread({
8644
9177
  marker: marker
8645
9178
  }, fragmentSn !== void 0 ? {
@@ -8658,10 +9191,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8658
9191
  sendAdDetectTracking(this.config.licenseKey, _object_spread({
8659
9192
  source: "scte35",
8660
9193
  timestamp: /* @__PURE__ */ new Date().toISOString()
8661
- }, detectPayload)).catch(function() {});
9194
+ }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8662
9195
  if (this.debug) {
8663
9196
  console.log("[PREFETCH] Starting ad prefetch for upcoming ad break");
8664
- console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
9197
+ console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " ").concat(optimizedPods ? "pod" : "VAST", " URL(s)"));
9198
+ }
9199
+ if (optimizedPods) {
9200
+ if (this.debug) {
9201
+ console.log("[PREFETCH] Optimized pods enabled - skipping single-ad preload pool");
9202
+ }
9203
+ return;
8665
9204
  }
8666
9205
  this.preloadPool.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
8667
9206
  }