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
@@ -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
  }
@@ -2032,6 +2304,16 @@ function normalizeBoolean(value) {
2032
2304
  }
2033
2305
  // src/utils/tracking.ts
2034
2306
  var cachedBrowserId = null;
2307
+ function buildAnalyticsContext(config) {
2308
+ var context = {};
2309
+ if (config.debugAdTiming) {
2310
+ context.debugAdTiming = true;
2311
+ }
2312
+ if (config.isLiveStream !== void 0) {
2313
+ context.inputStreamType = config.isLiveStream ? "live" : "vod";
2314
+ }
2315
+ return context;
2316
+ }
2035
2317
  function getClientInfo() {
2036
2318
  var _screen, _screen1, _screen2, _screen3, _screen_orientation, _screen4, _screen5, _window, _window1, _window_screen_orientation, _window_screen, _navigator_languages;
2037
2319
  var ua = navigator.userAgent;
@@ -2290,19 +2572,26 @@ function canPublish(licenseKey) {
2290
2572
  }
2291
2573
  function buildPlayerMetricEvent() {
2292
2574
  return _async_to_generator(function() {
2293
- var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
2575
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, debugAdTiming, playerId, captureAt;
2294
2576
  var _arguments = arguments;
2295
2577
  return _ts_generator(this, function(_state) {
2296
2578
  switch(_state.label){
2297
2579
  case 0:
2298
2580
  context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2299
2581
  clientInfo = getClientInfo();
2582
+ debugAdTiming = !!context.debugAdTiming;
2583
+ if (debugAdTiming) {
2584
+ console.log("[StormcloudVideoPlayer] clientInfo: ", JSON.stringify(clientInfo, null, 2));
2585
+ }
2300
2586
  return [
2301
2587
  4,
2302
2588
  getBrowserID(clientInfo)
2303
2589
  ];
2304
2590
  case 1:
2305
2591
  playerId = _state.sent();
2592
+ if (debugAdTiming) {
2593
+ console.log("[StormcloudVideoPlayer] playerId: ", playerId);
2594
+ }
2306
2595
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
2307
2596
  return [
2308
2597
  2,
@@ -3144,7 +3433,20 @@ function applyVastMacros(baseUrl, ctx) {
3144
3433
  if (ctx.pod != null) {
3145
3434
  params.set("pod", String(ctx.pod));
3146
3435
  }
3147
- if (ctx.adPosition != null) {
3436
+ var optimizedPodMode = ctx.podMaxAds != null || ctx.podMinDurationMs != null || ctx.podMaxDurationMs != null;
3437
+ if (optimizedPodMode) {
3438
+ params.delete("ppos");
3439
+ params.delete("lip");
3440
+ if (ctx.podMinDurationMs != null) {
3441
+ params.set("pmnd", String(Math.max(0, Math.round(ctx.podMinDurationMs))));
3442
+ }
3443
+ if (ctx.podMaxDurationMs != null) {
3444
+ params.set("pmxd", String(Math.max(0, Math.round(ctx.podMaxDurationMs))));
3445
+ }
3446
+ if (ctx.podMaxAds != null) {
3447
+ params.set("pmad", String(Math.max(1, Math.round(ctx.podMaxAds))));
3448
+ }
3449
+ } else if (ctx.adPosition != null) {
3148
3450
  params.set("ppos", String(ctx.adPosition));
3149
3451
  }
3150
3452
  var requestUrl = ctx.contentUrl || ctx.pageUrl;
@@ -4486,6 +4788,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
4486
4788
  this.podAssignedByPrefetch = false;
4487
4789
  this.adRequestPositionInBreak = 0;
4488
4790
  this.lastHeartbeatTime = 0;
4791
+ this.defaultPodMaxAds = 6;
4489
4792
  this.config = config;
4490
4793
  this.video = video;
4491
4794
  }
@@ -4941,12 +5244,58 @@ var AdConfigManager = /*#__PURE__*/ function() {
4941
5244
  return urls;
4942
5245
  }
4943
5246
  },
5247
+ {
5248
+ key: "resolvePodParams",
5249
+ value: function resolvePodParams(breakDurationMs) {
5250
+ var maxDurationMs = breakDurationMs != null && breakDurationMs > 0 ? breakDurationMs : this.config.podMaxDurationMs;
5251
+ var maxAds = this.config.podMaxAds && this.config.podMaxAds > 0 ? this.config.podMaxAds : this.apiNumberAds && this.apiNumberAds > 0 ? this.apiNumberAds : this.defaultPodMaxAds;
5252
+ var minDurationMs = this.config.podExactDuration && maxDurationMs != null ? maxDurationMs : 0;
5253
+ return {
5254
+ maxAds: maxAds,
5255
+ minDurationMs: minDurationMs,
5256
+ maxDurationMs: maxDurationMs
5257
+ };
5258
+ }
5259
+ },
5260
+ {
5261
+ key: "generatePodVastUrl",
5262
+ value: function generatePodVastUrl(baseUrl, podParams, adPlayer, palNonce, inAdBreak) {
5263
+ var _podParams_maxDurationMs;
5264
+ var adWillPlayMuted = inAdBreak ? adPlayer.getOriginalMutedState() : this.video.muted;
5265
+ var envSignals = resolveVastEnvironmentSignals(!!this.config.ctvAdRequest);
5266
+ var urlWithMacros = applyVastMacros(baseUrl, {
5267
+ correlator: generateCorrelator(),
5268
+ streamCorrelator: this.streamCorrelator,
5269
+ pod: this.podCounter > 0 ? this.podCounter : void 0,
5270
+ podMaxAds: podParams.maxAds,
5271
+ podMinDurationMs: podParams.minDurationMs,
5272
+ podMaxDurationMs: podParams.maxDurationMs,
5273
+ isCtv: envSignals.isCtv,
5274
+ contentUrl: envSignals.contentUrl,
5275
+ pageUrl: !envSignals.isCtv && typeof window !== "undefined" ? window.location.href : void 0,
5276
+ adWillPlayMuted: adWillPlayMuted,
5277
+ adWillAutoPlay: !!this.config.autoplay,
5278
+ appId: envSignals.appId,
5279
+ appName: envSignals.appName,
5280
+ sessionId: envSignals.sessionId,
5281
+ deviceId: envSignals.deviceId,
5282
+ deviceIdType: envSignals.deviceIdType,
5283
+ limitAdTracking: envSignals.limitAdTracking,
5284
+ deviceTypeHint: envSignals.deviceTypeHint,
5285
+ adTest: this.config.adTest,
5286
+ consent: this.consentSignals
5287
+ });
5288
+ var finalUrl = palNonce.injectNonce(urlWithMacros);
5289
+ 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);
5290
+ return finalUrl;
5291
+ }
5292
+ },
4944
5293
  {
4945
5294
  // --- Tracking ---
4946
5295
  key: "initializeTracking",
4947
5296
  value: function initializeTracking() {
4948
5297
  var _this = this;
4949
- sendInitialTracking(this.config.licenseKey).then(function() {
5298
+ sendInitialTracking(this.config.licenseKey, buildAnalyticsContext(this.config)).then(function() {
4950
5299
  _this.heartbeatInterval = window.setInterval(function() {
4951
5300
  _this.sendHeartbeatIfNeeded();
4952
5301
  }, 5e3);
@@ -4967,7 +5316,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
4967
5316
  var now = Date.now();
4968
5317
  if (!this.lastHeartbeatTime || now - this.lastHeartbeatTime > 3e4) {
4969
5318
  this.lastHeartbeatTime = now;
4970
- sendHeartbeat(this.config.licenseKey).catch(function(error) {
5319
+ sendHeartbeat(this.config.licenseKey, buildAnalyticsContext(this.config)).catch(function(error) {
4971
5320
  if (_this.debug) {
4972
5321
  console.warn("[StormcloudVideoPlayer] Failed to send heartbeat:", error);
4973
5322
  }
@@ -5246,6 +5595,13 @@ var AdTimingService = /*#__PURE__*/ function() {
5246
5595
  this.lastAdRequestTime = Date.now();
5247
5596
  }
5248
5597
  },
5598
+ {
5599
+ key: "notePodAdStarted",
5600
+ value: function notePodAdStarted() {
5601
+ this.currentAdIndex++;
5602
+ this.totalAdsInBreak = this.currentAdIndex;
5603
+ }
5604
+ },
5249
5605
  {
5250
5606
  key: "getRemainingAdMs",
5251
5607
  value: function getRemainingAdMs() {
@@ -6748,19 +7104,38 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6748
7104
  }
6749
7105
  },
6750
7106
  {
6751
- // ───────────────────────────────────────────────────────────────
7107
+ key: "analyticsContext",
7108
+ get: function get() {
7109
+ return buildAnalyticsContext(this.host.config);
7110
+ }
7111
+ },
7112
+ {
7113
+ key: "optimizedPodsEnabled",
7114
+ get: // ───────────────────────────────────────────────────────────────
6752
7115
  // IMA event listeners
6753
7116
  // ───────────────────────────────────────────────────────────────
7117
+ function get() {
7118
+ return !!this.host.config.optimizedPods;
7119
+ }
7120
+ },
7121
+ {
6754
7122
  key: "attachImaEventListeners",
6755
7123
  value: function attachImaEventListeners() {
6756
7124
  var _this = this;
6757
7125
  var adPlayer = this.host.getAdPlayer();
7126
+ adPlayer.on("pod_ad_started", function(payload) {
7127
+ _this.timing.notePodAdStarted();
7128
+ if (_this.debug) {
7129
+ var _ref;
7130
+ 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 : "?"));
7131
+ }
7132
+ });
6758
7133
  adPlayer.on("all_ads_completed", function() {
6759
7134
  sendAdImpressionTracking(_this.host.config.licenseKey, {
6760
7135
  source: "hls",
6761
7136
  adIndex: _this.timing.currentAdIndex,
6762
7137
  timestamp: /* @__PURE__ */ new Date().toISOString()
6763
- }).catch(function() {});
7138
+ }, _this.analyticsContext).catch(function() {});
6764
7139
  _this.timing.recordPlayedAdDuration(_this.host.getAdPlayer());
6765
7140
  var remaining = _this.timing.getRemainingAdMs();
6766
7141
  _this.timing.consecutiveFailures = 0;
@@ -6822,7 +7197,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6822
7197
  source: "hls",
6823
7198
  adIndex: _this.timing.currentAdIndex,
6824
7199
  timestamp: /* @__PURE__ */ new Date().toISOString()
6825
- }).catch(function() {});
7200
+ }, _this.analyticsContext).catch(function() {});
6826
7201
  if (!_this.host.video.muted) {
6827
7202
  _this.host.video.muted = true;
6828
7203
  if (_this.debug) {
@@ -6912,7 +7287,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6912
7287
  // ───────────────────────────────────────────────────────────────
6913
7288
  function handleAdStart(_marker) {
6914
7289
  return _async_to_generator(function() {
6915
- var scheduled, tags, baseVastUrl, adBreakDurationMs, mode, currentMuted, currentVolume, firstAdUrl, usePreloadedAd, preloadedController, preloaded, firstAdUrlArray, adPlayer, preservedMuted, preservedVolume, adVolume, adPlayer1, adVolume1, error, fallbackPreloaded, ap, adVolume2, fallbackError;
7290
+ 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;
6916
7291
  return _ts_generator(this, function(_state) {
6917
7292
  switch(_state.label){
6918
7293
  case 0:
@@ -6959,6 +7334,26 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6959
7334
  if (this.timing.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
6960
7335
  this.timing.expectedAdBreakDurationMs = adBreakDurationMs;
6961
7336
  }
7337
+ if (!this.optimizedPodsEnabled) return [
7338
+ 3,
7339
+ 2
7340
+ ];
7341
+ return [
7342
+ 4,
7343
+ this.startOptimizedPod(baseVastUrl, adBreakDurationMs, currentMuted, currentVolume)
7344
+ ];
7345
+ case 1:
7346
+ podPlayed = _state.sent();
7347
+ if (podPlayed) {
7348
+ return [
7349
+ 2
7350
+ ];
7351
+ }
7352
+ if (this.debug) {
7353
+ console.log("[POD] Optimized pod unavailable, falling back to sequential single-ad path");
7354
+ }
7355
+ _state.label = 2;
7356
+ case 2:
6962
7357
  usePreloadedAd = false;
6963
7358
  preloaded = this.preloadPool.getPreloadedAd();
6964
7359
  if (preloaded) {
@@ -6969,7 +7364,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6969
7364
  source: "hls",
6970
7365
  vastUrl: firstAdUrl,
6971
7366
  timestamp: /* @__PURE__ */ new Date().toISOString()
6972
- }).catch(function() {});
7367
+ }, this.analyticsContext).catch(function() {});
6973
7368
  if (this.debug) {
6974
7369
  console.log("[CONTINUOUS-FETCH] Using preloaded ad from pool (preloaded in advance, ready immediately!)");
6975
7370
  }
@@ -7000,21 +7395,21 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7000
7395
  if (this.debug) {
7001
7396
  console.log("[CONTINUOUS-FETCH] First VAST URL:", firstAdUrl);
7002
7397
  }
7003
- _state.label = 1;
7004
- case 1:
7398
+ _state.label = 3;
7399
+ case 3:
7005
7400
  _state.trys.push([
7006
- 1,
7007
- 12,
7401
+ 3,
7402
+ 14,
7008
7403
  ,
7009
- 20
7404
+ 22
7010
7405
  ]);
7011
7406
  if (!(usePreloadedAd && preloadedController)) return [
7012
7407
  3,
7013
- 5
7408
+ 7
7014
7409
  ];
7015
7410
  if (!!this.timing.ensureLoadedAdFitsBudget(preloadedController)) return [
7016
7411
  3,
7017
- 3
7412
+ 5
7018
7413
  ];
7019
7414
  this.timing.rejectLoadedAdForDuration(preloadedController);
7020
7415
  this.startContinuousFetching(baseVastUrl);
@@ -7022,12 +7417,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7022
7417
  4,
7023
7418
  this.tryNextAvailableAdWithRateLimit()
7024
7419
  ];
7025
- case 2:
7420
+ case 4:
7026
7421
  _state.sent();
7027
7422
  return [
7028
7423
  2
7029
7424
  ];
7030
- case 3:
7425
+ case 5:
7031
7426
  adPlayer = this.host.getAdPlayer();
7032
7427
  preservedMuted = adPlayer.getOriginalMutedState();
7033
7428
  preservedVolume = adPlayer.getOriginalVolume();
@@ -7052,7 +7447,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7052
7447
  4,
7053
7448
  preloadedController.play()
7054
7449
  ];
7055
- case 4:
7450
+ case 6:
7056
7451
  _state.sent();
7057
7452
  this.timing.markAdStarted();
7058
7453
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7062,34 +7457,34 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7062
7457
  preloadedController.setAdVolume(adVolume);
7063
7458
  return [
7064
7459
  3,
7065
- 11
7460
+ 13
7066
7461
  ];
7067
- case 5:
7462
+ case 7:
7068
7463
  adPlayer1 = this.host.getAdPlayer();
7069
7464
  return [
7070
7465
  4,
7071
7466
  this.timing.enforceGlobalRateLimit()
7072
7467
  ];
7073
- case 6:
7468
+ case 8:
7074
7469
  _state.sent();
7075
7470
  this.timing.lastAdRequestTime = Date.now();
7076
7471
  return [
7077
7472
  4,
7078
7473
  adPlayer1.requestAds(firstAdUrl)
7079
7474
  ];
7080
- case 7:
7475
+ case 9:
7081
7476
  _state.sent();
7082
7477
  sendAdLoadedTracking(this.host.config.licenseKey, {
7083
7478
  source: "hls",
7084
7479
  vastUrl: firstAdUrl,
7085
7480
  timestamp: /* @__PURE__ */ new Date().toISOString()
7086
- }).catch(function() {});
7481
+ }, this.analyticsContext).catch(function() {});
7087
7482
  if (this.debug) {
7088
7483
  console.log("[CONTINUOUS-FETCH] First ad request successful, starting playback");
7089
7484
  }
7090
7485
  if (!!this.timing.ensureLoadedAdFitsBudget(adPlayer1)) return [
7091
7486
  3,
7092
- 9
7487
+ 11
7093
7488
  ];
7094
7489
  this.timing.rejectLoadedAdForDuration(adPlayer1);
7095
7490
  this.recreateAdController();
@@ -7098,12 +7493,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7098
7493
  4,
7099
7494
  this.tryNextAvailableAdWithRateLimit()
7100
7495
  ];
7101
- case 8:
7496
+ case 10:
7102
7497
  _state.sent();
7103
7498
  return [
7104
7499
  2
7105
7500
  ];
7106
- case 9:
7501
+ case 11:
7107
7502
  this.timing.consecutiveFailures = 0;
7108
7503
  this.startContinuousFetching(baseVastUrl);
7109
7504
  if (!this.preloadPool.active) {
@@ -7113,7 +7508,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7113
7508
  4,
7114
7509
  adPlayer1.play()
7115
7510
  ];
7116
- case 10:
7511
+ case 12:
7117
7512
  _state.sent();
7118
7513
  this.timing.markAdStarted();
7119
7514
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7121,40 +7516,40 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7121
7516
  }
7122
7517
  adVolume1 = currentMuted ? 0 : currentVolume;
7123
7518
  adPlayer1.setAdVolume(adVolume1);
7124
- _state.label = 11;
7125
- case 11:
7519
+ _state.label = 13;
7520
+ case 13:
7126
7521
  return [
7127
7522
  3,
7128
- 20
7523
+ 22
7129
7524
  ];
7130
- case 12:
7525
+ case 14:
7131
7526
  error = _state.sent();
7132
7527
  if (this.debug) {
7133
7528
  console.warn("[CONTINUOUS-FETCH] First ad request failed:", error);
7134
7529
  }
7135
7530
  if (!!usePreloadedAd) return [
7136
7531
  3,
7137
- 18
7532
+ 20
7138
7533
  ];
7139
7534
  fallbackPreloaded = this.preloadPool.getPreloadedAd();
7140
7535
  if (!fallbackPreloaded) return [
7141
7536
  3,
7142
- 18
7537
+ 20
7143
7538
  ];
7144
7539
  if (this.debug) {
7145
7540
  console.log("[CONTINUOUS-FETCH] First ad failed, using preloaded fallback");
7146
7541
  }
7147
- _state.label = 13;
7148
- case 13:
7542
+ _state.label = 15;
7543
+ case 15:
7149
7544
  _state.trys.push([
7150
- 13,
7151
- 17,
7545
+ 15,
7546
+ 19,
7152
7547
  ,
7153
- 18
7548
+ 20
7154
7549
  ]);
7155
7550
  if (!!this.timing.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
7156
7551
  3,
7157
- 15
7552
+ 17
7158
7553
  ];
7159
7554
  this.timing.rejectLoadedAdForDuration(fallbackPreloaded.adController);
7160
7555
  this.startContinuousFetching(baseVastUrl);
@@ -7162,12 +7557,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7162
7557
  4,
7163
7558
  this.tryNextAvailableAdWithRateLimit()
7164
7559
  ];
7165
- case 14:
7560
+ case 16:
7166
7561
  _state.sent();
7167
7562
  return [
7168
7563
  2
7169
7564
  ];
7170
- case 15:
7565
+ case 17:
7171
7566
  this.swapToPreloadedAdPlayer(fallbackPreloaded.adController);
7172
7567
  this.timing.consecutiveFailures = 0;
7173
7568
  this.startContinuousFetching(baseVastUrl);
@@ -7180,7 +7575,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7180
7575
  4,
7181
7576
  ap.play()
7182
7577
  ];
7183
- case 16:
7578
+ case 18:
7184
7579
  _state.sent();
7185
7580
  this.timing.markAdStarted();
7186
7581
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7191,16 +7586,16 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7191
7586
  return [
7192
7587
  2
7193
7588
  ];
7194
- case 17:
7589
+ case 19:
7195
7590
  fallbackError = _state.sent();
7196
7591
  if (this.debug) {
7197
7592
  console.warn("[CONTINUOUS-FETCH] Preloaded fallback also failed:", fallbackError);
7198
7593
  }
7199
7594
  return [
7200
7595
  3,
7201
- 18
7596
+ 20
7202
7597
  ];
7203
- case 18:
7598
+ case 20:
7204
7599
  if (this.timing.isTemporaryAdError(error)) {
7205
7600
  this.timing.temporaryFailureUrls.set(firstAdUrl, Date.now());
7206
7601
  if (this.debug) {
@@ -7218,13 +7613,137 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7218
7613
  4,
7219
7614
  this.tryNextAvailableAdWithRateLimit()
7220
7615
  ];
7221
- case 19:
7616
+ case 21:
7222
7617
  _state.sent();
7223
7618
  return [
7224
7619
  3,
7225
- 20
7620
+ 22
7226
7621
  ];
7227
- case 20:
7622
+ case 22:
7623
+ return [
7624
+ 2
7625
+ ];
7626
+ }
7627
+ });
7628
+ }).call(this);
7629
+ }
7630
+ },
7631
+ {
7632
+ key: "startOptimizedPod",
7633
+ value: // ───────────────────────────────────────────────────────────────
7634
+ // Optimized pod (single multi-ad request)
7635
+ // ───────────────────────────────────────────────────────────────
7636
+ function startOptimizedPod(baseVastUrl, breakDurationMs, currentMuted, currentVolume) {
7637
+ return _async_to_generator(function() {
7638
+ var podUrl, adPlayer, requestToken, _ref, _adPlayer_getPodAdCount, podCount, error;
7639
+ return _ts_generator(this, function(_state) {
7640
+ switch(_state.label){
7641
+ case 0:
7642
+ if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {
7643
+ podUrl = this.pendingAdBreak.vastUrls[0];
7644
+ this.clearPendingAdBreak();
7645
+ } else {
7646
+ podUrl = this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);
7647
+ }
7648
+ if (!podUrl) {
7649
+ return [
7650
+ 2,
7651
+ false
7652
+ ];
7653
+ }
7654
+ this.continuousFetchingActive = false;
7655
+ this.recreateAdController();
7656
+ adPlayer = this.host.getAdPlayer();
7657
+ requestToken = ++this.timing.adRequestTokenCounter;
7658
+ this.timing.activeAdRequestToken = requestToken;
7659
+ this.timing.startAdRequestWatchdog(requestToken);
7660
+ _state.label = 1;
7661
+ case 1:
7662
+ _state.trys.push([
7663
+ 1,
7664
+ 5,
7665
+ ,
7666
+ 6
7667
+ ]);
7668
+ return [
7669
+ 4,
7670
+ this.timing.enforceGlobalRateLimit()
7671
+ ];
7672
+ case 2:
7673
+ _state.sent();
7674
+ this.timing.lastAdRequestTime = Date.now();
7675
+ return [
7676
+ 4,
7677
+ adPlayer.requestAds(podUrl)
7678
+ ];
7679
+ case 3:
7680
+ _state.sent();
7681
+ this.timing.clearAdRequestWatchdog();
7682
+ if (this.timing.activeAdRequestToken !== requestToken) {
7683
+ return [
7684
+ 2,
7685
+ true
7686
+ ];
7687
+ }
7688
+ podCount = (_ref = (_adPlayer_getPodAdCount = adPlayer.getPodAdCount) === null || _adPlayer_getPodAdCount === void 0 ? void 0 : _adPlayer_getPodAdCount.call(adPlayer)) !== null && _ref !== void 0 ? _ref : 1;
7689
+ if (podCount < 1) {
7690
+ return [
7691
+ 2,
7692
+ false
7693
+ ];
7694
+ }
7695
+ if (this.debug) {
7696
+ 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"));
7697
+ }
7698
+ sendAdLoadedTracking(this.host.config.licenseKey, {
7699
+ source: "hls",
7700
+ vastUrl: podUrl,
7701
+ timestamp: /* @__PURE__ */ new Date().toISOString()
7702
+ }, this.analyticsContext).catch(function() {});
7703
+ this.timing.startAdFailsafeTimer(requestToken);
7704
+ return [
7705
+ 4,
7706
+ adPlayer.play()
7707
+ ];
7708
+ case 4:
7709
+ _state.sent();
7710
+ this.timing.markAdStarted();
7711
+ if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
7712
+ this.timing.scheduleAdStopAtBreakBoundary();
7713
+ }
7714
+ adPlayer.setAdVolume(currentMuted ? 0 : currentVolume);
7715
+ this.timing.consecutiveFailures = 0;
7716
+ if (podCount === 1) {
7717
+ this.continuousFetchingActive = true;
7718
+ this.startContinuousFetching(baseVastUrl);
7719
+ if (!this.preloadPool.active) {
7720
+ this.preloadPool.startPreloadPool(baseVastUrl, []);
7721
+ }
7722
+ }
7723
+ return [
7724
+ 2,
7725
+ true
7726
+ ];
7727
+ case 5:
7728
+ error = _state.sent();
7729
+ this.timing.clearAdRequestWatchdog();
7730
+ this.timing.clearAdFailsafeTimer();
7731
+ if (this.timing.activeAdRequestToken === requestToken) {
7732
+ this.timing.activeAdRequestToken = null;
7733
+ }
7734
+ if (this.debug) {
7735
+ console.warn("[POD] Optimized pod request/play failed:", error);
7736
+ }
7737
+ if (this.timing.isTemporaryAdError(error)) {
7738
+ this.timing.temporaryFailureUrls.set(podUrl, Date.now());
7739
+ }
7740
+ this.continuousFetchingActive = true;
7741
+ this.recreateAdController();
7742
+ return [
7743
+ 2,
7744
+ false
7745
+ ];
7746
+ case 6:
7228
7747
  return [
7229
7748
  2
7230
7749
  ];
@@ -7547,7 +8066,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7547
8066
  source: "hls",
7548
8067
  vastUrl: preloaded.vastUrl,
7549
8068
  timestamp: /* @__PURE__ */ new Date().toISOString()
7550
- }).catch(function() {});
8069
+ }, this.analyticsContext).catch(function() {});
7551
8070
  _state.label = 1;
7552
8071
  case 1:
7553
8072
  _state.trys.push([
@@ -7780,7 +8299,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7780
8299
  source: "hls",
7781
8300
  vastUrl: vastTagUrl,
7782
8301
  timestamp: /* @__PURE__ */ new Date().toISOString()
7783
- }).catch(function() {});
8302
+ }, this.analyticsContext).catch(function() {});
7784
8303
  this.timing.clearAdRequestWatchdog();
7785
8304
  if (this.timing.activeAdRequestToken !== requestToken) return [
7786
8305
  2
@@ -8306,6 +8825,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8306
8825
  },
8307
8826
  generateVastUrls: function generateVastUrls(base, count) {
8308
8827
  return _this.generateVastUrls(base, count);
8828
+ },
8829
+ generatePodVastUrl: function generatePodVastUrl(base, breakDurationMs) {
8830
+ return _this.generatePodVastUrl(base, breakDurationMs);
8309
8831
  }
8310
8832
  }, this.timing, this.preloadPool, this.adConfig, this.cueManager, this.placeholder);
8311
8833
  }
