stormcloud-video-player 0.8.25 → 0.8.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/stormcloud-vp.min.js +1 -1
  2. package/lib/index.cjs +714 -128
  3. package/lib/index.cjs.map +1 -1
  4. package/lib/index.d.cts +7 -0
  5. package/lib/index.d.ts +7 -0
  6. package/lib/index.js +714 -128
  7. package/lib/index.js.map +1 -1
  8. package/lib/player/AdBreakOrchestrator.cjs +278 -49
  9. package/lib/player/AdBreakOrchestrator.cjs.map +1 -1
  10. package/lib/player/AdBreakOrchestrator.d.cts +4 -1
  11. package/lib/player/AdConfigManager.cjs +144 -14
  12. package/lib/player/AdConfigManager.cjs.map +1 -1
  13. package/lib/player/AdConfigManager.d.cts +12 -1
  14. package/lib/player/AdPreloadPool.d.cts +1 -1
  15. package/lib/player/AdTimingService.cjs +7 -0
  16. package/lib/player/AdTimingService.cjs.map +1 -1
  17. package/lib/player/AdTimingService.d.cts +2 -1
  18. package/lib/player/HlsEngine.d.cts +1 -1
  19. package/lib/player/PlayerControls.d.cts +1 -1
  20. package/lib/player/Scte35CueManager.d.cts +1 -1
  21. package/lib/player/Scte35Parser.d.cts +1 -1
  22. package/lib/player/StormcloudVideoPlayer.cjs +714 -128
  23. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  24. package/lib/player/StormcloudVideoPlayer.d.cts +2 -1
  25. package/lib/player/playerTypes.d.cts +1 -1
  26. package/lib/players/HlsPlayer.cjs +714 -128
  27. package/lib/players/HlsPlayer.cjs.map +1 -1
  28. package/lib/players/HlsPlayer.d.cts +1 -1
  29. package/lib/players/index.cjs +714 -128
  30. package/lib/players/index.cjs.map +1 -1
  31. package/lib/sdk/hlsAdPlayer.cjs +343 -71
  32. package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
  33. package/lib/sdk/hlsAdPlayer.d.cts +1 -1
  34. package/lib/{types-Bz4aRmzc.d.cts → types-Xgz2_W1C.d.cts} +6 -0
  35. package/lib/ui/StormcloudVideoPlayer.cjs +714 -128
  36. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  37. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  38. package/lib/utils/ctvVastSignals.cjs +80 -8
  39. package/lib/utils/ctvVastSignals.cjs.map +1 -1
  40. package/lib/utils/tracking.cjs +80 -8
  41. package/lib/utils/tracking.cjs.map +1 -1
  42. package/lib/utils/tracking.d.cts +1 -1
  43. package/lib/utils/vastEnvironmentSignals.cjs +83 -13
  44. package/lib/utils/vastEnvironmentSignals.cjs.map +1 -1
  45. package/lib/utils/vastMacros.cjs +15 -1
  46. package/lib/utils/vastMacros.cjs.map +1 -1
  47. package/lib/utils/vastMacros.d.cts +3 -0
  48. package/package.json +1 -1
@@ -448,6 +448,8 @@ function createHlsAdPlayer(contentVideo, options) {
448
448
  var adHls;
449
449
  var adContainerEl;
450
450
  var currentAd;
451
+ var podAds = [];
452
+ var podIndex = 0;
451
453
  var sessionId;
452
454
  var preloadedAds = /* @__PURE__ */ new Map();
453
455
  var preloadingAds = /* @__PURE__ */ new Map();
@@ -612,14 +614,14 @@ function createHlsAdPlayer(contentVideo, options) {
612
614
  error: []
613
615
  };
614
616
  }
