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
@@ -412,6 +412,8 @@ function createHlsAdPlayer(contentVideo, options) {
412
412
  var adHls;
413
413
  var adContainerEl;
414
414
  var currentAd;
415
+ var podAds = [];
416
+ var podIndex = 0;
415
417
  var sessionId;
416
418
  var preloadedAds = /* @__PURE__ */ new Map();
417
419
  var preloadingAds = /* @__PURE__ */ new Map();
@@ -576,14 +578,14 @@ function createHlsAdPlayer(contentVideo, options) {
576
578
  error: []
577
579
  };
578
580
  }
579
- function extractTrackingUrls(xmlDoc) {
581
+ function extractTrackingUrls(scope) {
580
582
  var trackingUrls = createEmptyTrackingUrls();
581
- xmlDoc.querySelectorAll("Impression").forEach(function(el) {
583
+ scope.querySelectorAll("Impression").forEach(function(el) {
582
584
  var _el_textContent;
583
585
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
584
586
  if (url) trackingUrls.impression.push(url);
585
587
  });
586
- xmlDoc.querySelectorAll("Tracking").forEach(function(el) {
588
+ scope.querySelectorAll("Tracking").forEach(function(el) {
587
589
  var _el_textContent;
588
590
  var event = el.getAttribute("event");
589
591
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
@@ -594,7 +596,7 @@ function createHlsAdPlayer(contentVideo, options) {
594
596
  }
595
597
  }
596
598
  });
597
- xmlDoc.querySelectorAll("Error").forEach(function(el) {
599
+ scope.querySelectorAll("Error").forEach(function(el) {
598
600
  var _el_textContent;
599
601
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
600
602
  if (url) trackingUrls.error.push(url);
@@ -704,6 +706,75 @@ function createHlsAdPlayer(contentVideo, options) {
704
706
  };
705
707
  }
706
708
  }
709
+ function parseAdElement(adElement) {
710
+ try {
711
+ var _adElement_querySelector, _adElement_querySelector_textContent, _adElement_querySelector1, _adElement_querySelector_textContent1, _adElement_querySelector2, _adElement_querySelector3;
712
+ var adId = adElement.getAttribute("id") || "unknown";
713
+ var title = ((_adElement_querySelector = adElement.querySelector("AdTitle")) === null || _adElement_querySelector === void 0 ? void 0 : _adElement_querySelector.textContent) || "Ad";
714
+ 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();
715
+ 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();
716
+ if (wrapperUri) {
717
+ return {
718
+ type: "wrapper",
719
+ vastAdTagUri: wrapperUri,
720
+ trackingUrls: extractTrackingUrls(adElement),
721
+ clickThrough: clickThrough
722
+ };
723
+ }
724
+ var isNoAdAvailable = adId === "empty" || title.toLowerCase().includes("no ad available") || title.toLowerCase() === "no ad available";
725
+ var durationText = ((_adElement_querySelector3 = adElement.querySelector("Duration")) === null || _adElement_querySelector3 === void 0 ? void 0 : _adElement_querySelector3.textContent) || "00:00:30";
726
+ var durationParts = durationText.split(":");
727
+ var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
728
+ var mediaFileElements = adElement.querySelectorAll("MediaFile");
729
+ var mediaFiles = [];
730
+ mediaFileElements.forEach(function(mf) {
731
+ var _mf_textContent;
732
+ var type = mf.getAttribute("type") || "";
733
+ var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
734
+ var width = mf.getAttribute("width") || "";
735
+ var height = mf.getAttribute("height") || "";
736
+ if (!url) return;
737
+ var bitrateAttr = mf.getAttribute("bitrate");
738
+ var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
739
+ var mediaFile = {
740
+ url: url,
741
+ type: type,
742
+ width: parseInt(width || "1920", 10),
743
+ height: parseInt(height || "1080", 10),
744
+ bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
745
+ };
746
+ if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
747
+ mediaFiles.push(mediaFile);
748
+ }
749
+ });
750
+ if (mediaFiles.length === 0) {
751
+ if (isNoAdAvailable) {
752
+ console.warn("[HlsAdPlayer] Pod <Ad> indicates no ad available");
753
+ } else {
754
+ console.warn("[HlsAdPlayer] No supported media files in pod <Ad>");
755
+ }
756
+ return {
757
+ type: "empty"
758
+ };
759
+ }
760
+ return {
761
+ type: "inline",
762
+ ad: {
763
+ id: adId,
764
+ title: title,
765
+ duration: duration,
766
+ mediaFiles: mediaFiles,
767
+ trackingUrls: extractTrackingUrls(adElement),
768
+ clickThrough: clickThrough
769
+ }
770
+ };
771
+ } catch (error) {
772
+ console.error("[HlsAdPlayer] Error parsing pod <Ad> element:", error);
773
+ return {
774
+ type: "empty"
775
+ };
776
+ }
777
+ }
707
778
  function fetchVastXml(vastTagUrl) {
708
779
  return _async_to_generator(function() {
709
780
  var response;
@@ -780,6 +851,146 @@ function createHlsAdPlayer(contentVideo, options) {
780
851
  });
781
852
  }).apply(this, arguments);
782
853
  }
854
+ function fetchAndParsePod(vastTagUrl) {
855
+ return _async_to_generator(function() {
856
+ var vastXml, parser, xmlDoc, adElements, ads, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, adElement, parsed, resolved, error, err;
857
+ return _ts_generator(this, function(_state) {
858
+ switch(_state.label){
859
+ case 0:
860
+ return [
861
+ 4,
862
+ fetchVastXml(vastTagUrl)
863
+ ];
864
+ case 1:
865
+ vastXml = _state.sent();
866
+ console.log("[HlsAdPlayer] Pod VAST XML received");
867
+ parser = new DOMParser();
868
+ xmlDoc = parser.parseFromString(vastXml, "text/xml");
869
+ if (xmlDoc.querySelector("parsererror")) {
870
+ console.error("[HlsAdPlayer] Pod VAST XML parsing error (malformed)");
871
+ return [
872
+ 2,
873
+ []
874
+ ];
875
+ }
876
+ adElements = Array.from(xmlDoc.querySelectorAll("Ad"));
877
+ if (adElements.length === 0) {
878
+ console.warn("[HlsAdPlayer] No <Ad> elements found in pod VAST response");
879
+ return [
880
+ 2,
881
+ []
882
+ ];
883
+ }
884
+ adElements.sort(function(a, b) {
885
+ var aSeq = parseInt(a.getAttribute("sequence") || "", 10);
886
+ var bSeq = parseInt(b.getAttribute("sequence") || "", 10);
887
+ var aVal = Number.isFinite(aSeq) ? aSeq : Number.MAX_SAFE_INTEGER;
888
+ var bVal = Number.isFinite(bSeq) ? bSeq : Number.MAX_SAFE_INTEGER;
889
+ return aVal - bVal;
890
+ });
891
+ ads = [];
892
+ _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
893
+ _state.label = 2;
894
+ case 2:
895
+ _state.trys.push([
896
+ 2,
897
+ 10,
898
+ 11,
899
+ 12
900
+ ]);
901
+ _iterator = adElements[Symbol.iterator]();
902
+ _state.label = 3;
903
+ case 3:
904
+ if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [
905
+ 3,
906
+ 9
907
+ ];
908
+ adElement = _step.value;
909
+ parsed = parseAdElement(adElement);
910
+ if (!(parsed.type === "inline")) return [
911
+ 3,
912
+ 4
913
+ ];
914
+ ads.push(parsed.ad);
915
+ return [
916
+ 3,
917
+ 8
918
+ ];
919
+ case 4:
920
+ if (!(parsed.type === "wrapper")) return [
921
+ 3,
922
+ 8
923
+ ];
924
+ _state.label = 5;
925
+ case 5:
926
+ _state.trys.push([
927
+ 5,
928
+ 7,
929
+ ,
930
+ 8
931
+ ]);
932
+ return [
933
+ 4,
934
+ fetchAndParseVastAd(parsed.vastAdTagUri, 1, parsed.trackingUrls)
935
+ ];
936
+ case 6:
937
+ resolved = _state.sent();
938
+ if (resolved) {
939
+ ads.push(resolved);
940
+ }
941
+ return [
942
+ 3,
943
+ 8
944
+ ];
945
+ case 7:
946
+ error = _state.sent();
947
+ console.warn("[HlsAdPlayer] Failed to resolve wrapper <Ad> in pod:", error);
948
+ return [
949
+ 3,
950
+ 8
951
+ ];
952
+ case 8:
953
+ _iteratorNormalCompletion = true;
954
+ return [
955
+ 3,
956
+ 3
957
+ ];
958
+ case 9:
959
+ return [
960
+ 3,
961
+ 12
962
+ ];
963
+ case 10:
964
+ err = _state.sent();
965
+ _didIteratorError = true;
966
+ _iteratorError = err;
967
+ return [
968
+ 3,
969
+ 12
970
+ ];
971
+ case 11:
972
+ try {
973
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
974
+ _iterator.return();
975
+ }
976
+ } finally{
977
+ if (_didIteratorError) {
978
+ throw _iteratorError;
979
+ }
980
+ }
981
+ return [
982
+ 7
983
+ ];
984
+ case 12:
985
+ console.log("[HlsAdPlayer] Parsed ".concat(ads.length, " ad(s) from pod VAST response"));
986
+ return [
987
+ 2,
988
+ ads
989
+ ];
990
+ }
991
+ });
992
+ })();
993
+ }
783
994
  function createAdVideoElement() {
784
995
  var video = document.createElement("video");
785
996
  video.style.position = "absolute";
@@ -824,6 +1035,9 @@ function createHlsAdPlayer(contentVideo, options) {
824
1035
  trackingFired.complete = true;
825
1036
  fireTrackingPixels(currentAd.trackingUrls.complete);
826
1037
  console.log("[HlsAdPlayer] Ad completed");
1038
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1039
+ return;
1040
+ }
827
1041
  handleAdComplete();
828
1042
  });
829
1043
  adVideoElement.addEventListener("error", function(e) {
@@ -832,6 +1046,9 @@ function createHlsAdPlayer(contentVideo, options) {
832
1046
  if (currentAd) {
833
1047
  fireTrackingPixels(currentAd.trackingUrls.error);
834
1048
  }
1049
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1050
+ return;
1051
+ }
835
1052
  handleAdError();
836
1053
  });
837
1054
  adVideoElement.addEventListener("volumechange", function() {
@@ -860,6 +1077,94 @@ function createHlsAdPlayer(contentVideo, options) {
860
1077
  delete contentVideo.dataset.stormcloudAdPlaying;
861
1078
  }
862
1079
  }
1080
+ function resetQuartileTracking(preserveImpression) {
1081
+ trackingFired = {
1082
+ impression: preserveImpression ? trackingFired.impression : false,
1083
+ start: false,
1084
+ firstQuartile: false,
1085
+ midpoint: false,
1086
+ thirdQuartile: false,
1087
+ complete: false
1088
+ };
1089
+ }
1090
+ function loadCurrentAdMedia() {
1091
+ if (!adVideoElement || !currentAd) {
1092
+ throw new Error("No ad video element or ad to load");
1093
+ }
1094
+ var mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1095
+ if (!mediaFile) {
1096
+ throw new Error("No media file available for ad");
1097
+ }
1098
+ var isHlsAd = isHlsMediaFile(mediaFile);
1099
+ console.log("[HlsAdPlayer] Loading ".concat(isHlsAd ? "HLS" : "progressive", " ad (pod ").concat(podIndex + 1, "/").concat(podAds.length || 1, ") from: ").concat(mediaFile.url));
1100
+ if (isHlsAd && import_hls.default.isSupported()) {
1101
+ if (adHls) {
1102
+ adHls.destroy();
1103
+ }
1104
+ adHls = new import_hls.default({
1105
+ enableWorker: true,
1106
+ lowLatencyMode: false
1107
+ });
1108
+ adHls.loadSource(mediaFile.url);
1109
+ adHls.attachMedia(adVideoElement);
1110
+ adHls.on(import_hls.default.Events.MANIFEST_PARSED, function() {
1111
+ console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1112
+ adVideoElement.play().catch(function(error) {
1113
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1114
+ handleAdError();
1115
+ });
1116
+ });
1117
+ adHls.on(import_hls.default.Events.ERROR, function(_event, data) {
1118
+ console.error("[HlsAdPlayer] HLS error:", data);
1119
+ if (data.fatal) {
1120
+ handleAdError();
1121
+ }
1122
+ });
1123
+ } else if (isHlsAd && adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1124
+ adVideoElement.src = mediaFile.url;
1125
+ adVideoElement.play().catch(function(error) {
1126
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1127
+ handleAdError();
1128
+ });
1129
+ } else if (!isHlsAd && isProgressiveMediaFile(mediaFile)) {
1130
+ if (adHls) {
1131
+ adHls.destroy();
1132
+ adHls = void 0;
1133
+ }
1134
+ adVideoElement.src = mediaFile.url;
1135
+ adVideoElement.load();
1136
+ adVideoElement.play().catch(function(error) {
1137
+ console.error("[HlsAdPlayer] Error starting progressive ad playback:", error);
1138
+ handleAdError();
1139
+ });
1140
+ } else {
1141
+ throw new Error("Unsupported ad media file type: ".concat(mediaFile.type));
1142
+ }
1143
+ }
1144
+ function advanceToNextPodAd() {
1145
+ if (podIndex >= podAds.length - 1) {
1146
+ return false;
1147
+ }
1148
+ podIndex++;
1149
+ currentAd = podAds[podIndex];
1150
+ resetQuartileTracking(false);
1151
+ if (currentAd) {
1152
+ fireTrackingPixels(currentAd.trackingUrls.impression);
1153
+ trackingFired.impression = true;
1154
+ console.log("[HlsAdPlayer] Advancing to next pod ad (".concat(podIndex + 1, "/").concat(podAds.length, "): ").concat(currentAd.title, ", duration: ").concat(currentAd.duration, "s"));
1155
+ }
1156
+ try {
1157
+ loadCurrentAdMedia();
1158
+ emit("pod_ad_started", {
1159
+ index: podIndex,
1160
+ total: podAds.length
1161
+ });
1162
+ return true;
1163
+ } catch (error) {
1164
+ console.error("[HlsAdPlayer] Failed to load next pod ad:", error);
1165
+ return false;
1166
+ }
1167
+ }
863
1168
  function handleAdComplete() {
864
1169
  console.log("[HlsAdPlayer] Handling ad completion");
865
1170
  adPlaying = false;
@@ -913,7 +1218,7 @@ function createHlsAdPlayer(contentVideo, options) {
913
1218
  },
914
1219
  requestAds: function requestAds(vastTagUrl) {
915
1220
  return _async_to_generator(function() {
916
- var ad, error;
1221
+ var ads, preloaded, error;
917
1222
  return _ts_generator(this, function(_state) {
918
1223
  switch(_state.label){
919
1224
  case 0:
@@ -934,12 +1239,18 @@ function createHlsAdPlayer(contentVideo, options) {
934
1239
  6
935
1240
  ]);
936
1241
  sessionId = generateSessionId();
1242
+ ads = [];
937
1243
  if (!preloadedAds.has(vastTagUrl)) return [
938
1244
  3,
939
1245
  2
940
1246
  ];
941
- ad = preloadedAds.get(vastTagUrl);
1247
+ preloaded = preloadedAds.get(vastTagUrl);
942
1248
  preloadedAds.delete(vastTagUrl);
1249
+ if (preloaded) {
1250
+ ads = [
1251
+ preloaded
1252
+ ];
1253
+ }
943
1254
  console.log("[HlsAdPlayer] Using preloaded VAST response:", vastTagUrl);
944
1255
  return [
945
1256
  3,
@@ -948,13 +1259,13 @@ function createHlsAdPlayer(contentVideo, options) {
948
1259
  case 2:
949
1260
  return [
950
1261
  4,
951
- fetchAndParseVastAd(vastTagUrl)
1262
+ fetchAndParsePod(vastTagUrl)
952
1263
  ];
953
1264
  case 3:
954
- ad = _state.sent();
1265
+ ads = _state.sent();
955
1266
  _state.label = 4;
956
1267
  case 4:
957
- if (!ad) {
1268
+ if (!ads.length) {
958
1269
  console.warn("[HlsAdPlayer] No ads available from VAST response");
959
1270
  emit("ad_error");
960
1271
  return [
@@ -962,10 +1273,14 @@ function createHlsAdPlayer(contentVideo, options) {
962
1273
  Promise.reject(new Error("No ads available from VAST response"))
963
1274
  ];
964
1275
  }
965
- currentAd = ad;
966
- console.log("[HlsAdPlayer] Ad parsed: ".concat(ad.title, ", duration: ").concat(ad.duration, "s"));
967
- fireTrackingPixels(ad.trackingUrls.impression);
968
- trackingFired.impression = true;
1276
+ podAds = ads;
1277
+ podIndex = 0;
1278
+ currentAd = ads[0];
1279
+ 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"));
1280
+ if (currentAd) {
1281
+ fireTrackingPixels(currentAd.trackingUrls.impression);
1282
+ trackingFired.impression = true;
1283
+ }
969
1284
  return [
970
1285
  2,
971
1286
  Promise.resolve()
@@ -988,7 +1303,7 @@ function createHlsAdPlayer(contentVideo, options) {
988
1303
  },
989
1304
  play: function play() {
990
1305
  return _async_to_generator(function() {
991
- var contentVolume, mediaFile, isHlsAd;
1306
+ var contentVolume;
992
1307
  return _ts_generator(this, function(_state) {
993
1308
  if (!currentAd) {
994
1309
  console.warn("[HlsAdPlayer] Cannot play: No ad loaded (no ads available)");
@@ -1004,14 +1319,7 @@ function createHlsAdPlayer(contentVideo, options) {
1004
1319
  adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.appendChild(adVideoElement);
1005
1320
  setupAdEventListeners();
1006
1321
  }
1007
- trackingFired = {
1008
- impression: trackingFired.impression,
1009
- start: false,
1010
- firstQuartile: false,
1011
- midpoint: false,
1012
- thirdQuartile: false,
1013
- complete: false
1014
- };
1322
+ resetQuartileTracking(true);
1015
1323
  contentVolume = contentVideo.volume;
1016
1324
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1017
1325
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
@@ -1029,60 +1337,12 @@ function createHlsAdPlayer(contentVideo, options) {
1029
1337
  adVideoElement.muted = originalMutedState;
1030
1338
  console.log("[HlsAdPlayer] Set ad video volume to ".concat(adVideoElement.volume, ", muted: ").concat(adVideoElement.muted, ", originalMutedState: ").concat(originalMutedState, ", contentVolume: ").concat(contentVolume));
1031
1339
  }
1032
- mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1033
- if (!mediaFile) {
1034
- throw new Error("No media file available for ad");
1035
- }
1036
1340
  if (adContainerEl) {
1037
1341
  adContainerEl.style.display = "flex";
1038
1342
  adContainerEl.style.pointerEvents = "auto";
1039
1343
  }
1040
1344
  emit("content_pause");
1041
- isHlsAd = isHlsMediaFile(mediaFile);
1042
- console.log("[HlsAdPlayer] Loading ".concat(isHlsAd ? "HLS" : "progressive", " ad from: ").concat(mediaFile.url));
1043
- if (isHlsAd && import_hls.default.isSupported()) {
1044
- if (adHls) {
1045
- adHls.destroy();
1046
- }
1047
- adHls = new import_hls.default({
1048
- enableWorker: true,
1049
- lowLatencyMode: false
1050
- });
1051
- adHls.loadSource(mediaFile.url);
1052
- adHls.attachMedia(adVideoElement);
1053
- adHls.on(import_hls.default.Events.MANIFEST_PARSED, function() {
1054
- console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1055
- adVideoElement.play().catch(function(error) {
1056
- console.error("[HlsAdPlayer] Error starting ad playback:", error);
1057
- handleAdError();
1058
- });
1059
- });
1060
- adHls.on(import_hls.default.Events.ERROR, function(event, data) {
1061
- console.error("[HlsAdPlayer] HLS error:", data);
1062
- if (data.fatal) {
1063
- handleAdError();
1064
- }
1065
- });
1066
- } else if (isHlsAd && adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1067
- adVideoElement.src = mediaFile.url;
1068
- adVideoElement.play().catch(function(error) {
1069
- console.error("[HlsAdPlayer] Error starting ad playback:", error);
1070
- handleAdError();
1071
- });
1072
- } else if (!isHlsAd && isProgressiveMediaFile(mediaFile)) {
1073
- if (adHls) {
1074
- adHls.destroy();
1075
- adHls = void 0;
1076
- }
1077
- adVideoElement.src = mediaFile.url;
1078
- adVideoElement.load();
1079
- adVideoElement.play().catch(function(error) {
1080
- console.error("[HlsAdPlayer] Error starting progressive ad playback:", error);
1081
- handleAdError();
1082
- });
1083
- } else {
1084
- throw new Error("Unsupported ad media file type: ".concat(mediaFile.type));
1085
- }
1345
+ loadCurrentAdMedia();
1086
1346
  return [
1087
1347
  2,
1088
1348
  Promise.resolve()
@@ -1157,6 +1417,8 @@ function createHlsAdPlayer(contentVideo, options) {
1157
1417
  adVideoElement.src = "";
1158
1418
  }
1159
1419
  currentAd = void 0;
1420
+ podAds = [];
1421
+ podIndex = 0;
1160
1422
  return [
1161
1423
  2
1162
1424
  ];
@@ -1206,6 +1468,8 @@ function createHlsAdPlayer(contentVideo, options) {
1206
1468
  }
1207
1469
  adContainerEl = void 0;
1208
1470
  currentAd = void 0;
1471
+ podAds = [];
1472
+ podIndex = 0;
1209
1473
  listeners.clear();
1210
1474
  preloadedAds.clear();
1211
1475
  preloadingAds.clear();
@@ -1297,6 +1561,14 @@ function createHlsAdPlayer(contentVideo, options) {
1297
1561
  },
1298
1562
  getLoadedAdId: function getLoadedAdId() {
1299
1563
  return currentAd === null || currentAd === void 0 ? void 0 : currentAd.id;
1564
+ },
1565
+ getPodAdCount: function getPodAdCount() {
1566
+ return podAds.length;
1567
+ },
1568
+ getPodTotalDurationMs: function getPodTotalDurationMs() {
1569
+ return podAds.reduce(function(total, ad) {
1570
+ return total + (ad.duration > 0 ? ad.duration * 1e3 : 0);
1571
+ }, 0);
1300
1572
  }
1301
1573
  };
1302
1574
  }
@@ -1996,6 +2268,16 @@ function normalizeBoolean(value) {
1996
2268
  }
1997
2269
  // src/utils/tracking.ts
1998
2270
  var cachedBrowserId = null;
2271
+ function buildAnalyticsContext(config) {
2272
+ var context = {};
2273
+ if (config.debugAdTiming) {
2274
+ context.debugAdTiming = true;
2275
+ }
2276
+ if (config.isLiveStream !== void 0) {
2277
+ context.inputStreamType = config.isLiveStream ? "live" : "vod";
2278
+ }
2279
+ return context;
2280
+ }
1999
2281
  function getClientInfo() {
2000
2282
  var _screen, _screen1, _screen2, _screen3, _screen_orientation, _screen4, _screen5, _window, _window1, _window_screen_orientation, _window_screen, _navigator_languages;
2001
2283
  var ua = navigator.userAgent;
@@ -2254,19 +2536,26 @@ function canPublish(licenseKey) {
2254
2536
  }
2255
2537
  function buildPlayerMetricEvent() {
2256
2538
  return _async_to_generator(function() {
2257
- var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
2539
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, debugAdTiming, playerId, captureAt;
2258
2540
  var _arguments = arguments;
2259
2541
  return _ts_generator(this, function(_state) {
2260
2542
  switch(_state.label){
2261
2543
  case 0:
2262
2544
  context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2263
2545
  clientInfo = getClientInfo();
2546
+ debugAdTiming = !!context.debugAdTiming;
2547
+ if (debugAdTiming) {
2548
+ console.log("[StormcloudVideoPlayer] clientInfo: ", JSON.stringify(clientInfo, null, 2));
2549
+ }
2264
2550
  return [
2265
2551
  4,
2266
2552
  getBrowserID(clientInfo)
2267
2553
  ];
2268
2554
  case 1:
2269
2555
  playerId = _state.sent();
2556
+ if (debugAdTiming) {
2557
+ console.log("[StormcloudVideoPlayer] playerId: ", playerId);
2558
+ }
2270
2559
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
2271
2560
  return [
2272
2561
  2,
@@ -3108,7 +3397,20 @@ function applyVastMacros(baseUrl, ctx) {
3108
3397
  if (ctx.pod != null) {
3109
3398
  params.set("pod", String(ctx.pod));
3110
3399
  }
3111
- if (ctx.adPosition != null) {
3400
+ var optimizedPodMode = ctx.podMaxAds != null || ctx.podMinDurationMs != null || ctx.podMaxDurationMs != null;
3401
+ if (optimizedPodMode) {
3402
+ params.delete("ppos");
3403
+ params.delete("lip");
3404
+ if (ctx.podMinDurationMs != null) {
3405
+ params.set("pmnd", String(Math.max(0, Math.round(ctx.podMinDurationMs))));
3406
+ }
3407
+ if (ctx.podMaxDurationMs != null) {
3408
+ params.set("pmxd", String(Math.max(0, Math.round(ctx.podMaxDurationMs))));
3409
+ }
3410
+ if (ctx.podMaxAds != null) {
3411
+ params.set("pmad", String(Math.max(1, Math.round(ctx.podMaxAds))));
3412
+ }
3413
+ } else if (ctx.adPosition != null) {
3112
3414
  params.set("ppos", String(ctx.adPosition));
3113
3415
  }
3114
3416
  var requestUrl = ctx.contentUrl || ctx.pageUrl;
@@ -4450,6 +4752,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
4450
4752
  this.podAssignedByPrefetch = false;
4451
4753
  this.adRequestPositionInBreak = 0;
4452
4754
  this.lastHeartbeatTime = 0;
4755
+ this.defaultPodMaxAds = 6;
4453
4756
  this.config = config;
4454
4757
  this.video = video;
4455
4758
  }
@@ -4905,12 +5208,58 @@ var AdConfigManager = /*#__PURE__*/ function() {
4905
5208
  return urls;
4906
5209
  }
4907
5210
  },
5211
+ {
5212
+ key: "resolvePodParams",
5213
+ value: function resolvePodParams(breakDurationMs) {
5214
+ var maxDurationMs = breakDurationMs != null && breakDurationMs > 0 ? breakDurationMs : this.config.podMaxDurationMs;
5215
+ var maxAds = this.config.podMaxAds && this.config.podMaxAds > 0 ? this.config.podMaxAds : this.apiNumberAds && this.apiNumberAds > 0 ? this.apiNumberAds : this.defaultPodMaxAds;
5216
+ var minDurationMs = this.config.podExactDuration && maxDurationMs != null ? maxDurationMs : 0;
5217
+ return {
5218
+ maxAds: maxAds,
5219
+ minDurationMs: minDurationMs,
5220
+ maxDurationMs: maxDurationMs
5221
+ };
5222
+ }
5223
+ },
5224
+ {
5225
+ key: "generatePodVastUrl",
5226
+ value: function generatePodVastUrl(baseUrl, podParams, adPlayer, palNonce, inAdBreak) {
5227
+ var _podParams_maxDurationMs;
5228
+ var adWillPlayMuted = inAdBreak ? adPlayer.getOriginalMutedState() : this.video.muted;
5229
+ var envSignals = resolveVastEnvironmentSignals(!!this.config.ctvAdRequest);
5230
+ var urlWithMacros = applyVastMacros(baseUrl, {
5231
+ correlator: generateCorrelator(),
5232
+ streamCorrelator: this.streamCorrelator,
5233
+ pod: this.podCounter > 0 ? this.podCounter : void 0,
5234
+ podMaxAds: podParams.maxAds,
5235
+ podMinDurationMs: podParams.minDurationMs,
5236
+ podMaxDurationMs: podParams.maxDurationMs,
5237
+ isCtv: envSignals.isCtv,
5238
+ contentUrl: envSignals.contentUrl,
5239
+ pageUrl: !envSignals.isCtv && typeof window !== "undefined" ? window.location.href : void 0,
5240
+ adWillPlayMuted: adWillPlayMuted,
5241
+ adWillAutoPlay: !!this.config.autoplay,
5242
+ appId: envSignals.appId,
5243
+ appName: envSignals.appName,
5244
+ sessionId: envSignals.sessionId,
5245
+ deviceId: envSignals.deviceId,
5246
+ deviceIdType: envSignals.deviceIdType,
5247
+ limitAdTracking: envSignals.limitAdTracking,
5248
+ deviceTypeHint: envSignals.deviceTypeHint,
5249
+ adTest: this.config.adTest,
5250
+ consent: this.consentSignals
5251
+ });
5252
+ var finalUrl = palNonce.injectNonce(urlWithMacros);
5253
+ 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);
5254
+ return finalUrl;
5255
+ }
5256
+ },
4908
5257
  {
4909
5258
  // --- Tracking ---
4910
5259
  key: "initializeTracking",
4911
5260
  value: function initializeTracking() {
4912
5261
  var _this = this;
4913
- sendInitialTracking(this.config.licenseKey).then(function() {
5262
+ sendInitialTracking(this.config.licenseKey, buildAnalyticsContext(this.config)).then(function() {
4914
5263
  _this.heartbeatInterval = window.setInterval(function() {
4915
5264
  _this.sendHeartbeatIfNeeded();
4916
5265
  }, 5e3);
@@ -4931,7 +5280,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
4931
5280
  var now = Date.now();
4932
5281
  if (!this.lastHeartbeatTime || now - this.lastHeartbeatTime > 3e4) {
4933
5282
  this.lastHeartbeatTime = now;
4934
- sendHeartbeat(this.config.licenseKey).catch(function(error) {
5283
+ sendHeartbeat(this.config.licenseKey, buildAnalyticsContext(this.config)).catch(function(error) {
4935
5284
  if (_this.debug) {
4936
5285
  console.warn("[StormcloudVideoPlayer] Failed to send heartbeat:", error);
4937
5286
  }
@@ -5210,6 +5559,13 @@ var AdTimingService = /*#__PURE__*/ function() {
5210
5559
  this.lastAdRequestTime = Date.now();
5211
5560
  }
5212
5561
  },
5562
+ {
5563
+ key: "notePodAdStarted",
5564
+ value: function notePodAdStarted() {
5565
+ this.currentAdIndex++;
5566
+ this.totalAdsInBreak = this.currentAdIndex;
5567
+ }
5568
+ },
5213
5569
  {
5214
5570
  key: "getRemainingAdMs",
5215
5571
  value: function getRemainingAdMs() {
@@ -6712,19 +7068,38 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6712
7068
  }
6713
7069
  },
6714
7070
  {
6715
- // ───────────────────────────────────────────────────────────────
7071
+ key: "analyticsContext",
7072
+ get: function get() {
7073
+ return buildAnalyticsContext(this.host.config);
7074
+ }
7075
+ },
7076
+ {
7077
+ key: "optimizedPodsEnabled",
7078
+ get: // ───────────────────────────────────────────────────────────────
6716
7079
  // IMA event listeners
6717
7080
  // ───────────────────────────────────────────────────────────────
7081
+ function get() {
7082
+ return !!this.host.config.optimizedPods;
7083
+ }
7084
+ },
7085
+ {
6718
7086
  key: "attachImaEventListeners",
6719
7087
  value: function attachImaEventListeners() {
6720
7088
  var _this = this;
6721
7089
  var adPlayer = this.host.getAdPlayer();
7090
+ adPlayer.on("pod_ad_started", function(payload) {
7091
+ _this.timing.notePodAdStarted();
7092
+ if (_this.debug) {
7093
+ var _ref;
7094
+ 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 : "?"));
7095
+ }
7096
+ });
6722
7097
  adPlayer.on("all_ads_completed", function() {
6723
7098
  sendAdImpressionTracking(_this.host.config.licenseKey, {
6724
7099
  source: "hls",
6725
7100
  adIndex: _this.timing.currentAdIndex,
6726
7101
  timestamp: /* @__PURE__ */ new Date().toISOString()
6727
- }).catch(function() {});
7102
+ }, _this.analyticsContext).catch(function() {});
6728
7103
  _this.timing.recordPlayedAdDuration(_this.host.getAdPlayer());
6729
7104
  var remaining = _this.timing.getRemainingAdMs();
6730
7105
  _this.timing.consecutiveFailures = 0;
@@ -6786,7 +7161,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6786
7161
  source: "hls",
6787
7162
  adIndex: _this.timing.currentAdIndex,
6788
7163
  timestamp: /* @__PURE__ */ new Date().toISOString()
6789
- }).catch(function() {});
7164
+ }, _this.analyticsContext).catch(function() {});
6790
7165
  if (!_this.host.video.muted) {
6791
7166
  _this.host.video.muted = true;
6792
7167
  if (_this.debug) {
@@ -6876,7 +7251,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6876
7251
  // ───────────────────────────────────────────────────────────────
6877
7252
  function handleAdStart(_marker) {
6878
7253
  return _async_to_generator(function() {
6879
- var scheduled, tags, baseVastUrl, adBreakDurationMs, mode, currentMuted, currentVolume, firstAdUrl, usePreloadedAd, preloadedController, preloaded, firstAdUrlArray, adPlayer, preservedMuted, preservedVolume, adVolume, adPlayer1, adVolume1, error, fallbackPreloaded, ap, adVolume2, fallbackError;
7254
+ 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;
6880
7255
  return _ts_generator(this, function(_state) {
6881
7256
  switch(_state.label){
6882
7257
  case 0:
@@ -6923,6 +7298,26 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6923
7298
  if (this.timing.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
6924
7299
  this.timing.expectedAdBreakDurationMs = adBreakDurationMs;
6925
7300
  }
7301
+ if (!this.optimizedPodsEnabled) return [
7302
+ 3,
7303
+ 2
7304
+ ];
7305
+ return [
7306
+ 4,
7307
+ this.startOptimizedPod(baseVastUrl, adBreakDurationMs, currentMuted, currentVolume)
7308
+ ];
7309
+ case 1:
7310
+ podPlayed = _state.sent();
7311
+ if (podPlayed) {
7312
+ return [
7313
+ 2
7314
+ ];
7315
+ }
7316
+ if (this.debug) {
7317
+ console.log("[POD] Optimized pod unavailable, falling back to sequential single-ad path");
7318
+ }
7319
+ _state.label = 2;
7320
+ case 2:
6926
7321
  usePreloadedAd = false;
6927
7322
  preloaded = this.preloadPool.getPreloadedAd();
6928
7323
  if (preloaded) {
@@ -6933,7 +7328,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6933
7328
  source: "hls",
6934
7329
  vastUrl: firstAdUrl,
6935
7330
  timestamp: /* @__PURE__ */ new Date().toISOString()
6936
- }).catch(function() {});
7331
+ }, this.analyticsContext).catch(function() {});
6937
7332
  if (this.debug) {
6938
7333
  console.log("[CONTINUOUS-FETCH] Using preloaded ad from pool (preloaded in advance, ready immediately!)");
6939
7334
  }
@@ -6964,21 +7359,21 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6964
7359
  if (this.debug) {
6965
7360
  console.log("[CONTINUOUS-FETCH] First VAST URL:", firstAdUrl);
6966
7361
  }
6967
- _state.label = 1;
6968
- case 1:
7362
+ _state.label = 3;
7363
+ case 3:
6969
7364
  _state.trys.push([
6970
- 1,
6971
- 12,
7365
+ 3,
7366
+ 14,
6972
7367
  ,
6973
- 20
7368
+ 22
6974
7369
  ]);
6975
7370
  if (!(usePreloadedAd && preloadedController)) return [
6976
7371
  3,
6977
- 5
7372
+ 7
6978
7373
  ];
6979
7374
  if (!!this.timing.ensureLoadedAdFitsBudget(preloadedController)) return [
6980
7375
  3,
6981
- 3
7376
+ 5
6982
7377
  ];
6983
7378
  this.timing.rejectLoadedAdForDuration(preloadedController);
6984
7379
  this.startContinuousFetching(baseVastUrl);
@@ -6986,12 +7381,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6986
7381
  4,
6987
7382
  this.tryNextAvailableAdWithRateLimit()
6988
7383
  ];
6989
- case 2:
7384
+ case 4:
6990
7385
  _state.sent();
6991
7386
  return [
6992
7387
  2
6993
7388
  ];
6994
- case 3:
7389
+ case 5:
6995
7390
  adPlayer = this.host.getAdPlayer();
6996
7391
  preservedMuted = adPlayer.getOriginalMutedState();
6997
7392
  preservedVolume = adPlayer.getOriginalVolume();
@@ -7016,7 +7411,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7016
7411
  4,
7017
7412
  preloadedController.play()
7018
7413
  ];
7019
- case 4:
7414
+ case 6:
7020
7415
  _state.sent();
7021
7416
  this.timing.markAdStarted();
7022
7417
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7026,34 +7421,34 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7026
7421
  preloadedController.setAdVolume(adVolume);
7027
7422
  return [
7028
7423
  3,
7029
- 11
7424
+ 13
7030
7425
  ];
7031
- case 5:
7426
+ case 7:
7032
7427
  adPlayer1 = this.host.getAdPlayer();
7033
7428
  return [
7034
7429
  4,
7035
7430
  this.timing.enforceGlobalRateLimit()
7036
7431
  ];
7037
- case 6:
7432
+ case 8:
7038
7433
  _state.sent();
7039
7434
  this.timing.lastAdRequestTime = Date.now();
7040
7435
  return [
7041
7436
  4,
7042
7437
  adPlayer1.requestAds(firstAdUrl)
7043
7438
  ];
7044
- case 7:
7439
+ case 9:
7045
7440
  _state.sent();
7046
7441
  sendAdLoadedTracking(this.host.config.licenseKey, {
7047
7442
  source: "hls",
7048
7443
  vastUrl: firstAdUrl,
7049
7444
  timestamp: /* @__PURE__ */ new Date().toISOString()
7050
- }).catch(function() {});
7445
+ }, this.analyticsContext).catch(function() {});
7051
7446
  if (this.debug) {
7052
7447
  console.log("[CONTINUOUS-FETCH] First ad request successful, starting playback");
7053
7448
  }
7054
7449
  if (!!this.timing.ensureLoadedAdFitsBudget(adPlayer1)) return [
7055
7450
  3,
7056
- 9
7451
+ 11
7057
7452
  ];
7058
7453
  this.timing.rejectLoadedAdForDuration(adPlayer1);
7059
7454
  this.recreateAdController();
@@ -7062,12 +7457,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7062
7457
  4,
7063
7458
  this.tryNextAvailableAdWithRateLimit()
7064
7459
  ];
7065
- case 8:
7460
+ case 10:
7066
7461
  _state.sent();
7067
7462
  return [
7068
7463
  2
7069
7464
  ];
7070
- case 9:
7465
+ case 11:
7071
7466
  this.timing.consecutiveFailures = 0;
7072
7467
  this.startContinuousFetching(baseVastUrl);
7073
7468
  if (!this.preloadPool.active) {
@@ -7077,7 +7472,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7077
7472
  4,
7078
7473
  adPlayer1.play()
7079
7474
  ];
7080
- case 10:
7475
+ case 12:
7081
7476
  _state.sent();
7082
7477
  this.timing.markAdStarted();
7083
7478
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7085,40 +7480,40 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7085
7480
  }
7086
7481
  adVolume1 = currentMuted ? 0 : currentVolume;
7087
7482
  adPlayer1.setAdVolume(adVolume1);
7088
- _state.label = 11;
7089
- case 11:
7483
+ _state.label = 13;
7484
+ case 13:
7090
7485
  return [
7091
7486
  3,
7092
- 20
7487
+ 22
7093
7488
  ];
7094
- case 12:
7489
+ case 14:
7095
7490
  error = _state.sent();
7096
7491
  if (this.debug) {
7097
7492
  console.warn("[CONTINUOUS-FETCH] First ad request failed:", error);
7098
7493
  }
7099
7494
  if (!!usePreloadedAd) return [
7100
7495
  3,
7101
- 18
7496
+ 20
7102
7497
  ];
7103
7498
  fallbackPreloaded = this.preloadPool.getPreloadedAd();
7104
7499
  if (!fallbackPreloaded) return [
7105
7500
  3,
7106
- 18
7501
+ 20
7107
7502
  ];
7108
7503
  if (this.debug) {
7109
7504
  console.log("[CONTINUOUS-FETCH] First ad failed, using preloaded fallback");
7110
7505
  }
7111
- _state.label = 13;
7112
- case 13:
7506
+ _state.label = 15;
7507
+ case 15:
7113
7508
  _state.trys.push([
7114
- 13,
7115
- 17,
7509
+ 15,
7510
+ 19,
7116
7511
  ,
7117
- 18
7512
+ 20
7118
7513
  ]);
7119
7514
  if (!!this.timing.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
7120
7515
  3,
7121
- 15
7516
+ 17
7122
7517
  ];
7123
7518
  this.timing.rejectLoadedAdForDuration(fallbackPreloaded.adController);
7124
7519
  this.startContinuousFetching(baseVastUrl);
@@ -7126,12 +7521,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7126
7521
  4,
7127
7522
  this.tryNextAvailableAdWithRateLimit()
7128
7523
  ];
7129
- case 14:
7524
+ case 16:
7130
7525
  _state.sent();
7131
7526
  return [
7132
7527
  2
7133
7528
  ];
7134
- case 15:
7529
+ case 17:
7135
7530
  this.swapToPreloadedAdPlayer(fallbackPreloaded.adController);
7136
7531
  this.timing.consecutiveFailures = 0;
7137
7532
  this.startContinuousFetching(baseVastUrl);
@@ -7144,7 +7539,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7144
7539
  4,
7145
7540
  ap.play()
7146
7541
  ];
7147
- case 16:
7542
+ case 18:
7148
7543
  _state.sent();
7149
7544
  this.timing.markAdStarted();
7150
7545
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7155,16 +7550,16 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7155
7550
  return [
7156
7551
  2
7157
7552
  ];
7158
- case 17:
7553
+ case 19:
7159
7554
  fallbackError = _state.sent();
7160
7555
  if (this.debug) {
7161
7556
  console.warn("[CONTINUOUS-FETCH] Preloaded fallback also failed:", fallbackError);
7162
7557
  }
7163
7558
  return [
7164
7559
  3,
7165
- 18
7560
+ 20
7166
7561
  ];
7167
- case 18:
7562
+ case 20:
7168
7563
  if (this.timing.isTemporaryAdError(error)) {
7169
7564
  this.timing.temporaryFailureUrls.set(firstAdUrl, Date.now());
7170
7565
  if (this.debug) {
@@ -7182,13 +7577,137 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7182
7577
  4,
7183
7578
  this.tryNextAvailableAdWithRateLimit()
7184
7579
  ];
7185
- case 19:
7580
+ case 21:
7186
7581
  _state.sent();
7187
7582
  return [
7188
7583
  3,
7189
- 20
7584
+ 22
7190
7585
  ];
7191
- case 20:
7586
+ case 22:
7587
+ return [
7588
+ 2
7589
+ ];
7590
+ }
7591
+ });
7592
+ }).call(this);
7593
+ }
7594
+ },
7595
+ {
7596
+ key: "startOptimizedPod",
7597
+ value: // ───────────────────────────────────────────────────────────────
7598
+ // Optimized pod (single multi-ad request)
7599
+ // ───────────────────────────────────────────────────────────────
7600
+ function startOptimizedPod(baseVastUrl, breakDurationMs, currentMuted, currentVolume) {
7601
+ return _async_to_generator(function() {
7602
+ var podUrl, adPlayer, requestToken, _ref, _adPlayer_getPodAdCount, podCount, error;
7603
+ return _ts_generator(this, function(_state) {
7604
+ switch(_state.label){
7605
+ case 0:
7606
+ if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {
7607
+ podUrl = this.pendingAdBreak.vastUrls[0];
7608
+ this.clearPendingAdBreak();
7609
+ } else {
7610
+ podUrl = this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);
7611
+ }
7612
+ if (!podUrl) {
7613
+ return [
7614
+ 2,
7615
+ false
7616
+ ];
7617
+ }
7618
+ this.continuousFetchingActive = false;
7619
+ this.recreateAdController();
7620
+ adPlayer = this.host.getAdPlayer();
7621
+ requestToken = ++this.timing.adRequestTokenCounter;
7622
+ this.timing.activeAdRequestToken = requestToken;
7623
+ this.timing.startAdRequestWatchdog(requestToken);
7624
+ _state.label = 1;
7625
+ case 1:
7626
+ _state.trys.push([
7627
+ 1,
7628
+ 5,
7629
+ ,
7630
+ 6
7631
+ ]);
7632
+ return [
7633
+ 4,
7634
+ this.timing.enforceGlobalRateLimit()
7635
+ ];
7636
+ case 2:
7637
+ _state.sent();
7638
+ this.timing.lastAdRequestTime = Date.now();
7639
+ return [
7640
+ 4,
7641
+ adPlayer.requestAds(podUrl)
7642
+ ];
7643
+ case 3:
7644
+ _state.sent();
7645
+ this.timing.clearAdRequestWatchdog();
7646
+ if (this.timing.activeAdRequestToken !== requestToken) {
7647
+ return [
7648
+ 2,
7649
+ true
7650
+ ];
7651
+ }
7652
+ podCount = (_ref = (_adPlayer_getPodAdCount = adPlayer.getPodAdCount) === null || _adPlayer_getPodAdCount === void 0 ? void 0 : _adPlayer_getPodAdCount.call(adPlayer)) !== null && _ref !== void 0 ? _ref : 1;
7653
+ if (podCount < 1) {
7654
+ return [
7655
+ 2,
7656
+ false
7657
+ ];
7658
+ }
7659
+ if (this.debug) {
7660
+ 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"));
7661
+ }
7662
+ sendAdLoadedTracking(this.host.config.licenseKey, {
7663
+ source: "hls",
7664
+ vastUrl: podUrl,
7665
+ timestamp: /* @__PURE__ */ new Date().toISOString()
7666
+ }, this.analyticsContext).catch(function() {});
7667
+ this.timing.startAdFailsafeTimer(requestToken);
7668
+ return [
7669
+ 4,
7670
+ adPlayer.play()
7671
+ ];
7672
+ case 4:
7673
+ _state.sent();
7674
+ this.timing.markAdStarted();
7675
+ if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
7676
+ this.timing.scheduleAdStopAtBreakBoundary();
7677
+ }
7678
+ adPlayer.setAdVolume(currentMuted ? 0 : currentVolume);
7679
+ this.timing.consecutiveFailures = 0;
7680
+ if (podCount === 1) {
7681
+ this.continuousFetchingActive = true;
7682
+ this.startContinuousFetching(baseVastUrl);
7683
+ if (!this.preloadPool.active) {
7684
+ this.preloadPool.startPreloadPool(baseVastUrl, []);
7685
+ }
7686
+ }
7687
+ return [
7688
+ 2,
7689
+ true
7690
+ ];
7691
+ case 5:
7692
+ error = _state.sent();
7693
+ this.timing.clearAdRequestWatchdog();
7694
+ this.timing.clearAdFailsafeTimer();
7695
+ if (this.timing.activeAdRequestToken === requestToken) {
7696
+ this.timing.activeAdRequestToken = null;
7697
+ }
7698
+ if (this.debug) {
7699
+ console.warn("[POD] Optimized pod request/play failed:", error);
7700
+ }
7701
+ if (this.timing.isTemporaryAdError(error)) {
7702
+ this.timing.temporaryFailureUrls.set(podUrl, Date.now());
7703
+ }
7704
+ this.continuousFetchingActive = true;
7705
+ this.recreateAdController();
7706
+ return [
7707
+ 2,
7708
+ false
7709
+ ];
7710
+ case 6:
7192
7711
  return [
7193
7712
  2
7194
7713
  ];
@@ -7511,7 +8030,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7511
8030
  source: "hls",
7512
8031
  vastUrl: preloaded.vastUrl,
7513
8032
  timestamp: /* @__PURE__ */ new Date().toISOString()
7514
- }).catch(function() {});
8033
+ }, this.analyticsContext).catch(function() {});
7515
8034
  _state.label = 1;
7516
8035
  case 1:
7517
8036
  _state.trys.push([
@@ -7744,7 +8263,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7744
8263
  source: "hls",
7745
8264
  vastUrl: vastTagUrl,
7746
8265
  timestamp: /* @__PURE__ */ new Date().toISOString()
7747
- }).catch(function() {});
8266
+ }, this.analyticsContext).catch(function() {});
7748
8267
  this.timing.clearAdRequestWatchdog();
7749
8268
  if (this.timing.activeAdRequestToken !== requestToken) return [
7750
8269
  2
@@ -8270,6 +8789,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8270
8789
  },
8271
8790
  generateVastUrls: function generateVastUrls(base, count) {
8272
8791
  return _this.generateVastUrls(base, count);
8792
+ },
8793
+ generatePodVastUrl: function generatePodVastUrl(base, breakDurationMs) {
8794
+ return _this.generatePodVastUrl(base, breakDurationMs);
8273
8795
  }
8274
8796
  }, this.timing, this.preloadPool, this.adConfig, this.cueManager, this.placeholder);
8275
8797
  }
@@ -8302,6 +8824,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8302
8824
  return this.adConfig.generateVastUrlsWithCorrelators(baseUrl, count, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8303
8825
  }
8304
8826
  },
8827
+ {
8828
+ key: "generatePodVastUrl",
8829
+ value: function generatePodVastUrl(baseUrl, breakDurationMs) {
8830
+ var podParams = this.adConfig.resolvePodParams(breakDurationMs);
8831
+ return this.adConfig.generatePodVastUrl(baseUrl, podParams, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8832
+ }
8833
+ },
8305
8834
  {
8306
8835
  key: "load",
8307
8836
  value: // ───────────────────────────────────────────────────────────────
@@ -8536,7 +9065,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8536
9065
  sendAdDetectTracking(this.config.licenseKey, _object_spread({
8537
9066
  source: "scte35",
8538
9067
  timestamp: /* @__PURE__ */ new Date().toISOString()
8539
- }, detectPayload)).catch(function() {});
9068
+ }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8540
9069
  }
8541
9070
  var hasPrefetchedAds = this.adBreak.pendingAdBreak && this.adBreak.pendingAdBreak.vastUrls.length > 0;
8542
9071
  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;
@@ -8690,7 +9219,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8690
9219
  }
8691
9220
  this.adConfig.beginNewAdPod();
8692
9221
  this.adConfig.podAssignedByPrefetch = true;
8693
- var generatedUrls = this.generateVastUrls(baseVastUrl, 1);
9222
+ var optimizedPods = !!this.config.optimizedPods;
9223
+ var breakDurationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : void 0;
9224
+ var generatedUrls = optimizedPods ? [
9225
+ this.generatePodVastUrl(baseVastUrl, breakDurationMs)
9226
+ ] : this.generateVastUrls(baseVastUrl, 1);
8694
9227
  this.adBreak.pendingAdBreak = _object_spread_props(_object_spread({
8695
9228
  marker: marker
8696
9229
  }, fragmentSn !== void 0 ? {
@@ -8709,10 +9242,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8709
9242
  sendAdDetectTracking(this.config.licenseKey, _object_spread({
8710
9243
  source: "scte35",
8711
9244
  timestamp: /* @__PURE__ */ new Date().toISOString()
8712
- }, detectPayload)).catch(function() {});
9245
+ }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8713
9246
  if (this.debug) {
8714
9247
  console.log("[PREFETCH] Starting ad prefetch for upcoming ad break");
8715
- console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
9248
+ console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " ").concat(optimizedPods ? "pod" : "VAST", " URL(s)"));
9249
+ }
9250
+ if (optimizedPods) {
9251
+ if (this.debug) {
9252
+ console.log("[PREFETCH] Optimized pods enabled - skipping single-ad preload pool");
9253
+ }
9254
+ return;
8716
9255
  }
8717
9256
  this.preloadPool.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
8718
9257
  }