@@ -8338,6 +8860,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8338
8860
  return this.adConfig.generateVastUrlsWithCorrelators(baseUrl, count, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8339
8861
  }
8340
8862
  },
8863
+ {
8864
+ key: "generatePodVastUrl",
8865
+ value: function generatePodVastUrl(baseUrl, breakDurationMs) {
8866
+ var podParams = this.adConfig.resolvePodParams(breakDurationMs);
8867
+ return this.adConfig.generatePodVastUrl(baseUrl, podParams, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8868
+ }
8869
+ },
8341
8870
  {
8342
8871
  key: "load",
8343
8872
  value: // ───────────────────────────────────────────────────────────────
@@ -8572,7 +9101,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8572
9101
  sendAdDetectTracking(this.config.licenseKey, _object_spread({
8573
9102
  source: "scte35",
8574
9103
  timestamp: /* @__PURE__ */ new Date().toISOString()
8575
- }, detectPayload)).catch(function() {});
9104
+ }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8576
9105
  }
8577
9106
  var hasPrefetchedAds = this.adBreak.pendingAdBreak && this.adBreak.pendingAdBreak.vastUrls.length > 0;
8578
9107
  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;
@@ -8726,7 +9255,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8726
9255
  }
8727
9256
  this.adConfig.beginNewAdPod();