615
- function extractTrackingUrls(xmlDoc) {
617
+ function extractTrackingUrls(scope) {
616
618
  var trackingUrls = createEmptyTrackingUrls();
617
- xmlDoc.querySelectorAll("Impression").forEach(function(el) {
619
+ scope.querySelectorAll("Impression").forEach(function(el) {
618
620
  var _el_textContent;
619
621
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
620
622
  if (url) trackingUrls.impression.push(url);
621
623
  });
622
- xmlDoc.querySelectorAll("Tracking").forEach(function(el) {
624
+ scope.querySelectorAll("Tracking").forEach(function(el) {
623
625
  var _el_textContent;
624
626
  var event = el.getAttribute("event");
625
627
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
@@ -630,7 +632,7 @@ function createHlsAdPlayer(contentVideo, options) {
630
632
  }
631
633
  }
632
634
  });
633
- xmlDoc.querySelectorAll("Error").forEach(function(el) {
635
+ scope.querySelectorAll("Error").forEach(function(el) {
634
636
  var _el_textContent;
635
637
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
636
638
  if (url) trackingUrls.error.push(url);
@@ -740,6 +742,75 @@ function createHlsAdPlayer(contentVideo, options) {
740
742
  };
741
743
  }
742
744
  }
745
+ function parseAdElement(adElement) {
746
+ try {
747
+ var _adElement_querySelector, _adElement_querySelector_textContent, _adElement_querySelector1, _adElement_querySelector_textContent1, _adElement_querySelector2, _adElement_querySelector3;
748
+ var adId = adElement.getAttribute("id") || "unknown";
749
+ var title = ((_adElement_querySelector = adElement.querySelector("AdTitle")) === null || _adElement_querySelector === void 0 ? void 0 : _adElement_querySelector.textContent) || "Ad";
750
+ 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();
751
+ 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();
752
+ if (wrapperUri) {
753
+ return {
754
+ type: "wrapper",
755
+ vastAdTagUri: wrapperUri,
756
+ trackingUrls: extractTrackingUrls(adElement),
757
+ clickThrough: clickThrough
758
+ };
759
+ }
760
+ var isNoAdAvailable = adId === "empty" || title.toLowerCase().includes("no ad available") || title.toLowerCase() === "no ad available";
761
+ var durationText = ((_adElement_querySelector3 = adElement.querySelector("Duration")) === null || _adElement_querySelector3 === void 0 ? void 0 : _adElement_querySelector3.textContent) || "00:00:30";
762
+ var durationParts = durationText.split(":");
763
+ var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
764
+ var mediaFileElements = adElement.querySelectorAll("MediaFile");
765
+ var mediaFiles = [];
766
+ mediaFileElements.forEach(function(mf) {
767
+ var _mf_textContent;
768
+ var type = mf.getAttribute("type") || "";
769
+ var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
770
+ var width = mf.getAttribute("width") || "";
771
+ var height = mf.getAttribute("height") || "";
772
+ if (!url) return;
773
+ var bitrateAttr = mf.getAttribute("bitrate");
774
+ var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
775
+ var mediaFile = {
776
+ url: url,
777
+ type: type,
778
+ width: parseInt(width || "1920", 10),
779
+ height: parseInt(height || "1080", 10),
780
+ bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
781
+ };
782
+ if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
783
+ mediaFiles.push(mediaFile);
784
+ }
785
+ });
786
+ if (mediaFiles.length === 0) {
787
+ if (isNoAdAvailable) {
788
+ console.warn("[HlsAdPlayer] Pod <Ad> indicates no ad available");
789
+ } else {
790
+ console.warn("[HlsAdPlayer] No supported media files in pod <Ad>");
791
+ }
792
+ return {
793
+ type: "empty"
794
+ };
795
+ }
796
+ return {
797
+ type: "inline",
798
+ ad: {
799
+ id: adId,
800
+ title: title,
801
+ duration: duration,
802
+ mediaFiles: mediaFiles,
803
+ trackingUrls: extractTrackingUrls(adElement),
804
+ clickThrough: clickThrough
805
+ }
806
+ };
807
+ } catch (error) {
808
+ console.error("[HlsAdPlayer] Error parsing pod <Ad> element:", error);
809
+ return {
810
+ type: "empty"
811
+ };
812
+ }
813
+ }
743
814
  function fetchVastXml(vastTagUrl) {
744
815
  return _async_to_generator(function() {
745
816
  var response;
@@ -816,6 +887,146 @@ function createHlsAdPlayer(contentVideo, options) {
816
887
  });
817
888
  }).apply(this, arguments);
818
889
  }
890
+ function fetchAndParsePod(vastTagUrl) {
891
+ return _async_to_generator(function() {
892
+ var vastXml, parser, xmlDoc, adElements, ads, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, adElement, parsed, resolved, error, err;
893
+ return _ts_generator(this, function(_state) {
894
+ switch(_state.label){
895
+ case 0:
896
+ return [
897
+ 4,
898
+ fetchVastXml(vastTagUrl)
899
+ ];
900
+ case 1:
901
+ vastXml = _state.sent();
902
+ console.log("[HlsAdPlayer] Pod VAST XML received");
903
+ parser = new DOMParser();
904
+ xmlDoc = parser.parseFromString(vastXml, "text/xml");
905
+ if (xmlDoc.querySelector("parsererror")) {
906
+ console.error("[HlsAdPlayer] Pod VAST XML parsing error (malformed)");
907
+ return [
908
+ 2,
909
+ []
910
+ ];
911
+ }
912
+ adElements = Array.from(xmlDoc.querySelectorAll("Ad"));
913
+ if (adElements.length === 0) {
914
+ console.warn("[HlsAdPlayer] No <Ad> elements found in pod VAST response");
915
+ return [
916
+ 2,
917
+ []
918
+ ];
919
+ }
920
+ adElements.sort(function(a, b) {
921
+ var aSeq = parseInt(a.getAttribute("sequence") || "", 10);
922
+ var bSeq = parseInt(b.getAttribute("sequence") || "", 10);
923
+ var aVal = Number.isFinite(aSeq) ? aSeq : Number.MAX_SAFE_INTEGER;
924
+ var bVal = Number.isFinite(bSeq) ? bSeq : Number.MAX_SAFE_INTEGER;
925
+ return aVal - bVal;
926
+ });
927
+ ads = [];
928
+ _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
929
+ _state.label = 2;
930
+ case 2:
931
+ _state.trys.push([
932
+ 2,
933
+ 10,
934
+ 11,
935
+ 12
936
+ ]);
937
+ _iterator = adElements[Symbol.iterator]();
938
+ _state.label = 3;
939
+ case 3:
940
+ if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [
941
+ 3,
942
+ 9
943
+ ];
944
+ adElement = _step.value;
945
+ parsed = parseAdElement(adElement);
946
+ if (!(parsed.type === "inline")) return [
947
+ 3,
948
+ 4
949
+ ];
950
+ ads.push(parsed.ad);
951
+ return [
952
+ 3,
953
+ 8
954
+ ];
955
+ case 4:
956
+ if (!(parsed.type === "wrapper")) return [
957
+ 3,
958
+ 8
959
+ ];
960
+ _state.label = 5;
961
+ case 5:
962
+ _state.trys.push([
963
+ 5,
964
+ 7,
965
+ ,
966
+ 8
967
+ ]);
968
+ return [
969
+ 4,
970
+ fetchAndParseVastAd(parsed.vastAdTagUri, 1, parsed.trackingUrls)
971
+ ];
972
+ case 6:
973
+ resolved = _state.sent();
974
+ if (resolved) {
975
+ ads.push(resolved);
976
+ }
977
+ return [
978
+ 3,
979
+ 8
980
+ ];
981
+ case 7:
982
+ error = _state.sent();
983
+ console.warn("[HlsAdPlayer] Failed to resolve wrapper <Ad> in pod:", error);
984
+ return [
985
+ 3,
986
+ 8
987
+ ];
988
+ case 8:
989
+ _iteratorNormalCompletion = true;
990
+ return [
991
+ 3,
992
+ 3
993
+ ];
994
+ case 9:
995
+ return [
996
+ 3,
997
+ 12
998
+ ];
999
+ case 10:
1000
+ err = _state.sent();
1001
+ _didIteratorError = true;
1002
+ _iteratorError = err;
1003
+ return [
1004
+ 3,
1005
+ 12
1006
+ ];
1007
+ case 11:
1008
+ try {
1009
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1010
+ _iterator.return();
1011
+ }
1012
+ } finally{
1013
+ if (_didIteratorError) {
1014
+ throw _iteratorError;
1015
+ }
1016
+ }
1017
+ return [
1018
+ 7
1019
+ ];
1020
+ case 12:
1021
+ console.log("[HlsAdPlayer] Parsed ".concat(ads.length, " ad(s) from pod VAST response"));
1022
+ return [
1023
+ 2,
1024
+ ads
1025
+ ];
1026
+ }
1027
+ });
1028
+ })();
1029
+ }
819
1030
  function createAdVideoElement() {
820
1031
  var video = document.createElement("video");
821
1032
  video.style.position = "absolute";
@@ -860,6 +1071,9 @@ function createHlsAdPlayer(contentVideo, options) {
860
1071
  trackingFired.complete = true;
861
1072
  fireTrackingPixels(currentAd.trackingUrls.complete);
862
1073
  console.log("[HlsAdPlayer] Ad completed");
1074
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1075
+ return;
1076
+ }
863
1077
  handleAdComplete();
864
1078
  });
865
1079
  adVideoElement.addEventListener("error", function(e) {
@@ -868,6 +1082,9 @@ function createHlsAdPlayer(contentVideo, options) {
868
1082
  if (currentAd) {
869
1083
  fireTrackingPixels(currentAd.trackingUrls.error);
870
1084
  }
1085
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1086
+ return;
1087
+ }
871
1088
  handleAdError();
872
1089
  });
873
1090
  adVideoElement.addEventListener("volumechange", function() {
@@ -896,6 +1113,94 @@ function createHlsAdPlayer(contentVideo, options) {
896
1113
  delete contentVideo.dataset.stormcloudAdPlaying;
897
1114
  }
898
1115
  }
1116
+ function resetQuartileTracking(preserveImpression) {
1117
+ trackingFired = {
1118
+ impression: preserveImpression ? trackingFired.impression : false,
1119
+ start: false,
1120
+ firstQuartile: false,
1121
+ midpoint: false,
1122
+ thirdQuartile: false,
1123
+ complete: false
1124
+ };
1125
+ }
1126
+ function loadCurrentAdMedia() {
1127
+ if (!adVideoElement || !currentAd) {
1128
+ throw new Error("No ad video element or ad to load");
1129
+ }
1130
+ var mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1131
+ if (!mediaFile) {
1132
+ throw new Error("No media file available for ad");
1133
+ }
1134
+ var isHlsAd = isHlsMediaFile(mediaFile);
1135
+ console.log("[HlsAdPlayer] Loading ".concat(isHlsAd ? "HLS" : "progressive", " ad (pod ").concat(podIndex + 1, "/").concat(podAds.length || 1, ") from: ").concat(mediaFile.url));
1136
+ if (isHlsAd && import_hls.default.isSupported()) {
1137
+ if (adHls) {
1138
+ adHls.destroy();
1139
+ }
1140
+ adHls = new import_hls.default({
1141
+ enableWorker: true,
1142
+ lowLatencyMode: false
1143
+ });
1144
+ adHls.loadSource(mediaFile.url);
1145
+ adHls.attachMedia(adVideoElement);
1146
+ adHls.on(import_hls.default.Events.MANIFEST_PARSED, function() {
1147
+ console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1148
+ adVideoElement.play().catch(function(error) {
1149
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1150
+ handleAdError();
1151
+ });
1152
+ });
1153
+ adHls.on(import_hls.default.Events.ERROR, function(_event, data) {
1154
+ console.error("[HlsAdPlayer] HLS error:", data);
1155
+ if (data.fatal) {
1156
+ handleAdError();
1157
+ }
1158
+ });
1159
+ } else if (isHlsAd && adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1160
+ adVideoElement.src = mediaFile.url;
1161
+ adVideoElement.play().catch(function(error) {
1162
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1163
+ handleAdError();
1164
+ });
1165
+ } else if (!isHlsAd && isProgressiveMediaFile(mediaFile)) {
1166
+ if (adHls) {
1167
+ adHls.destroy();
1168
+ adHls = void 0;
1169
+ }
1170
+ adVideoElement.src = mediaFile.url;
1171
+ adVideoElement.load();
1172
+ adVideoElement.play().catch(function(error) {
1173
+ console.error("[HlsAdPlayer] Error starting progressive ad playback:", error);
1174
+ handleAdError();
1175
+ });
1176
+ } else {
1177
+ throw new Error("Unsupported ad media file type: ".concat(mediaFile.type));
1178
+ }
1179
+ }
1180
+ function advanceToNextPodAd() {
1181
+ if (podIndex >= podAds.length - 1) {
1182
+ return false;
1183
+ }
1184
+ podIndex++;
1185
+ currentAd = podAds[podIndex];
1186
+ resetQuartileTracking(false);
1187
+ if (currentAd) {
1188
+ fireTrackingPixels(currentAd.trackingUrls.impression);
1189
+ trackingFired.impression = true;
1190
+ console.log("[HlsAdPlayer] Advancing to next pod ad (".concat(podIndex + 1, "/").concat(podAds.length, "): ").concat(currentAd.title, ", duration: ").concat(currentAd.duration, "s"));
1191
+ }
1192
+ try {
1193
+ loadCurrentAdMedia();
1194
+ emit("pod_ad_started", {
1195
+ index: podIndex,
1196
+ total: podAds.length
1197
+ });
1198
+ return true;
1199
+ } catch (error) {
1200
+ console.error("[HlsAdPlayer] Failed to load next pod ad:", error);
1201
+ return false;
1202
+ }
1203
+ }
899
1204
  function handleAdComplete() {
900
1205
  console.log("[HlsAdPlayer] Handling ad completion");
901
1206
  adPlaying = false;
@@ -949,7 +1254,7 @@ function createHlsAdPlayer(contentVideo, options) {
949
1254
  },
950
1255
  requestAds: function requestAds(vastTagUrl) {
951
1256
  return _async_to_generator(function() {
952
- var ad, error;
1257
+ var ads, preloaded, error;
953
1258
  return _ts_generator(this, function(_state) {
954
1259
  switch(_state.label){
955
1260
  case 0:
@@ -970,12 +1275,18 @@ function createHlsAdPlayer(contentVideo, options) {
970
1275
  6
971
1276
  ]);
972
1277
  sessionId = generateSessionId();
1278
+ ads = [];
973
1279
  if (!preloadedAds.has(vastTagUrl)) return [
974
1280
  3,
975
1281
  2
976
1282
  ];
977
- ad = preloadedAds.get(vastTagUrl);
1283
+ preloaded = preloadedAds.get(vastTagUrl);
978
1284
  preloadedAds.delete(vastTagUrl);
1285
+ if (preloaded) {
1286
+ ads = [
1287
+ preloaded
1288
+ ];
1289
+ }
979
1290
  console.log("[HlsAdPlayer] Using preloaded VAST response:", vastTagUrl);
980
1291
  return [
981
1292
  3,
@@ -984,13 +1295,13 @@ function createHlsAdPlayer(contentVideo, options) {
984
1295
  case 2:
985
1296
  return [
986
1297
  4,
987
- fetchAndParseVastAd(vastTagUrl)
1298
+ fetchAndParsePod(vastTagUrl)
988
1299
  ];
989
1300
  case 3:
990
- ad = _state.sent();
1301
+ ads = _state.sent();
991
1302
  _state.label = 4;
992
1303
  case 4:
993
- if (!ad) {
1304
+ if (!ads.length) {
994
1305
  console.warn("[HlsAdPlayer] No ads available from VAST response");
995
1306
  emit("ad_error");
996
1307
  return [
@@ -998,10 +1309,14 @@ function createHlsAdPlayer(contentVideo, options) {
998
1309
  Promise.reject(new Error("No ads available from VAST response"))
999
1310
  ];
1000
1311
  }
1001
- currentAd = ad;
1002
- console.log("[HlsAdPlayer] Ad parsed: ".concat(ad.title, ", duration: ").concat(ad.duration, "s"));
1003
- fireTrackingPixels(ad.trackingUrls.impression);
1004
- trackingFired.impression = true;
1312
+ podAds = ads;
1313
+ podIndex = 0;
1314
+ currentAd = ads[0];
1315
+ 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"));
1316
+ if (currentAd) {
1317
+ fireTrackingPixels(currentAd.trackingUrls.impression);
1318
+ trackingFired.impression = true;
1319
+ }
1005
1320
  return [
1006
1321
  2,
1007
1322
  Promise.resolve()
@@ -1024,7 +1339,7 @@ function createHlsAdPlayer(contentVideo, options) {
1024
1339
  },
1025
1340
  play: function play() {
1026
1341
  return _async_to_generator(function() {
1027
- var contentVolume, mediaFile, isHlsAd;
1342
+ var contentVolume;
1028
1343
  return _ts_generator(this, function(_state) {
1029
1344
  if (!currentAd) {
1030
1345
  console.warn("[HlsAdPlayer] Cannot play: No ad loaded (no ads available)");
@@ -1040,14 +1355,7 @@ function createHlsAdPlayer(contentVideo, options) {
1040
1355
  adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.appendChild(adVideoElement);
1041
1356
  setupAdEventListeners();
1042
1357
  }
1043
- trackingFired = {
1044
- impression: trackingFired.impression,
1045
- start: false,
1046
- firstQuartile: false,
1047
- midpoint: false,
1048
- thirdQuartile: false,
1049
- complete: false
1050
- };
1358
+ resetQuartileTracking(true);
1051
1359
  contentVolume = contentVideo.volume;
1052
1360
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1053
1361
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
@@ -1065,60 +1373,12 @@ function createHlsAdPlayer(contentVideo, options) {
1065
1373
  adVideoElement.muted = originalMutedState;
1066
1374
  console.log("[HlsAdPlayer] Set ad video volume to ".concat(adVideoElement.volume, ", muted: ").concat(adVideoElement.muted, ", originalMutedState: ").concat(originalMutedState, ", contentVolume: ").concat(contentVolume));
1067
1375
  }
1068
- mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1069
- if (!mediaFile) {
1070
- throw new Error("No media file available for ad");
1071
- }
1072
1376
  if (adContainerEl) {
1073
1377
  adContainerEl.style.display = "flex";
1074
1378
  adContainerEl.style.pointerEvents = "auto";
1075
1379
  }
1076
1380
  emit("content_pause");
1077
- isHlsAd = isHlsMediaFile(mediaFile);
1078
- console.log("[HlsAdPlayer] Loading ".concat(isHlsAd ? "HLS" : "progressive", " ad from: ").concat(mediaFile.url));
1079
- if (isHlsAd && import_hls.default.isSupported()) {
1080
- if (adHls) {
1081
- adHls.destroy();
1082
- }
1083
- adHls = new import_hls.default({
1084
- enableWorker: true,
1085
- lowLatencyMode: false
1086
- });
1087
- adHls.loadSource(mediaFile.url);
1088
- adHls.attachMedia(adVideoElement);
1089
- adHls.on(import_hls.default.Events.MANIFEST_PARSED, function() {
1090
- console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1091
- adVideoElement.play().catch(function(error) {
1092
- console.error("[HlsAdPlayer] Error starting ad playback:", error);
1093
- handleAdError();
1094
- });
1095
- });
1096
- adHls.on(import_hls.default.Events.ERROR, function(event, data) {
1097
- console.error("[HlsAdPlayer] HLS error:", data);
1098
- if (data.fatal) {
1099
- handleAdError();
1100
- }
1101
- });
1102
- } else if (isHlsAd && adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1103
- adVideoElement.src = mediaFile.url;
1104
- adVideoElement.play().catch(function(error) {
1105
- console.error("[HlsAdPlayer] Error starting ad playback:", error);
1106
- handleAdError();
1107
- });
1108
- } else if (!isHlsAd && isProgressiveMediaFile(mediaFile)) {
1109
- if (adHls) {
1110
- adHls.destroy();
1111
- adHls = void 0;
1112
- }
1113
- adVideoElement.src = mediaFile.url;
1114
- adVideoElement.load();
1115
- adVideoElement.play().catch(function(error) {
1116
- console.error("[HlsAdPlayer] Error starting progressive ad playback:", error);
1117
- handleAdError();
1118
- });
1119
- } else {
1120
- throw new Error("Unsupported ad media file type: ".concat(mediaFile.type));
1121
- }
1381
+ loadCurrentAdMedia();
1122
1382
  return [
1123
1383
  2,
1124
1384
  Promise.resolve()
@@ -1193,6 +1453,8 @@ function createHlsAdPlayer(contentVideo, options) {
1193
1453
  adVideoElement.src = "";
1194
1454
  }
1195
1455
  currentAd = void 0;
1456
+ podAds = [];
1457
+ podIndex = 0;
1196
1458
  return [
1197
1459
  2
1198
1460
  ];
@@ -1242,6 +1504,8 @@ function createHlsAdPlayer(contentVideo, options) {
1242
1504
  }
1243
1505
  adContainerEl = void 0;
1244
1506
  currentAd = void 0;
1507
+ podAds = [];
1508
+ podIndex = 0;
1245
1509
  listeners.clear();
1246
1510
  preloadedAds.clear();
1247
1511
  preloadingAds.clear();
@@ -1333,6 +1597,14 @@ function createHlsAdPlayer(contentVideo, options) {
1333
1597
  },
1334
1598
  getLoadedAdId: function getLoadedAdId() {
1335
1599
  return currentAd === null || currentAd === void 0 ? void 0 : currentAd.id;
1600
+ },
1601
+ getPodAdCount: function getPodAdCount() {
1602
+ return podAds.length;
1603
+ },
1604
+ getPodTotalDurationMs: function getPodTotalDurationMs() {
1605
+ return podAds.reduce(function(total, ad) {
1606
+ return total + (ad.duration > 0 ? ad.duration * 1e3 : 0);
1607
+ }, 0);
1336
1608
  }
1337
1609
  };
1338
1610
  }
@@ -1712,38 +1984,108 @@ function getOrCreateStoredUuid(storageKey) {
1712
1984
  if (typeof window === "undefined") {
1713
1985
  return void 0;
1714
1986
  }
1715
- var existing = readStoredString(storageKey);
1987
+ var existing = readPersistentId(storageKey);
1716
1988
  if (existing) {
1989
+ writePersistentId(storageKey, existing);
1717
1990
  return existing;
1718
1991
  }
1719
1992
  var id = createUuid();
1720
- try {
1721
- var _window_localStorage;
1722
- (_window_localStorage = window.localStorage) === null || _window_localStorage === void 0 ? void 0 : _window_localStorage.setItem(storageKey, id);
1723
- } catch (unused) {}
1993
+ writePersistentId(storageKey, id);
1724
1994
  return id;
1725
1995
  }
1726
1996
  function getOrCreateCtvSessionId() {
1727
- var existing = readStoredString(CTV_SESSION_STORAGE_KEY);
1997
+ var existing = readPersistentId(CTV_SESSION_STORAGE_KEY);
1728
1998
  if (existing) {
1999
+ writePersistentId(CTV_SESSION_STORAGE_KEY, existing);
1729
2000
  return existing;
1730
2001
  }
1731
2002
  var sessionId = createUuid();
2003
+ writePersistentId(CTV_SESSION_STORAGE_KEY, sessionId);
2004
+ return sessionId;
2005
+ }
2006
+ function readPersistentId(storageKey) {
2007
+ return readStoredString(storageKey) || readCookie(storageKey);
2008
+ }
2009
+ function writePersistentId(storageKey, value) {
1732
2010
  try {
1733
2011
  var _window_localStorage;
1734
- (_window_localStorage = window.localStorage) === null || _window_localStorage === void 0 ? void 0 : _window_localStorage.setItem(CTV_SESSION_STORAGE_KEY, sessionId);
2012
+ (_window_localStorage = window.localStorage) === null || _window_localStorage === void 0 ? void 0 : _window_localStorage.setItem(storageKey, value);
2013
+ } catch (unused) {}
2014
+ writeCookie(storageKey, value);
2015
+ }
2016
+ var PERSISTENT_ID_COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5;
2017
+ function readCookie(name) {
2018
+ if (typeof document === "undefined") {
2019
+ return void 0;
2020
+ }
2021
+ try {
2022
+ var prefix = encodeURIComponent(name) + "=";
2023
+ var parts = document.cookie ? document.cookie.split(";") : [];
2024
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2025
+ try {
2026
+ for(var _iterator = parts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
2027
+ var part = _step.value;
2028
+ var entry = part.trim();
2029
+ if (entry.startsWith(prefix)) {
2030
+ return decodeURIComponent(entry.slice(prefix.length)) || void 0;
2031
+ }
2032
+ }
2033
+ } catch (err) {
2034
+ _didIteratorError = true;
2035
+ _iteratorError = err;
2036
+ } finally{
2037
+ try {
2038
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
2039
+ _iterator.return();
2040
+ }
2041
+ } finally{
2042
+ if (_didIteratorError) {
2043
+ throw _iteratorError;
2044
+ }
2045
+ }
2046
+ }
2047
+ } catch (unused) {}
2048
+ return void 0;
2049
+ }
2050
+ function writeCookie(name, value) {
2051
+ if (typeof document === "undefined") {
2052
+ return;
2053
+ }
2054
+ try {
2055
+ document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + ";path=/;max-age=" + PERSISTENT_ID_COOKIE_MAX_AGE + ";SameSite=Lax";
1735
2056
  } catch (unused) {}
1736
- return sessionId;
1737
2057
  }
1738
2058
  function createUuid() {
2059
+ var _bytes_, _bytes_1;
1739
2060
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1740
2061
  return crypto.randomUUID();
1741
2062
  }
1742
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(char) {
1743
- var value = Math.floor(Math.random() * 16);
1744
- var nibble = char === "x" ? value : value & 3 | 8;
1745
- return nibble.toString(16);
1746
- });
2063
+ var bytes = new Uint8Array(16);
2064
+ if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
2065
+ crypto.getRandomValues(bytes);
2066
+ } else {
2067
+ var seed = Date.now() ^ Math.floor(performanceNow() * 1e3);
2068
+ for(var i = 0; i < 16; i++){
2069
+ seed = seed * 1103515245 + 12345 & 2147483647;
2070
+ bytes[i] = (seed ^ Math.floor(Math.random() * 256)) & 255;
2071
+ }
2072
+ }
2073
+ bytes[6] = ((_bytes_ = bytes[6]) !== null && _bytes_ !== void 0 ? _bytes_ : 0) & 15 | 64;
2074
+ bytes[8] = ((_bytes_1 = bytes[8]) !== null && _bytes_1 !== void 0 ? _bytes_1 : 0) & 63 | 128;
2075
+ var hex = "";
2076
+ for(var i1 = 0; i1 < 16; i1++){
2077
+ var _bytes_i;
2078
+ hex += ((_bytes_i = bytes[i1]) !== null && _bytes_i !== void 0 ? _bytes_i : 0).toString(16).padStart(2, "0");
2079
+ }
2080
+ return hex.slice(0, 8) + "-" + hex.slice(8, 12) + "-" + hex.slice(12, 16) + "-" + hex.slice(16, 20) + "-" + hex.slice(20, 32);
2081
+ }
2082
+ function performanceNow() {
2083
+ try {
2084
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
2085
+ return performance.now();
2086
+ }
2087
+ } catch (unused) {}
2088
+ return 0;
1747
2089
  }
1748
2090
  function readPlatformNativeDeviceId() {
1749
2091
  if (typeof window === "undefined") return {};
@@ -3161,7 +3503,20 @@ function applyVastMacros(baseUrl, ctx) {
3161
3503
  if (ctx.pod != null) {
3162
3504
  params.set("pod", String(ctx.pod));
3163
3505
  }
3164
- if (ctx.adPosition != null) {
3506
+ var optimizedPodMode = ctx.podMaxAds != null || ctx.podMinDurationMs != null || ctx.podMaxDurationMs != null;
3507
+ if (optimizedPodMode) {
3508
+ params.delete("ppos");
3509
+ params.delete("lip");
3510
+ if (ctx.podMinDurationMs != null) {
3511
+ params.set("pmnd", String(Math.max(0, Math.round(ctx.podMinDurationMs))));
3512
+ }
3513
+ if (ctx.podMaxDurationMs != null) {
3514
+ params.set("pmxd", String(Math.max(0, Math.round(ctx.podMaxDurationMs))));
3515
+ }
3516
+ if (ctx.podMaxAds != null) {
3517
+ params.set("pmad", String(Math.max(1, Math.round(ctx.podMaxAds))));
3518
+ }
3519
+ } else if (ctx.adPosition != null) {
3165
3520
  params.set("ppos", String(ctx.adPosition));
3166
3521
  }
3167
3522
  var requestUrl = ctx.contentUrl || ctx.pageUrl;
@@ -4503,6 +4858,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
4503
4858
  this.podAssignedByPrefetch = false;
4504
4859
  this.adRequestPositionInBreak = 0;
4505
4860
  this.lastHeartbeatTime = 0;
4861
+ this.defaultPodMaxAds = 6;
4506
4862
  this.config = config;
4507
4863
  this.video = video;
4508
4864
  }
@@ -4958,6 +5314,52 @@ var AdConfigManager = /*#__PURE__*/ function() {
4958
5314
  return urls;
4959
5315
  }
4960
5316
  },
5317
+ {
5318
+ key: "resolvePodParams",
5319
+ value: function resolvePodParams(breakDurationMs) {
5320
+ var maxDurationMs = breakDurationMs != null && breakDurationMs > 0 ? breakDurationMs : this.config.podMaxDurationMs;
5321
+ var maxAds = this.config.podMaxAds && this.config.podMaxAds > 0 ? this.config.podMaxAds : this.apiNumberAds && this.apiNumberAds > 0 ? this.apiNumberAds : this.defaultPodMaxAds;
5322
+ var minDurationMs = this.config.podExactDuration && maxDurationMs != null ? maxDurationMs : 0;
5323
+ return {
5324
+ maxAds: maxAds,
5325
+ minDurationMs: minDurationMs,
5326
+ maxDurationMs: maxDurationMs
5327
+ };
5328
+ }
5329
+ },
5330
+ {
5331
+ key: "generatePodVastUrl",
5332
+ value: function generatePodVastUrl(baseUrl, podParams, adPlayer, palNonce, inAdBreak) {
5333
+ var _podParams_maxDurationMs;
5334
+ var adWillPlayMuted = inAdBreak ? adPlayer.getOriginalMutedState() : this.video.muted;
5335
+ var envSignals = resolveVastEnvironmentSignals(!!this.config.ctvAdRequest);
5336
+ var urlWithMacros = applyVastMacros(baseUrl, {
5337
+ correlator: generateCorrelator(),
5338
+ streamCorrelator: this.streamCorrelator,
5339
+ pod: this.podCounter > 0 ? this.podCounter : void 0,
5340
+ podMaxAds: podParams.maxAds,
5341
+ podMinDurationMs: podParams.minDurationMs,
5342
+ podMaxDurationMs: podParams.maxDurationMs,
5343
+ isCtv: envSignals.isCtv,
5344
+ contentUrl: envSignals.contentUrl,
5345
+ pageUrl: !envSignals.isCtv && typeof window !== "undefined" ? window.location.href : void 0,
5346
+ adWillPlayMuted: adWillPlayMuted,
5347
+ adWillAutoPlay: !!this.config.autoplay,
5348
+ appId: envSignals.appId,
5349
+ appName: envSignals.appName,
5350
+ sessionId: envSignals.sessionId,
5351
+ deviceId: envSignals.deviceId,
5352
+ deviceIdType: envSignals.deviceIdType,
5353
+ limitAdTracking: envSignals.limitAdTracking,
5354
+ deviceTypeHint: envSignals.deviceTypeHint,
5355
+ adTest: this.config.adTest,
5356
+ consent: this.consentSignals
5357
+ });
5358
+ var finalUrl = palNonce.injectNonce(urlWithMacros);
5359
+ 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);
5360
+ return finalUrl;
5361
+ }
5362
+ },
4961
5363
  {
4962
5364
  // --- Tracking ---
4963
5365
  key: "initializeTracking",
@@ -5263,6 +5665,13 @@ var AdTimingService = /*#__PURE__*/ function() {
5263
5665
  this.lastAdRequestTime = Date.now();
5264
5666
  }
5265
5667
  },
5668
+ {
5669
+ key: "notePodAdStarted",
5670
+ value: function notePodAdStarted() {
5671
+ this.currentAdIndex++;
5672
+ this.totalAdsInBreak = this.currentAdIndex;
5673
+ }
5674
+ },
5266
5675
  {
5267
5676
  key: "getRemainingAdMs",
5268
5677
  value: function getRemainingAdMs() {
@@ -6771,13 +7180,26 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6771
7180
  }
6772
7181
  },
6773
7182
  {
6774
- // ───────────────────────────────────────────────────────────────
7183
+ key: "optimizedPodsEnabled",
7184
+ get: // ───────────────────────────────────────────────────────────────
6775
7185
  // IMA event listeners
6776
7186
  // ───────────────────────────────────────────────────────────────
7187
+ function get() {
7188
+ return !!this.host.config.optimizedPods;
7189
+ }
7190
+ },
7191
+ {
6777
7192
  key: "attachImaEventListeners",
6778
7193
  value: function attachImaEventListeners() {
6779
7194
  var _this = this;
6780
7195
  var adPlayer = this.host.getAdPlayer();
7196
+ adPlayer.on("pod_ad_started", function(payload) {
7197
+ _this.timing.notePodAdStarted();
7198
+ if (_this.debug) {
7199
+ var _ref;
7200
+ 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 : "?"));
7201
+ }
7202
+ });
6781
7203
  adPlayer.on("all_ads_completed", function() {
6782
7204
  sendAdImpressionTracking(_this.host.config.licenseKey, {
6783
7205
  source: "hls",
@@ -6935,7 +7357,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6935
7357
  // ───────────────────────────────────────────────────────────────
6936
7358
  function handleAdStart(_marker) {
6937
7359
  return _async_to_generator(function() {
6938
- var scheduled, tags, baseVastUrl, adBreakDurationMs, mode, currentMuted, currentVolume, firstAdUrl, usePreloadedAd, preloadedController, preloaded, firstAdUrlArray, adPlayer, preservedMuted, preservedVolume, adVolume, adPlayer1, adVolume1, error, fallbackPreloaded, ap, adVolume2, fallbackError;
7360
+ 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;
6939
7361
  return _ts_generator(this, function(_state) {
6940
7362
  switch(_state.label){
6941
7363
  case 0:
@@ -6982,6 +7404,26 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6982
7404
  if (this.timing.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
6983
7405
  this.timing.expectedAdBreakDurationMs = adBreakDurationMs;
6984
7406
  }
7407
+ if (!this.optimizedPodsEnabled) return [
7408
+ 3,
7409
+ 2
7410
+ ];
7411
+ return [
7412
+ 4,
7413
+ this.startOptimizedPod(baseVastUrl, adBreakDurationMs, currentMuted, currentVolume)
7414
+ ];
7415
+ case 1:
7416
+ podPlayed = _state.sent();
7417
+ if (podPlayed) {
7418
+ return [
7419
+ 2
7420
+ ];
7421
+ }
7422
+ if (this.debug) {
7423
+ console.log("[POD] Optimized pod unavailable, falling back to sequential single-ad path");
7424
+ }
7425
+ _state.label = 2;
7426
+ case 2:
6985
7427
  usePreloadedAd = false;
6986
7428
  preloaded = this.preloadPool.getPreloadedAd();
6987
7429
  if (preloaded) {
@@ -7023,21 +7465,21 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7023
7465
  if (this.debug) {
7024
7466
  console.log("[CONTINUOUS-FETCH] First VAST URL:", firstAdUrl);
7025
7467
  }
7026
- _state.label = 1;
7027
- case 1:
7468
+ _state.label = 3;
7469
+ case 3:
7028
7470
  _state.trys.push([
7029
- 1,
7030
- 12,
7471
+ 3,
7472
+ 14,
7031
7473
  ,
7032
- 20
7474
+ 22
7033
7475
  ]);
7034
7476
  if (!(usePreloadedAd && preloadedController)) return [
7035
7477
  3,
7036
- 5
7478
+ 7
7037
7479
  ];
7038
7480
  if (!!this.timing.ensureLoadedAdFitsBudget(preloadedController)) return [
7039
7481
  3,
7040
- 3
7482
+ 5
7041
7483
  ];
7042
7484
  this.timing.rejectLoadedAdForDuration(preloadedController);
7043
7485
  this.startContinuousFetching(baseVastUrl);
@@ -7045,12 +7487,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7045
7487
  4,
7046
7488
  this.tryNextAvailableAdWithRateLimit()
7047
7489
  ];
7048
- case 2:
7490
+ case 4:
7049
7491
  _state.sent();
7050
7492
  return [
7051
7493
  2
7052
7494
  ];
7053
- case 3:
7495
+ case 5:
7054
7496
  adPlayer = this.host.getAdPlayer();
7055
7497
  preservedMuted = adPlayer.getOriginalMutedState();
7056
7498
  preservedVolume = adPlayer.getOriginalVolume();
@@ -7075,7 +7517,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7075
7517
  4,
7076
7518
  preloadedController.play()
7077
7519
  ];
7078
- case 4:
7520
+ case 6:
7079
7521
  _state.sent();
7080
7522
  this.timing.markAdStarted();
7081
7523
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7085,22 +7527,22 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7085
7527
  preloadedController.setAdVolume(adVolume);
7086
7528
  return [
7087
7529
  3,
7088
- 11
7530
+ 13
7089
7531
  ];
7090
- case 5:
7532
+ case 7:
7091
7533
  adPlayer1 = this.host.getAdPlayer();
7092
7534
  return [
7093
7535
  4,
7094
7536
  this.timing.enforceGlobalRateLimit()
7095
7537
  ];
7096
- case 6:
7538
+ case 8:
7097
7539
  _state.sent();
7098
7540
  this.timing.lastAdRequestTime = Date.now();
7099
7541
  return [
7100
7542
  4,
7101
7543
  adPlayer1.requestAds(firstAdUrl)
7102
7544
  ];
7103
- case 7:
7545
+ case 9:
7104
7546
  _state.sent();
7105
7547
  sendAdLoadedTracking(this.host.config.licenseKey, {
7106
7548
  source: "hls",
@@ -7112,7 +7554,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7112
7554
  }
7113
7555
  if (!!this.timing.ensureLoadedAdFitsBudget(adPlayer1)) return [
7114
7556
  3,
7115
- 9
7557
+ 11
7116
7558
  ];
7117
7559
  this.timing.rejectLoadedAdForDuration(adPlayer1);
7118
7560
  this.recreateAdController();
@@ -7121,12 +7563,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7121
7563
  4,
7122
7564
  this.tryNextAvailableAdWithRateLimit()
7123
7565
  ];
7124
- case 8:
7566
+ case 10:
7125
7567
  _state.sent();
7126
7568
  return [
7127
7569
  2
7128
7570
  ];
7129
- case 9:
7571
+ case 11:
7130
7572
  this.timing.consecutiveFailures = 0;
7131
7573
  this.startContinuousFetching(baseVastUrl);
7132
7574
  if (!this.preloadPool.active) {
@@ -7136,7 +7578,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7136
7578
  4,
7137
7579
  adPlayer1.play()
7138
7580
  ];
7139
- case 10:
7581
+ case 12:
7140
7582
  _state.sent();
7141
7583
  this.timing.markAdStarted();
7142
7584
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7144,40 +7586,40 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7144
7586
  }
7145
7587
  adVolume1 = currentMuted ? 0 : currentVolume;
7146
7588
  adPlayer1.setAdVolume(adVolume1);
7147
- _state.label = 11;
7148
- case 11:
7589
+ _state.label = 13;
7590
+ case 13:
7149
7591
  return [
7150
7592
  3,
7151
- 20
7593
+ 22
7152
7594
  ];
7153
- case 12:
7595
+ case 14:
7154
7596
  error = _state.sent();
7155
7597
  if (this.debug) {
7156
7598
  console.warn("[CONTINUOUS-FETCH] First ad request failed:", error);
7157
7599
  }
7158
7600
  if (!!usePreloadedAd) return [
7159
7601
  3,
7160
- 18
7602
+ 20
7161
7603
  ];
7162
7604
  fallbackPreloaded = this.preloadPool.getPreloadedAd();
7163
7605
  if (!fallbackPreloaded) return [
7164
7606
  3,
7165
- 18
7607
+ 20
7166
7608
  ];
7167
7609
  if (this.debug) {
7168
7610
  console.log("[CONTINUOUS-FETCH] First ad failed, using preloaded fallback");
7169
7611
  }
7170
- _state.label = 13;
7171
- case 13:
7612
+ _state.label = 15;
7613
+ case 15:
7172
7614
  _state.trys.push([
7173
- 13,
7174
- 17,
7615
+ 15,
7616
+ 19,
7175
7617
  ,
7176
- 18
7618
+ 20
7177
7619
  ]);
7178
7620
  if (!!this.timing.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
7179
7621
  3,
7180
- 15
7622
+ 17
7181
7623
  ];
7182
7624
  this.timing.rejectLoadedAdForDuration(fallbackPreloaded.adController);
7183
7625
  this.startContinuousFetching(baseVastUrl);
@@ -7185,12 +7627,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7185
7627
  4,
7186
7628
  this.tryNextAvailableAdWithRateLimit()
7187
7629
  ];
7188
- case 14:
7630
+ case 16:
7189
7631
  _state.sent();
7190
7632
  return [
7191
7633
  2
7192
7634
  ];
7193
- case 15:
7635
+ case 17:
7194
7636
  this.swapToPreloadedAdPlayer(fallbackPreloaded.adController);
7195
7637
  this.timing.consecutiveFailures = 0;
7196
7638
  this.startContinuousFetching(baseVastUrl);
@@ -7203,7 +7645,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7203
7645
  4,
7204
7646
  ap.play()
7205
7647
  ];
7206
- case 16:
7648
+ case 18:
7207
7649
  _state.sent();
7208
7650
  this.timing.markAdStarted();
7209
7651
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7214,16 +7656,16 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7214
7656
  return [
7215
7657
  2
7216
7658
  ];
7217
- case 17:
7659
+ case 19:
7218
7660
  fallbackError = _state.sent();
7219
7661
  if (this.debug) {
7220
7662
  console.warn("[CONTINUOUS-FETCH] Preloaded fallback also failed:", fallbackError);
7221
7663
  }
7222
7664
  return [
7223
7665
  3,
7224
- 18
7666
+ 20
7225
7667
  ];
7226
- case 18:
7668
+ case 20:
7227
7669
  if (this.timing.isTemporaryAdError(error)) {
7228
7670
  this.timing.temporaryFailureUrls.set(firstAdUrl, Date.now());
7229
7671
  if (this.debug) {
@@ -7241,13 +7683,137 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7241
7683
  4,
7242
7684
  this.tryNextAvailableAdWithRateLimit()
7243
7685
  ];
7244
- case 19:
7686
+ case 21:
7245
7687
  _state.sent();
7246
7688
  return [
7247
7689
  3,
7248
- 20
7690
+ 22
7249
7691
  ];
7250
- case 20:
7692
+ case 22:
7693
+ return [
7694
+ 2
7695
+ ];
7696
+ }
7697
+ });
7698
+ }).call(this);
7699
+ }
7700
+ },
7701
+ {
7702
+ key: "startOptimizedPod",
7703
+ value: // ───────────────────────────────────────────────────────────────
7704
+ // Optimized pod (single multi-ad request)
7705
+ // ───────────────────────────────────────────────────────────────
7706
+ function startOptimizedPod(baseVastUrl, breakDurationMs, currentMuted, currentVolume) {
7707
+ return _async_to_generator(function() {
7708
+ var podUrl, adPlayer, requestToken, _ref, _adPlayer_getPodAdCount, podCount, error;
7709
+ return _ts_generator(this, function(_state) {
7710
+ switch(_state.label){
7711
+ case 0:
7712
+ if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {
7713
+ podUrl = this.pendingAdBreak.vastUrls[0];
7714
+ this.clearPendingAdBreak();
7715
+ } else {
7716
+ podUrl = this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);
7717
+ }
7718
+ if (!podUrl) {
7719
+ return [
7720
+ 2,
7721
+ false
7722
+ ];
7723
+ }
7724
+ this.continuousFetchingActive = false;
7725
+ this.recreateAdController();
7726
+ adPlayer = this.host.getAdPlayer();
7727
+ requestToken = ++this.timing.adRequestTokenCounter;
7728
+ this.timing.activeAdRequestToken = requestToken;
7729
+ this.timing.startAdRequestWatchdog(requestToken);
7730
+ _state.label = 1;
7731
+ case 1:
7732
+ _state.trys.push([
7733
+ 1,
7734
+ 5,
7735
+ ,
7736
+ 6
7737
+ ]);
7738
+ return [
7739
+ 4,
7740
+ this.timing.enforceGlobalRateLimit()
7741
+ ];
7742
+ case 2:
7743
+ _state.sent();
7744
+ this.timing.lastAdRequestTime = Date.now();
7745
+ return [
7746
+ 4,
7747
+ adPlayer.requestAds(podUrl)
7748
+ ];
7749
+ case 3:
7750
+ _state.sent();
7751
+ this.timing.clearAdRequestWatchdog();
7752
+ if (this.timing.activeAdRequestToken !== requestToken) {
7753
+ return [
7754
+ 2,
7755
+ true
7756
+ ];
7757
+ }
7758
+ podCount = (_ref = (_adPlayer_getPodAdCount = adPlayer.getPodAdCount) === null || _adPlayer_getPodAdCount === void 0 ? void 0 : _adPlayer_getPodAdCount.call(adPlayer)) !== null && _ref !== void 0 ? _ref : 1;
7759
+ if (podCount < 1) {
7760
+ return [
7761
+ 2,
7762
+ false
7763
+ ];
7764
+ }
7765
+ if (this.debug) {
7766
+ 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"));
7767
+ }
7768
+ sendAdLoadedTracking(this.host.config.licenseKey, {
7769
+ source: "hls",
7770
+ vastUrl: podUrl,
7771
+ timestamp: /* @__PURE__ */ new Date().toISOString()
7772
+ }, this.analyticsContext).catch(function() {});
7773
+ this.timing.startAdFailsafeTimer(requestToken);
7774
+ return [
7775
+ 4,
7776
+ adPlayer.play()
7777
+ ];
7778
+ case 4:
7779
+ _state.sent();
7780
+ this.timing.markAdStarted();
7781
+ if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
7782
+ this.timing.scheduleAdStopAtBreakBoundary();
7783
+ }
7784
+ adPlayer.setAdVolume(currentMuted ? 0 : currentVolume);
7785
+ this.timing.consecutiveFailures = 0;
7786
+ if (podCount === 1) {
7787
+ this.continuousFetchingActive = true;
7788
+ this.startContinuousFetching(baseVastUrl);
7789
+ if (!this.preloadPool.active) {
7790
+ this.preloadPool.startPreloadPool(baseVastUrl, []);
7791
+ }
7792
+ }
7793
+ return [
7794
+ 2,
7795
+ true
7796
+ ];
7797
+ case 5:
7798
+ error = _state.sent();
7799
+ this.timing.clearAdRequestWatchdog();
7800
+ this.timing.clearAdFailsafeTimer();
7801
+ if (this.timing.activeAdRequestToken === requestToken) {
7802
+ this.timing.activeAdRequestToken = null;
7803
+ }
7804
+ if (this.debug) {
7805
+ console.warn("[POD] Optimized pod request/play failed:", error);
7806
+ }
7807
+ if (this.timing.isTemporaryAdError(error)) {
7808
+ this.timing.temporaryFailureUrls.set(podUrl, Date.now());
7809
+ }
7810
+ this.continuousFetchingActive = true;
7811
+ this.recreateAdController();
7812
+ return [
7813
+ 2,
7814
+ false
7815
+ ];
7816
+ case 6:
7251
7817
  return [
7252
7818
  2
7253
7819
  ];
@@ -8329,6 +8895,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8329
8895
  },
8330
8896
  generateVastUrls: function generateVastUrls(base, count) {
8331
8897
  return _this.generateVastUrls(base, count);
8898
+ },
8899
+ generatePodVastUrl: function generatePodVastUrl(base, breakDurationMs) {
8900
+ return _this.generatePodVastUrl(base, breakDurationMs);
8332
8901
  }
8333
8902
  }, this.timing, this.preloadPool, this.adConfig, this.cueManager, this.placeholder);
8334
8903
  }
@@ -8361,6 +8930,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8361
8930
  return this.adConfig.generateVastUrlsWithCorrelators(baseUrl, count, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8362
8931
  }
8363
8932
  },
8933
+ {
8934
+ key: "generatePodVastUrl",
8935
+ value: function generatePodVastUrl(baseUrl, breakDurationMs) {
8936
+ var podParams = this.adConfig.resolvePodParams(breakDurationMs);
8937
+ return this.adConfig.generatePodVastUrl(baseUrl, podParams, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8938
+ }
8939
+ },
8364
8940
  {
8365
8941
  key: "load",
8366
8942
  value: // ───────────────────────────────────────────────────────────────
@@ -8749,7 +9325,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8749
9325
  }
8750
9326
  this.adConfig.beginNewAdPod();
8751
9327
  this.adConfig.podAssignedByPrefetch = true;
8752
- var generatedUrls = this.generateVastUrls(baseVastUrl, 1);
9328
+ var optimizedPods = !!this.config.optimizedPods;
9329
+ var breakDurationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : void 0;
9330
+ var generatedUrls = optimizedPods ? [
9331
+ this.generatePodVastUrl(baseVastUrl, breakDurationMs)
9332
+ ] : this.generateVastUrls(baseVastUrl, 1);
8753
9333
  this.adBreak.pendingAdBreak = _object_spread_props(_object_spread({
8754
9334
  marker: marker
8755
9335
  }, fragmentSn !== void 0 ? {
@@ -8771,7 +9351,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8771
9351
  }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8772
9352
  if (this.debug) {
8773
9353
  console.log("[PREFETCH] Starting ad prefetch for upcoming ad break");
8774
- console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
9354
+ console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " ").concat(optimizedPods ? "pod" : "VAST", " URL(s)"));
9355
+ }
9356
+ if (optimizedPods) {
9357
+ if (this.debug) {
9358
+ console.log("[PREFETCH] Optimized pods enabled - skipping single-ad preload pool");
9359
+ }
9360
+ return;
8775
9361
  }
8776
9362
  this.preloadPool.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
8777
9363
  }