8728
9257
  this.adConfig.podAssignedByPrefetch = true;
8729
- var generatedUrls = this.generateVastUrls(baseVastUrl, 1);
9258
+ var optimizedPods = !!this.config.optimizedPods;
9259
+ var breakDurationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : void 0;
9260
+ var generatedUrls = optimizedPods ? [
9261
+ this.generatePodVastUrl(baseVastUrl, breakDurationMs)
9262
+ ] : this.generateVastUrls(baseVastUrl, 1);
8730
9263
  this.adBreak.pendingAdBreak = _object_spread_props(_object_spread({
8731
9264
  marker: marker
8732
9265
  }, fragmentSn !== void 0 ? {
@@ -8745,10 +9278,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8745
9278
  sendAdDetectTracking(this.config.licenseKey, _object_spread({
8746
9279
  source: "scte35",
8747
9280
  timestamp: /* @__PURE__ */ new Date().toISOString()
8748
- }, detectPayload)).catch(function() {});
9281
+ }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8749
9282
  if (this.debug) {
8750
9283
  console.log("[PREFETCH] Starting ad prefetch for upcoming ad break");
8751
- console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
9284
+ console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " ").concat(optimizedPods ? "pod" : "VAST", " URL(s)"));
9285
+ }
9286
+ if (optimizedPods) {
9287
+ if (this.debug) {
9288
+ console.log("[PREFETCH] Optimized pods enabled - skipping single-ad preload pool");
9289
+ }
9290
+ return;
8752
9291
  }
8753
9292
  this.preloadPool.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
8754
9293
  }