stormcloud-video-player 0.8.24 → 0.8.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/stormcloud-vp.min.js +1 -1
  2. package/lib/index.cjs +669 -126
  3. package/lib/index.cjs.map +1 -1
  4. package/lib/index.d.cts +10 -1
  5. package/lib/index.d.ts +10 -1
  6. package/lib/index.js +666 -127
  7. package/lib/index.js.map +1 -1
  8. package/lib/player/AdBreakOrchestrator.cjs +228 -48
  9. package/lib/player/AdBreakOrchestrator.cjs.map +1 -1
  10. package/lib/player/AdBreakOrchestrator.d.cts +5 -1
  11. package/lib/player/AdConfigManager.cjs +81 -4
  12. package/lib/player/AdConfigManager.cjs.map +1 -1
  13. package/lib/player/AdConfigManager.d.cts +12 -1
  14. package/lib/player/AdPreloadPool.d.cts +1 -1
  15. package/lib/player/AdTimingService.cjs +7 -0
  16. package/lib/player/AdTimingService.cjs.map +1 -1
  17. package/lib/player/AdTimingService.d.cts +2 -1
  18. package/lib/player/HlsEngine.d.cts +1 -1
  19. package/lib/player/PlayerControls.d.cts +1 -1
  20. package/lib/player/Scte35CueManager.d.cts +1 -1
  21. package/lib/player/Scte35Parser.d.cts +1 -1
  22. package/lib/player/StormcloudVideoPlayer.cjs +665 -126
  23. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  24. package/lib/player/StormcloudVideoPlayer.d.cts +2 -1
  25. package/lib/player/playerTypes.d.cts +1 -1
  26. package/lib/players/HlsPlayer.cjs +665 -126
  27. package/lib/players/HlsPlayer.cjs.map +1 -1
  28. package/lib/players/HlsPlayer.d.cts +1 -1
  29. package/lib/players/index.cjs +665 -126
  30. package/lib/players/index.cjs.map +1 -1
  31. package/lib/sdk/hlsAdPlayer.cjs +343 -71
  32. package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
  33. package/lib/sdk/hlsAdPlayer.d.cts +1 -1
  34. package/lib/{types-DNiBmPKK.d.cts → types-Xgz2_W1C.d.cts} +7 -0
  35. package/lib/ui/StormcloudVideoPlayer.cjs +665 -126
  36. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  37. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  38. package/lib/utils/tracking.cjs +22 -1
  39. package/lib/utils/tracking.cjs.map +1 -1
  40. package/lib/utils/tracking.d.cts +3 -2
  41. package/lib/utils/vastMacros.cjs +14 -1
  42. package/lib/utils/vastMacros.cjs.map +1 -1
  43. package/lib/utils/vastMacros.d.cts +3 -0
  44. package/package.json +1 -1
package/lib/index.cjs CHANGED
@@ -464,6 +464,9 @@ __export(index_exports, {
464
464
  applyMQTTConfig: function applyMQTTConfig1() {
465
465
  return applyMQTTConfig;
466
466
  },
467
+ buildAnalyticsContext: function buildAnalyticsContext1() {
468
+ return buildAnalyticsContext;
469
+ },
467
470
  buildMQTTBrokerUrl: function buildMQTTBrokerUrl1() {
468
471
  return buildMQTTBrokerUrl;
469
472
  },
@@ -596,6 +599,8 @@ function createHlsAdPlayer(contentVideo, options) {
596
599
  var adHls;
597
600
  var adContainerEl;
598
601
  var currentAd;
602
+ var podAds = [];
603
+ var podIndex = 0;
599
604
  var sessionId;
600
605
  var preloadedAds = /* @__PURE__ */ new Map();
601
606
  var preloadingAds = /* @__PURE__ */ new Map();
@@ -760,14 +765,14 @@ function createHlsAdPlayer(contentVideo, options) {
760
765
  error: []
761
766
  };
762
767
  }
763
- function extractTrackingUrls(xmlDoc) {
768
+ function extractTrackingUrls(scope) {
764
769
  var trackingUrls = createEmptyTrackingUrls();
765
- xmlDoc.querySelectorAll("Impression").forEach(function(el) {
770
+ scope.querySelectorAll("Impression").forEach(function(el) {
766
771
  var _el_textContent;
767
772
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
768
773
  if (url) trackingUrls.impression.push(url);
769
774
  });
770
- xmlDoc.querySelectorAll("Tracking").forEach(function(el) {
775
+ scope.querySelectorAll("Tracking").forEach(function(el) {
771
776
  var _el_textContent;
772
777
  var event = el.getAttribute("event");
773
778
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
@@ -778,7 +783,7 @@ function createHlsAdPlayer(contentVideo, options) {
778
783
  }
779
784
  }
780
785
  });
781
- xmlDoc.querySelectorAll("Error").forEach(function(el) {
786
+ scope.querySelectorAll("Error").forEach(function(el) {
782
787
  var _el_textContent;
783
788
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
784
789
  if (url) trackingUrls.error.push(url);
@@ -888,6 +893,75 @@ function createHlsAdPlayer(contentVideo, options) {
888
893
  };
889
894
  }
890
895
  }
896
+ function parseAdElement(adElement) {
897
+ try {
898
+ var _adElement_querySelector, _adElement_querySelector_textContent, _adElement_querySelector1, _adElement_querySelector_textContent1, _adElement_querySelector2, _adElement_querySelector3;
899
+ var adId = adElement.getAttribute("id") || "unknown";
900
+ var title = ((_adElement_querySelector = adElement.querySelector("AdTitle")) === null || _adElement_querySelector === void 0 ? void 0 : _adElement_querySelector.textContent) || "Ad";
901
+ 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();
902
+ 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();
903
+ if (wrapperUri) {
904
+ return {
905
+ type: "wrapper",
906
+ vastAdTagUri: wrapperUri,
907
+ trackingUrls: extractTrackingUrls(adElement),
908
+ clickThrough: clickThrough
909
+ };
910
+ }
911
+ var isNoAdAvailable = adId === "empty" || title.toLowerCase().includes("no ad available") || title.toLowerCase() === "no ad available";
912
+ var durationText = ((_adElement_querySelector3 = adElement.querySelector("Duration")) === null || _adElement_querySelector3 === void 0 ? void 0 : _adElement_querySelector3.textContent) || "00:00:30";
913
+ var durationParts = durationText.split(":");
914
+ var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
915
+ var mediaFileElements = adElement.querySelectorAll("MediaFile");
916
+ var mediaFiles = [];
917
+ mediaFileElements.forEach(function(mf) {
918
+ var _mf_textContent;
919
+ var type = mf.getAttribute("type") || "";
920
+ var url = ((_mf_textContent = mf.textContent) === null || _mf_textContent === void 0 ? void 0 : _mf_textContent.trim()) || "";
921
+ var width = mf.getAttribute("width") || "";
922
+ var height = mf.getAttribute("height") || "";
923
+ if (!url) return;
924
+ var bitrateAttr = mf.getAttribute("bitrate");
925
+ var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
926
+ var mediaFile = {
927
+ url: url,
928
+ type: type,
929
+ width: parseInt(width || "1920", 10),
930
+ height: parseInt(height || "1080", 10),
931
+ bitrate: bitrateValue && bitrateValue > 0 ? bitrateValue : void 0
932
+ };
933
+ if (isHlsMediaFile(mediaFile) || isProgressiveMediaFile(mediaFile)) {
934
+ mediaFiles.push(mediaFile);
935
+ }
936
+ });
937
+ if (mediaFiles.length === 0) {
938
+ if (isNoAdAvailable) {
939
+ console.warn("[HlsAdPlayer] Pod <Ad> indicates no ad available");
940
+ } else {
941
+ console.warn("[HlsAdPlayer] No supported media files in pod <Ad>");
942
+ }
943
+ return {
944
+ type: "empty"
945
+ };
946
+ }
947
+ return {
948
+ type: "inline",
949
+ ad: {
950
+ id: adId,
951
+ title: title,
952
+ duration: duration,
953
+ mediaFiles: mediaFiles,
954
+ trackingUrls: extractTrackingUrls(adElement),
955
+ clickThrough: clickThrough
956
+ }
957
+ };
958
+ } catch (error) {
959
+ console.error("[HlsAdPlayer] Error parsing pod <Ad> element:", error);
960
+ return {
961
+ type: "empty"
962
+ };
963
+ }
964
+ }
891
965
  function fetchVastXml(vastTagUrl) {
892
966
  return _async_to_generator(function() {
893
967
  var response;
@@ -964,6 +1038,146 @@ function createHlsAdPlayer(contentVideo, options) {
964
1038
  });
965
1039
  }).apply(this, arguments);
966
1040
  }
1041
+ function fetchAndParsePod(vastTagUrl) {
1042
+ return _async_to_generator(function() {
1043
+ var vastXml, parser, xmlDoc, adElements, ads, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, adElement, parsed, resolved, error, err;
1044
+ return _ts_generator(this, function(_state) {
1045
+ switch(_state.label){
1046
+ case 0:
1047
+ return [
1048
+ 4,
1049
+ fetchVastXml(vastTagUrl)
1050
+ ];
1051
+ case 1:
1052
+ vastXml = _state.sent();
1053
+ console.log("[HlsAdPlayer] Pod VAST XML received");
1054
+ parser = new DOMParser();
1055
+ xmlDoc = parser.parseFromString(vastXml, "text/xml");
1056
+ if (xmlDoc.querySelector("parsererror")) {
1057
+ console.error("[HlsAdPlayer] Pod VAST XML parsing error (malformed)");
1058
+ return [
1059
+ 2,
1060
+ []
1061
+ ];
1062
+ }
1063
+ adElements = Array.from(xmlDoc.querySelectorAll("Ad"));
1064
+ if (adElements.length === 0) {
1065
+ console.warn("[HlsAdPlayer] No <Ad> elements found in pod VAST response");
1066
+ return [
1067
+ 2,
1068
+ []
1069
+ ];
1070
+ }
1071
+ adElements.sort(function(a, b) {
1072
+ var aSeq = parseInt(a.getAttribute("sequence") || "", 10);
1073
+ var bSeq = parseInt(b.getAttribute("sequence") || "", 10);
1074
+ var aVal = Number.isFinite(aSeq) ? aSeq : Number.MAX_SAFE_INTEGER;
1075
+ var bVal = Number.isFinite(bSeq) ? bSeq : Number.MAX_SAFE_INTEGER;
1076
+ return aVal - bVal;
1077
+ });
1078
+ ads = [];
1079
+ _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1080
+ _state.label = 2;
1081
+ case 2:
1082
+ _state.trys.push([
1083
+ 2,
1084
+ 10,
1085
+ 11,
1086
+ 12
1087
+ ]);
1088
+ _iterator = adElements[Symbol.iterator]();
1089
+ _state.label = 3;
1090
+ case 3:
1091
+ if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [
1092
+ 3,
1093
+ 9
1094
+ ];
1095
+ adElement = _step.value;
1096
+ parsed = parseAdElement(adElement);
1097
+ if (!(parsed.type === "inline")) return [
1098
+ 3,
1099
+ 4
1100
+ ];
1101
+ ads.push(parsed.ad);
1102
+ return [
1103
+ 3,
1104
+ 8
1105
+ ];
1106
+ case 4:
1107
+ if (!(parsed.type === "wrapper")) return [
1108
+ 3,
1109
+ 8
1110
+ ];
1111
+ _state.label = 5;
1112
+ case 5:
1113
+ _state.trys.push([
1114
+ 5,
1115
+ 7,
1116
+ ,
1117
+ 8
1118
+ ]);
1119
+ return [
1120
+ 4,
1121
+ fetchAndParseVastAd(parsed.vastAdTagUri, 1, parsed.trackingUrls)
1122
+ ];
1123
+ case 6:
1124
+ resolved = _state.sent();
1125
+ if (resolved) {
1126
+ ads.push(resolved);
1127
+ }
1128
+ return [
1129
+ 3,
1130
+ 8
1131
+ ];
1132
+ case 7:
1133
+ error = _state.sent();
1134
+ console.warn("[HlsAdPlayer] Failed to resolve wrapper <Ad> in pod:", error);
1135
+ return [
1136
+ 3,
1137
+ 8
1138
+ ];
1139
+ case 8:
1140
+ _iteratorNormalCompletion = true;
1141
+ return [
1142
+ 3,
1143
+ 3
1144
+ ];
1145
+ case 9:
1146
+ return [
1147
+ 3,
1148
+ 12
1149
+ ];
1150
+ case 10:
1151
+ err = _state.sent();
1152
+ _didIteratorError = true;
1153
+ _iteratorError = err;
1154
+ return [
1155
+ 3,
1156
+ 12
1157
+ ];
1158
+ case 11:
1159
+ try {
1160
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1161
+ _iterator.return();
1162
+ }
1163
+ } finally{
1164
+ if (_didIteratorError) {
1165
+ throw _iteratorError;
1166
+ }
1167
+ }
1168
+ return [
1169
+ 7
1170
+ ];
1171
+ case 12:
1172
+ console.log("[HlsAdPlayer] Parsed ".concat(ads.length, " ad(s) from pod VAST response"));
1173
+ return [
1174
+ 2,
1175
+ ads
1176
+ ];
1177
+ }
1178
+ });
1179
+ })();
1180
+ }
967
1181
  function createAdVideoElement() {
968
1182
  var video = document.createElement("video");
969
1183
  video.style.position = "absolute";
@@ -1008,6 +1222,9 @@ function createHlsAdPlayer(contentVideo, options) {
1008
1222
  trackingFired.complete = true;
1009
1223
  fireTrackingPixels(currentAd.trackingUrls.complete);
1010
1224
  console.log("[HlsAdPlayer] Ad completed");
1225
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1226
+ return;
1227
+ }
1011
1228
  handleAdComplete();
1012
1229
  });
1013
1230
  adVideoElement.addEventListener("error", function(e) {
@@ -1016,6 +1233,9 @@ function createHlsAdPlayer(contentVideo, options) {
1016
1233
  if (currentAd) {
1017
1234
  fireTrackingPixels(currentAd.trackingUrls.error);
1018
1235
  }
1236
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1237
+ return;
1238
+ }
1019
1239
  handleAdError();
1020
1240
  });
1021
1241
  adVideoElement.addEventListener("volumechange", function() {
@@ -1044,6 +1264,94 @@ function createHlsAdPlayer(contentVideo, options) {
1044
1264
  delete contentVideo.dataset.stormcloudAdPlaying;
1045
1265
  }
1046
1266
  }
1267
+ function resetQuartileTracking(preserveImpression) {
1268
+ trackingFired = {
1269
+ impression: preserveImpression ? trackingFired.impression : false,
1270
+ start: false,
1271
+ firstQuartile: false,
1272
+ midpoint: false,
1273
+ thirdQuartile: false,
1274
+ complete: false
1275
+ };
1276
+ }
1277
+ function loadCurrentAdMedia() {
1278
+ if (!adVideoElement || !currentAd) {
1279
+ throw new Error("No ad video element or ad to load");
1280
+ }
1281
+ var mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1282
+ if (!mediaFile) {
1283
+ throw new Error("No media file available for ad");
1284
+ }
1285
+ var isHlsAd = isHlsMediaFile(mediaFile);
1286
+ console.log("[HlsAdPlayer] Loading ".concat(isHlsAd ? "HLS" : "progressive", " ad (pod ").concat(podIndex + 1, "/").concat(podAds.length || 1, ") from: ").concat(mediaFile.url));
1287
+ if (isHlsAd && import_hls.default.isSupported()) {
1288
+ if (adHls) {
1289
+ adHls.destroy();
1290
+ }
1291
+ adHls = new import_hls.default({
1292
+ enableWorker: true,
1293
+ lowLatencyMode: false
1294
+ });
1295
+ adHls.loadSource(mediaFile.url);
1296
+ adHls.attachMedia(adVideoElement);
1297
+ adHls.on(import_hls.default.Events.MANIFEST_PARSED, function() {
1298
+ console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1299
+ adVideoElement.play().catch(function(error) {
1300
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1301
+ handleAdError();
1302
+ });
1303
+ });
1304
+ adHls.on(import_hls.default.Events.ERROR, function(_event, data) {
1305
+ console.error("[HlsAdPlayer] HLS error:", data);
1306
+ if (data.fatal) {
1307
+ handleAdError();
1308
+ }
1309
+ });
1310
+ } else if (isHlsAd && adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1311
+ adVideoElement.src = mediaFile.url;
1312
+ adVideoElement.play().catch(function(error) {
1313
+ console.error("[HlsAdPlayer] Error starting ad playback:", error);
1314
+ handleAdError();
1315
+ });
1316
+ } else if (!isHlsAd && isProgressiveMediaFile(mediaFile)) {
1317
+ if (adHls) {
1318
+ adHls.destroy();
1319
+ adHls = void 0;
1320
+ }
1321
+ adVideoElement.src = mediaFile.url;
1322
+ adVideoElement.load();
1323
+ adVideoElement.play().catch(function(error) {
1324
+ console.error("[HlsAdPlayer] Error starting progressive ad playback:", error);
1325
+ handleAdError();
1326
+ });
1327
+ } else {
1328
+ throw new Error("Unsupported ad media file type: ".concat(mediaFile.type));
1329
+ }
1330
+ }
1331
+ function advanceToNextPodAd() {
1332
+ if (podIndex >= podAds.length - 1) {
1333
+ return false;
1334
+ }
1335
+ podIndex++;
1336
+ currentAd = podAds[podIndex];
1337
+ resetQuartileTracking(false);
1338
+ if (currentAd) {
1339
+ fireTrackingPixels(currentAd.trackingUrls.impression);
1340
+ trackingFired.impression = true;
1341
+ console.log("[HlsAdPlayer] Advancing to next pod ad (".concat(podIndex + 1, "/").concat(podAds.length, "): ").concat(currentAd.title, ", duration: ").concat(currentAd.duration, "s"));
1342
+ }
1343
+ try {
1344
+ loadCurrentAdMedia();
1345
+ emit("pod_ad_started", {
1346
+ index: podIndex,
1347
+ total: podAds.length
1348
+ });
1349
+ return true;
1350
+ } catch (error) {
1351
+ console.error("[HlsAdPlayer] Failed to load next pod ad:", error);
1352
+ return false;
1353
+ }
1354
+ }
1047
1355
  function handleAdComplete() {
1048
1356
  console.log("[HlsAdPlayer] Handling ad completion");
1049
1357
  adPlaying = false;
@@ -1097,7 +1405,7 @@ function createHlsAdPlayer(contentVideo, options) {
1097
1405
  },
1098
1406
  requestAds: function requestAds(vastTagUrl) {
1099
1407
  return _async_to_generator(function() {
1100
- var ad, error;
1408
+ var ads, preloaded, error;
1101
1409
  return _ts_generator(this, function(_state) {
1102
1410
  switch(_state.label){
1103
1411
  case 0:
@@ -1118,12 +1426,18 @@ function createHlsAdPlayer(contentVideo, options) {
1118
1426
  6
1119
1427
  ]);
1120
1428
  sessionId = generateSessionId();
1429
+ ads = [];
1121
1430
  if (!preloadedAds.has(vastTagUrl)) return [
1122
1431
  3,
1123
1432
  2
1124
1433
  ];
1125
- ad = preloadedAds.get(vastTagUrl);
1434
+ preloaded = preloadedAds.get(vastTagUrl);
1126
1435
  preloadedAds.delete(vastTagUrl);
1436
+ if (preloaded) {
1437
+ ads = [
1438
+ preloaded
1439
+ ];
1440
+ }
1127
1441
  console.log("[HlsAdPlayer] Using preloaded VAST response:", vastTagUrl);
1128
1442
  return [
1129
1443
  3,
@@ -1132,13 +1446,13 @@ function createHlsAdPlayer(contentVideo, options) {
1132
1446
  case 2:
1133
1447
  return [
1134
1448
  4,
1135
- fetchAndParseVastAd(vastTagUrl)
1449
+ fetchAndParsePod(vastTagUrl)
1136
1450
  ];
1137
1451
  case 3:
1138
- ad = _state.sent();
1452
+ ads = _state.sent();
1139
1453
  _state.label = 4;
1140
1454
  case 4:
1141
- if (!ad) {
1455
+ if (!ads.length) {
1142
1456
  console.warn("[HlsAdPlayer] No ads available from VAST response");
1143
1457
  emit("ad_error");
1144
1458
  return [
@@ -1146,10 +1460,14 @@ function createHlsAdPlayer(contentVideo, options) {
1146
1460
  Promise.reject(new Error("No ads available from VAST response"))
1147
1461
  ];
1148
1462
  }
1149
- currentAd = ad;
1150
- console.log("[HlsAdPlayer] Ad parsed: ".concat(ad.title, ", duration: ").concat(ad.duration, "s"));
1151
- fireTrackingPixels(ad.trackingUrls.impression);
1152
- trackingFired.impression = true;
1463
+ podAds = ads;
1464
+ podIndex = 0;
1465
+ currentAd = ads[0];
1466
+ 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"));
1467
+ if (currentAd) {
1468
+ fireTrackingPixels(currentAd.trackingUrls.impression);
1469
+ trackingFired.impression = true;
1470
+ }
1153
1471
  return [
1154
1472
  2,
1155
1473
  Promise.resolve()
@@ -1172,7 +1490,7 @@ function createHlsAdPlayer(contentVideo, options) {
1172
1490
  },
1173
1491
  play: function play() {
1174
1492
  return _async_to_generator(function() {
1175
- var contentVolume, mediaFile, isHlsAd;
1493
+ var contentVolume;
1176
1494
  return _ts_generator(this, function(_state) {
1177
1495
  if (!currentAd) {
1178
1496
  console.warn("[HlsAdPlayer] Cannot play: No ad loaded (no ads available)");
@@ -1188,14 +1506,7 @@ function createHlsAdPlayer(contentVideo, options) {
1188
1506
  adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.appendChild(adVideoElement);
1189
1507
  setupAdEventListeners();
1190
1508
  }
1191
- trackingFired = {
1192
- impression: trackingFired.impression,
1193
- start: false,
1194
- firstQuartile: false,
1195
- midpoint: false,
1196
- thirdQuartile: false,
1197
- complete: false
1198
- };
1509
+ resetQuartileTracking(true);
1199
1510
  contentVolume = contentVideo.volume;
1200
1511
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1201
1512
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
@@ -1213,60 +1524,12 @@ function createHlsAdPlayer(contentVideo, options) {
1213
1524
  adVideoElement.muted = originalMutedState;
1214
1525
  console.log("[HlsAdPlayer] Set ad video volume to ".concat(adVideoElement.volume, ", muted: ").concat(adVideoElement.muted, ", originalMutedState: ").concat(originalMutedState, ", contentVolume: ").concat(contentVolume));
1215
1526
  }
1216
- mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1217
- if (!mediaFile) {
1218
- throw new Error("No media file available for ad");
1219
- }
1220
1527
  if (adContainerEl) {
1221
1528
  adContainerEl.style.display = "flex";
1222
1529
  adContainerEl.style.pointerEvents = "auto";
1223
1530
  }
1224
1531
  emit("content_pause");
1225
- isHlsAd = isHlsMediaFile(mediaFile);
1226
- console.log("[HlsAdPlayer] Loading ".concat(isHlsAd ? "HLS" : "progressive", " ad from: ").concat(mediaFile.url));
1227
- if (isHlsAd && import_hls.default.isSupported()) {
1228
- if (adHls) {
1229
- adHls.destroy();
1230
- }
1231
- adHls = new import_hls.default({
1232
- enableWorker: true,
1233
- lowLatencyMode: false
1234
- });
1235
- adHls.loadSource(mediaFile.url);
1236
- adHls.attachMedia(adVideoElement);
1237
- adHls.on(import_hls.default.Events.MANIFEST_PARSED, function() {
1238
- console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1239
- adVideoElement.play().catch(function(error) {
1240
- console.error("[HlsAdPlayer] Error starting ad playback:", error);
1241
- handleAdError();
1242
- });
1243
- });
1244
- adHls.on(import_hls.default.Events.ERROR, function(event, data) {
1245
- console.error("[HlsAdPlayer] HLS error:", data);
1246
- if (data.fatal) {
1247
- handleAdError();
1248
- }
1249
- });
1250
- } else if (isHlsAd && adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1251
- adVideoElement.src = mediaFile.url;
1252
- adVideoElement.play().catch(function(error) {
1253
- console.error("[HlsAdPlayer] Error starting ad playback:", error);
1254
- handleAdError();
1255
- });
1256
- } else if (!isHlsAd && isProgressiveMediaFile(mediaFile)) {
1257
- if (adHls) {
1258
- adHls.destroy();
1259
- adHls = void 0;
1260
- }
1261
- adVideoElement.src = mediaFile.url;
1262
- adVideoElement.load();
1263
- adVideoElement.play().catch(function(error) {
1264
- console.error("[HlsAdPlayer] Error starting progressive ad playback:", error);
1265
- handleAdError();
1266
- });
1267
- } else {
1268
- throw new Error("Unsupported ad media file type: ".concat(mediaFile.type));
1269
- }
1532
+ loadCurrentAdMedia();
1270
1533
  return [
1271
1534
  2,
1272
1535
  Promise.resolve()
@@ -1341,6 +1604,8 @@ function createHlsAdPlayer(contentVideo, options) {
1341
1604
  adVideoElement.src = "";
1342
1605
  }
1343
1606
  currentAd = void 0;
1607
+ podAds = [];
1608
+ podIndex = 0;
1344
1609
  return [
1345
1610
  2
1346
1611
  ];
@@ -1390,6 +1655,8 @@ function createHlsAdPlayer(contentVideo, options) {
1390
1655
  }
1391
1656
  adContainerEl = void 0;
1392
1657
  currentAd = void 0;
1658
+ podAds = [];
1659
+ podIndex = 0;
1393
1660
  listeners.clear();
1394
1661
  preloadedAds.clear();
1395
1662
  preloadingAds.clear();
@@ -1481,6 +1748,14 @@ function createHlsAdPlayer(contentVideo, options) {
1481
1748
  },
1482
1749
  getLoadedAdId: function getLoadedAdId() {
1483
1750
  return currentAd === null || currentAd === void 0 ? void 0 : currentAd.id;
1751
+ },
1752
+ getPodAdCount: function getPodAdCount() {
1753
+ return podAds.length;
1754
+ },
1755
+ getPodTotalDurationMs: function getPodTotalDurationMs() {
1756
+ return podAds.reduce(function(total, ad) {
1757
+ return total + (ad.duration > 0 ? ad.duration * 1e3 : 0);
1758
+ }, 0);
1484
1759
  }
1485
1760
  };
1486
1761
  }
@@ -2204,6 +2479,16 @@ function normalizeBoolean(value) {
2204
2479
  }
2205
2480
  // src/utils/tracking.ts
2206
2481
  var cachedBrowserId = null;
2482
+ function buildAnalyticsContext(config) {
2483
+ var context = {};
2484
+ if (config.debugAdTiming) {
2485
+ context.debugAdTiming = true;
2486
+ }
2487
+ if (config.isLiveStream !== void 0) {
2488
+ context.inputStreamType = config.isLiveStream ? "live" : "vod";
2489
+ }
2490
+ return context;
2491
+ }
2207
2492
  function getClientInfo() {
2208
2493
  var _screen, _screen1, _screen2, _screen3, _screen_orientation, _screen4, _screen5, _window, _window1, _window_screen_orientation, _window_screen, _navigator_languages;
2209
2494
  var ua = navigator.userAgent;
@@ -2462,19 +2747,26 @@ function canPublish(licenseKey) {
2462
2747
  }
2463
2748
  function buildPlayerMetricEvent() {
2464
2749
  return _async_to_generator(function() {
2465
- var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
2750
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, debugAdTiming, playerId, captureAt;
2466
2751
  var _arguments = arguments;
2467
2752
  return _ts_generator(this, function(_state) {
2468
2753
  switch(_state.label){
2469
2754
  case 0:
2470
2755
  context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
2471
2756
  clientInfo = getClientInfo();
2757
+ debugAdTiming = !!context.debugAdTiming;
2758
+ if (debugAdTiming) {
2759
+ console.log("[StormcloudVideoPlayer] clientInfo: ", JSON.stringify(clientInfo, null, 2));
2760
+ }
2472
2761
  return [
2473
2762
  4,
2474
2763
  getBrowserID(clientInfo)
2475
2764
  ];
2476
2765
  case 1:
2477
2766
  playerId = _state.sent();
2767
+ if (debugAdTiming) {
2768
+ console.log("[StormcloudVideoPlayer] playerId: ", playerId);
2769
+ }
2478
2770
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
2479
2771
  return [
2480
2772
  2,
@@ -3316,7 +3608,20 @@ function applyVastMacros(baseUrl, ctx) {
3316
3608
  if (ctx.pod != null) {
3317
3609
  params.set("pod", String(ctx.pod));
3318
3610
  }
3319
- if (ctx.adPosition != null) {
3611
+ var optimizedPodMode = ctx.podMaxAds != null || ctx.podMinDurationMs != null || ctx.podMaxDurationMs != null;
3612
+ if (optimizedPodMode) {
3613
+ params.delete("ppos");
3614
+ params.delete("lip");
3615
+ if (ctx.podMinDurationMs != null) {
3616
+ params.set("pmnd", String(Math.max(0, Math.round(ctx.podMinDurationMs))));
3617
+ }
3618
+ if (ctx.podMaxDurationMs != null) {
3619
+ params.set("pmxd", String(Math.max(0, Math.round(ctx.podMaxDurationMs))));
3620
+ }
3621
+ if (ctx.podMaxAds != null) {
3622
+ params.set("pmad", String(Math.max(1, Math.round(ctx.podMaxAds))));
3623
+ }
3624
+ } else if (ctx.adPosition != null) {
3320
3625
  params.set("ppos", String(ctx.adPosition));
3321
3626
  }
3322
3627
  var requestUrl = ctx.contentUrl || ctx.pageUrl;
@@ -4681,6 +4986,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
4681
4986
  this.podAssignedByPrefetch = false;
4682
4987
  this.adRequestPositionInBreak = 0;
4683
4988
  this.lastHeartbeatTime = 0;
4989
+ this.defaultPodMaxAds = 6;
4684
4990
  this.config = config;
4685
4991
  this.video = video;
4686
4992
  }
@@ -5136,12 +5442,58 @@ var AdConfigManager = /*#__PURE__*/ function() {
5136
5442
  return urls;
5137
5443
  }
5138
5444
  },
5445
+ {
5446
+ key: "resolvePodParams",
5447
+ value: function resolvePodParams(breakDurationMs) {
5448
+ var maxDurationMs = breakDurationMs != null && breakDurationMs > 0 ? breakDurationMs : this.config.podMaxDurationMs;
5449
+ var maxAds = this.config.podMaxAds && this.config.podMaxAds > 0 ? this.config.podMaxAds : this.apiNumberAds && this.apiNumberAds > 0 ? this.apiNumberAds : this.defaultPodMaxAds;
5450
+ var minDurationMs = this.config.podExactDuration && maxDurationMs != null ? maxDurationMs : 0;
5451
+ return {
5452
+ maxAds: maxAds,
5453
+ minDurationMs: minDurationMs,
5454
+ maxDurationMs: maxDurationMs
5455
+ };
5456
+ }
5457
+ },
5458
+ {
5459
+ key: "generatePodVastUrl",
5460
+ value: function generatePodVastUrl(baseUrl, podParams, adPlayer, palNonce, inAdBreak) {
5461
+ var _podParams_maxDurationMs;
5462
+ var adWillPlayMuted = inAdBreak ? adPlayer.getOriginalMutedState() : this.video.muted;
5463
+ var envSignals = resolveVastEnvironmentSignals(!!this.config.ctvAdRequest);
5464
+ var urlWithMacros = applyVastMacros(baseUrl, {
5465
+ correlator: generateCorrelator(),
5466
+ streamCorrelator: this.streamCorrelator,
5467
+ pod: this.podCounter > 0 ? this.podCounter : void 0,
5468
+ podMaxAds: podParams.maxAds,
5469
+ podMinDurationMs: podParams.minDurationMs,
5470
+ podMaxDurationMs: podParams.maxDurationMs,
5471
+ isCtv: envSignals.isCtv,
5472
+ contentUrl: envSignals.contentUrl,
5473
+ pageUrl: !envSignals.isCtv && typeof window !== "undefined" ? window.location.href : void 0,
5474
+ adWillPlayMuted: adWillPlayMuted,
5475
+ adWillAutoPlay: !!this.config.autoplay,
5476
+ appId: envSignals.appId,
5477
+ appName: envSignals.appName,
5478
+ sessionId: envSignals.sessionId,
5479
+ deviceId: envSignals.deviceId,
5480
+ deviceIdType: envSignals.deviceIdType,
5481
+ limitAdTracking: envSignals.limitAdTracking,
5482
+ deviceTypeHint: envSignals.deviceTypeHint,
5483
+ adTest: this.config.adTest,
5484
+ consent: this.consentSignals
5485
+ });
5486
+ var finalUrl = palNonce.injectNonce(urlWithMacros);
5487
+ 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);
5488
+ return finalUrl;
5489
+ }
5490
+ },
5139
5491
  {
5140
5492
  // --- Tracking ---
5141
5493
  key: "initializeTracking",
5142
5494
  value: function initializeTracking() {
5143
5495
  var _this = this;
5144
- sendInitialTracking(this.config.licenseKey).then(function() {
5496
+ sendInitialTracking(this.config.licenseKey, buildAnalyticsContext(this.config)).then(function() {
5145
5497
  _this.heartbeatInterval = window.setInterval(function() {
5146
5498
  _this.sendHeartbeatIfNeeded();
5147
5499
  }, 5e3);
@@ -5162,7 +5514,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
5162
5514
  var now = Date.now();
5163
5515
  if (!this.lastHeartbeatTime || now - this.lastHeartbeatTime > 3e4) {
5164
5516
  this.lastHeartbeatTime = now;
5165
- sendHeartbeat(this.config.licenseKey).catch(function(error) {
5517
+ sendHeartbeat(this.config.licenseKey, buildAnalyticsContext(this.config)).catch(function(error) {
5166
5518
  if (_this.debug) {
5167
5519
  console.warn("[StormcloudVideoPlayer] Failed to send heartbeat:", error);
5168
5520
  }
@@ -5441,6 +5793,13 @@ var AdTimingService = /*#__PURE__*/ function() {
5441
5793
  this.lastAdRequestTime = Date.now();
5442
5794
  }
5443
5795
  },
5796
+ {
5797
+ key: "notePodAdStarted",
5798
+ value: function notePodAdStarted() {
5799
+ this.currentAdIndex++;
5800
+ this.totalAdsInBreak = this.currentAdIndex;
5801
+ }
5802
+ },
5444
5803
  {
5445
5804
  key: "getRemainingAdMs",
5446
5805
  value: function getRemainingAdMs() {
@@ -6943,19 +7302,38 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6943
7302
  }
6944
7303
  },
6945
7304
  {
6946
- // ───────────────────────────────────────────────────────────────
7305
+ key: "analyticsContext",
7306
+ get: function get() {
7307
+ return buildAnalyticsContext(this.host.config);
7308
+ }
7309
+ },
7310
+ {
7311
+ key: "optimizedPodsEnabled",
7312
+ get: // ───────────────────────────────────────────────────────────────
6947
7313
  // IMA event listeners
6948
7314
  // ───────────────────────────────────────────────────────────────
7315
+ function get() {
7316
+ return !!this.host.config.optimizedPods;
7317
+ }
7318
+ },
7319
+ {
6949
7320
  key: "attachImaEventListeners",
6950
7321
  value: function attachImaEventListeners() {
6951
7322
  var _this = this;
6952
7323
  var adPlayer = this.host.getAdPlayer();
7324
+ adPlayer.on("pod_ad_started", function(payload) {
7325
+ _this.timing.notePodAdStarted();
7326
+ if (_this.debug) {
7327
+ var _ref;
7328
+ 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 : "?"));
7329
+ }
7330
+ });
6953
7331
  adPlayer.on("all_ads_completed", function() {
6954
7332
  sendAdImpressionTracking(_this.host.config.licenseKey, {
6955
7333
  source: "hls",
6956
7334
  adIndex: _this.timing.currentAdIndex,
6957
7335
  timestamp: /* @__PURE__ */ new Date().toISOString()
6958
- }).catch(function() {});
7336
+ }, _this.analyticsContext).catch(function() {});
6959
7337
  _this.timing.recordPlayedAdDuration(_this.host.getAdPlayer());
6960
7338
  var remaining = _this.timing.getRemainingAdMs();
6961
7339
  _this.timing.consecutiveFailures = 0;
@@ -7017,7 +7395,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7017
7395
  source: "hls",
7018
7396
  adIndex: _this.timing.currentAdIndex,
7019
7397
  timestamp: /* @__PURE__ */ new Date().toISOString()
7020
- }).catch(function() {});
7398
+ }, _this.analyticsContext).catch(function() {});
7021
7399
  if (!_this.host.video.muted) {
7022
7400
  _this.host.video.muted = true;
7023
7401
  if (_this.debug) {
@@ -7107,7 +7485,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7107
7485
  // ───────────────────────────────────────────────────────────────
7108
7486
  function handleAdStart(_marker) {
7109
7487
  return _async_to_generator(function() {
7110
- var scheduled, tags, baseVastUrl, adBreakDurationMs, mode, currentMuted, currentVolume, firstAdUrl, usePreloadedAd, preloadedController, preloaded, firstAdUrlArray, adPlayer, preservedMuted, preservedVolume, adVolume, adPlayer1, adVolume1, error, fallbackPreloaded, ap, adVolume2, fallbackError;
7488
+ 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;
7111
7489
  return _ts_generator(this, function(_state) {
7112
7490
  switch(_state.label){
7113
7491
  case 0:
@@ -7154,6 +7532,26 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7154
7532
  if (this.timing.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
7155
7533
  this.timing.expectedAdBreakDurationMs = adBreakDurationMs;
7156
7534
  }
7535
+ if (!this.optimizedPodsEnabled) return [
7536
+ 3,
7537
+ 2
7538
+ ];
7539
+ return [
7540
+ 4,
7541
+ this.startOptimizedPod(baseVastUrl, adBreakDurationMs, currentMuted, currentVolume)
7542
+ ];
7543
+ case 1:
7544
+ podPlayed = _state.sent();
7545
+ if (podPlayed) {
7546
+ return [
7547
+ 2
7548
+ ];
7549
+ }
7550
+ if (this.debug) {
7551
+ console.log("[POD] Optimized pod unavailable, falling back to sequential single-ad path");
7552
+ }
7553
+ _state.label = 2;
7554
+ case 2:
7157
7555
  usePreloadedAd = false;
7158
7556
  preloaded = this.preloadPool.getPreloadedAd();
7159
7557
  if (preloaded) {
@@ -7164,7 +7562,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7164
7562
  source: "hls",
7165
7563
  vastUrl: firstAdUrl,
7166
7564
  timestamp: /* @__PURE__ */ new Date().toISOString()
7167
- }).catch(function() {});
7565
+ }, this.analyticsContext).catch(function() {});
7168
7566
  if (this.debug) {
7169
7567
  console.log("[CONTINUOUS-FETCH] Using preloaded ad from pool (preloaded in advance, ready immediately!)");
7170
7568
  }
@@ -7195,21 +7593,21 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7195
7593
  if (this.debug) {
7196
7594
  console.log("[CONTINUOUS-FETCH] First VAST URL:", firstAdUrl);
7197
7595
  }
7198
- _state.label = 1;
7199
- case 1:
7596
+ _state.label = 3;
7597
+ case 3:
7200
7598
  _state.trys.push([
7201
- 1,
7202
- 12,
7599
+ 3,
7600
+ 14,
7203
7601
  ,
7204
- 20
7602
+ 22
7205
7603
  ]);
7206
7604
  if (!(usePreloadedAd && preloadedController)) return [
7207
7605
  3,
7208
- 5
7606
+ 7
7209
7607
  ];
7210
7608
  if (!!this.timing.ensureLoadedAdFitsBudget(preloadedController)) return [
7211
7609
  3,
7212
- 3
7610
+ 5
7213
7611
  ];
7214
7612
  this.timing.rejectLoadedAdForDuration(preloadedController);
7215
7613
  this.startContinuousFetching(baseVastUrl);
@@ -7217,12 +7615,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7217
7615
  4,
7218
7616
  this.tryNextAvailableAdWithRateLimit()
7219
7617
  ];
7220
- case 2:
7618
+ case 4:
7221
7619
  _state.sent();
7222
7620
  return [
7223
7621
  2
7224
7622
  ];
7225
- case 3:
7623
+ case 5:
7226
7624
  adPlayer = this.host.getAdPlayer();
7227
7625
  preservedMuted = adPlayer.getOriginalMutedState();
7228
7626
  preservedVolume = adPlayer.getOriginalVolume();
@@ -7247,7 +7645,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7247
7645
  4,
7248
7646
  preloadedController.play()
7249
7647
  ];
7250
- case 4:
7648
+ case 6:
7251
7649
  _state.sent();
7252
7650
  this.timing.markAdStarted();
7253
7651
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7257,34 +7655,34 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7257
7655
  preloadedController.setAdVolume(adVolume);
7258
7656
  return [
7259
7657
  3,
7260
- 11
7658
+ 13
7261
7659
  ];
7262
- case 5:
7660
+ case 7:
7263
7661
  adPlayer1 = this.host.getAdPlayer();
7264
7662
  return [
7265
7663
  4,
7266
7664
  this.timing.enforceGlobalRateLimit()
7267
7665
  ];
7268
- case 6:
7666
+ case 8:
7269
7667
  _state.sent();
7270
7668
  this.timing.lastAdRequestTime = Date.now();
7271
7669
  return [
7272
7670
  4,
7273
7671
  adPlayer1.requestAds(firstAdUrl)
7274
7672
  ];
7275
- case 7:
7673
+ case 9:
7276
7674
  _state.sent();
7277
7675
  sendAdLoadedTracking(this.host.config.licenseKey, {
7278
7676
  source: "hls",
7279
7677
  vastUrl: firstAdUrl,
7280
7678
  timestamp: /* @__PURE__ */ new Date().toISOString()
7281
- }).catch(function() {});
7679
+ }, this.analyticsContext).catch(function() {});
7282
7680
  if (this.debug) {
7283
7681
  console.log("[CONTINUOUS-FETCH] First ad request successful, starting playback");
7284
7682
  }
7285
7683
  if (!!this.timing.ensureLoadedAdFitsBudget(adPlayer1)) return [
7286
7684
  3,
7287
- 9
7685
+ 11
7288
7686
  ];
7289
7687
  this.timing.rejectLoadedAdForDuration(adPlayer1);
7290
7688
  this.recreateAdController();
@@ -7293,12 +7691,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7293
7691
  4,
7294
7692
  this.tryNextAvailableAdWithRateLimit()
7295
7693
  ];
7296
- case 8:
7694
+ case 10:
7297
7695
  _state.sent();
7298
7696
  return [
7299
7697
  2
7300
7698
  ];
7301
- case 9:
7699
+ case 11:
7302
7700
  this.timing.consecutiveFailures = 0;
7303
7701
  this.startContinuousFetching(baseVastUrl);
7304
7702
  if (!this.preloadPool.active) {
@@ -7308,7 +7706,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7308
7706
  4,
7309
7707
  adPlayer1.play()
7310
7708
  ];
7311
- case 10:
7709
+ case 12:
7312
7710
  _state.sent();
7313
7711
  this.timing.markAdStarted();
7314
7712
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7316,40 +7714,40 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7316
7714
  }
7317
7715
  adVolume1 = currentMuted ? 0 : currentVolume;
7318
7716
  adPlayer1.setAdVolume(adVolume1);
7319
- _state.label = 11;
7320
- case 11:
7717
+ _state.label = 13;
7718
+ case 13:
7321
7719
  return [
7322
7720
  3,
7323
- 20
7721
+ 22
7324
7722
  ];
7325
- case 12:
7723
+ case 14:
7326
7724
  error = _state.sent();
7327
7725
  if (this.debug) {
7328
7726
  console.warn("[CONTINUOUS-FETCH] First ad request failed:", error);
7329
7727
  }
7330
7728
  if (!!usePreloadedAd) return [
7331
7729
  3,
7332
- 18
7730
+ 20
7333
7731
  ];
7334
7732
  fallbackPreloaded = this.preloadPool.getPreloadedAd();
7335
7733
  if (!fallbackPreloaded) return [
7336
7734
  3,
7337
- 18
7735
+ 20
7338
7736
  ];
7339
7737
  if (this.debug) {
7340
7738
  console.log("[CONTINUOUS-FETCH] First ad failed, using preloaded fallback");
7341
7739
  }
7342
- _state.label = 13;
7343
- case 13:
7740
+ _state.label = 15;
7741
+ case 15:
7344
7742
  _state.trys.push([
7345
- 13,
7346
- 17,
7743
+ 15,
7744
+ 19,
7347
7745
  ,
7348
- 18
7746
+ 20
7349
7747
  ]);
7350
7748
  if (!!this.timing.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
7351
7749
  3,
7352
- 15
7750
+ 17
7353
7751
  ];
7354
7752
  this.timing.rejectLoadedAdForDuration(fallbackPreloaded.adController);
7355
7753
  this.startContinuousFetching(baseVastUrl);
@@ -7357,12 +7755,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7357
7755
  4,
7358
7756
  this.tryNextAvailableAdWithRateLimit()
7359
7757
  ];
7360
- case 14:
7758
+ case 16:
7361
7759
  _state.sent();
7362
7760
  return [
7363
7761
  2
7364
7762
  ];
7365
- case 15:
7763
+ case 17:
7366
7764
  this.swapToPreloadedAdPlayer(fallbackPreloaded.adController);
7367
7765
  this.timing.consecutiveFailures = 0;
7368
7766
  this.startContinuousFetching(baseVastUrl);
@@ -7375,7 +7773,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7375
7773
  4,
7376
7774
  ap.play()
7377
7775
  ];
7378
- case 16:
7776
+ case 18:
7379
7777
  _state.sent();
7380
7778
  this.timing.markAdStarted();
7381
7779
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7386,16 +7784,16 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7386
7784
  return [
7387
7785
  2
7388
7786
  ];
7389
- case 17:
7787
+ case 19:
7390
7788
  fallbackError = _state.sent();
7391
7789
  if (this.debug) {
7392
7790
  console.warn("[CONTINUOUS-FETCH] Preloaded fallback also failed:", fallbackError);
7393
7791
  }
7394
7792
  return [
7395
7793
  3,
7396
- 18
7794
+ 20
7397
7795
  ];
7398
- case 18:
7796
+ case 20:
7399
7797
  if (this.timing.isTemporaryAdError(error)) {
7400
7798
  this.timing.temporaryFailureUrls.set(firstAdUrl, Date.now());
7401
7799
  if (this.debug) {
@@ -7413,13 +7811,137 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7413
7811
  4,
7414
7812
  this.tryNextAvailableAdWithRateLimit()
7415
7813
  ];
7416
- case 19:
7814
+ case 21:
7417
7815
  _state.sent();
7418
7816
  return [
7419
7817
  3,
7420
- 20
7818
+ 22
7421
7819
  ];
7422
- case 20:
7820
+ case 22:
7821
+ return [
7822
+ 2
7823
+ ];
7824
+ }
7825
+ });
7826
+ }).call(this);
7827
+ }
7828
+ },
7829
+ {
7830
+ key: "startOptimizedPod",
7831
+ value: // ───────────────────────────────────────────────────────────────
7832
+ // Optimized pod (single multi-ad request)
7833
+ // ───────────────────────────────────────────────────────────────
7834
+ function startOptimizedPod(baseVastUrl, breakDurationMs, currentMuted, currentVolume) {
7835
+ return _async_to_generator(function() {
7836
+ var podUrl, adPlayer, requestToken, _ref, _adPlayer_getPodAdCount, podCount, error;
7837
+ return _ts_generator(this, function(_state) {
7838
+ switch(_state.label){
7839
+ case 0:
7840
+ if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {
7841
+ podUrl = this.pendingAdBreak.vastUrls[0];
7842
+ this.clearPendingAdBreak();
7843
+ } else {
7844
+ podUrl = this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);
7845
+ }
7846
+ if (!podUrl) {
7847
+ return [
7848
+ 2,
7849
+ false
7850
+ ];
7851
+ }
7852
+ this.continuousFetchingActive = false;
7853
+ this.recreateAdController();
7854
+ adPlayer = this.host.getAdPlayer();
7855
+ requestToken = ++this.timing.adRequestTokenCounter;
7856
+ this.timing.activeAdRequestToken = requestToken;
7857
+ this.timing.startAdRequestWatchdog(requestToken);
7858
+ _state.label = 1;
7859
+ case 1:
7860
+ _state.trys.push([
7861
+ 1,
7862
+ 5,
7863
+ ,
7864
+ 6
7865
+ ]);
7866
+ return [
7867
+ 4,
7868
+ this.timing.enforceGlobalRateLimit()
7869
+ ];
7870
+ case 2:
7871
+ _state.sent();
7872
+ this.timing.lastAdRequestTime = Date.now();
7873
+ return [
7874
+ 4,
7875
+ adPlayer.requestAds(podUrl)
7876
+ ];
7877
+ case 3:
7878
+ _state.sent();
7879
+ this.timing.clearAdRequestWatchdog();
7880
+ if (this.timing.activeAdRequestToken !== requestToken) {
7881
+ return [
7882
+ 2,
7883
+ true
7884
+ ];
7885
+ }
7886
+ podCount = (_ref = (_adPlayer_getPodAdCount = adPlayer.getPodAdCount) === null || _adPlayer_getPodAdCount === void 0 ? void 0 : _adPlayer_getPodAdCount.call(adPlayer)) !== null && _ref !== void 0 ? _ref : 1;
7887
+ if (podCount < 1) {
7888
+ return [
7889
+ 2,
7890
+ false
7891
+ ];
7892
+ }
7893
+ if (this.debug) {
7894
+ 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"));
7895
+ }
7896
+ sendAdLoadedTracking(this.host.config.licenseKey, {
7897
+ source: "hls",
7898
+ vastUrl: podUrl,
7899
+ timestamp: /* @__PURE__ */ new Date().toISOString()
7900
+ }, this.analyticsContext).catch(function() {});
7901
+ this.timing.startAdFailsafeTimer(requestToken);
7902
+ return [
7903
+ 4,
7904
+ adPlayer.play()
7905
+ ];
7906
+ case 4:
7907
+ _state.sent();
7908
+ this.timing.markAdStarted();
7909
+ if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
7910
+ this.timing.scheduleAdStopAtBreakBoundary();
7911
+ }
7912
+ adPlayer.setAdVolume(currentMuted ? 0 : currentVolume);
7913
+ this.timing.consecutiveFailures = 0;
7914
+ if (podCount === 1) {
7915
+ this.continuousFetchingActive = true;
7916
+ this.startContinuousFetching(baseVastUrl);
7917
+ if (!this.preloadPool.active) {
7918
+ this.preloadPool.startPreloadPool(baseVastUrl, []);
7919
+ }
7920
+ }
7921
+ return [
7922
+ 2,
7923
+ true
7924
+ ];
7925
+ case 5:
7926
+ error = _state.sent();
7927
+ this.timing.clearAdRequestWatchdog();
7928
+ this.timing.clearAdFailsafeTimer();
7929
+ if (this.timing.activeAdRequestToken === requestToken) {
7930
+ this.timing.activeAdRequestToken = null;
7931
+ }
7932
+ if (this.debug) {
7933
+ console.warn("[POD] Optimized pod request/play failed:", error);
7934
+ }
7935
+ if (this.timing.isTemporaryAdError(error)) {
7936
+ this.timing.temporaryFailureUrls.set(podUrl, Date.now());
7937
+ }
7938
+ this.continuousFetchingActive = true;
7939
+ this.recreateAdController();
7940
+ return [
7941
+ 2,
7942
+ false
7943
+ ];
7944
+ case 6:
7423
7945
  return [
7424
7946
  2
7425
7947
  ];
@@ -7742,7 +8264,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7742
8264
  source: "hls",
7743
8265
  vastUrl: preloaded.vastUrl,
7744
8266
  timestamp: /* @__PURE__ */ new Date().toISOString()
7745
- }).catch(function() {});
8267
+ }, this.analyticsContext).catch(function() {});
7746
8268
  _state.label = 1;
7747
8269
  case 1:
7748
8270
  _state.trys.push([
@@ -7975,7 +8497,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7975
8497
  source: "hls",
7976
8498
  vastUrl: vastTagUrl,
7977
8499
  timestamp: /* @__PURE__ */ new Date().toISOString()
7978
- }).catch(function() {});
8500
+ }, this.analyticsContext).catch(function() {});
7979
8501
  this.timing.clearAdRequestWatchdog();
7980
8502
  if (this.timing.activeAdRequestToken !== requestToken) return [
7981
8503
  2
@@ -8501,6 +9023,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8501
9023
  },
8502
9024
  generateVastUrls: function generateVastUrls(base, count) {
8503
9025
  return _this.generateVastUrls(base, count);
9026
+ },
9027
+ generatePodVastUrl: function generatePodVastUrl(base, breakDurationMs) {
9028
+ return _this.generatePodVastUrl(base, breakDurationMs);
8504
9029
  }
8505
9030
  }, this.timing, this.preloadPool, this.adConfig, this.cueManager, this.placeholder);
8506
9031
  }
@@ -8533,6 +9058,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8533
9058
  return this.adConfig.generateVastUrlsWithCorrelators(baseUrl, count, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8534
9059
  }
8535
9060
  },
9061
+ {
9062
+ key: "generatePodVastUrl",
9063
+ value: function generatePodVastUrl(baseUrl, breakDurationMs) {
9064
+ var podParams = this.adConfig.resolvePodParams(breakDurationMs);
9065
+ return this.adConfig.generatePodVastUrl(baseUrl, podParams, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
9066
+ }
9067
+ },
8536
9068
  {
8537
9069
  key: "load",
8538
9070
  value: // ───────────────────────────────────────────────────────────────
@@ -8767,7 +9299,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8767
9299
  sendAdDetectTracking(this.config.licenseKey, _object_spread({
8768
9300
  source: "scte35",
8769
9301
  timestamp: /* @__PURE__ */ new Date().toISOString()
8770
- }, detectPayload)).catch(function() {});
9302
+ }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8771
9303
  }
8772
9304
  var hasPrefetchedAds = this.adBreak.pendingAdBreak && this.adBreak.pendingAdBreak.vastUrls.length > 0;
8773
9305
  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;
@@ -8921,7 +9453,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8921
9453
  }
8922
9454
  this.adConfig.beginNewAdPod();
8923
9455
  this.adConfig.podAssignedByPrefetch = true;
8924
- var generatedUrls = this.generateVastUrls(baseVastUrl, 1);
9456
+ var optimizedPods = !!this.config.optimizedPods;
9457
+ var breakDurationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : void 0;
9458
+ var generatedUrls = optimizedPods ? [
9459
+ this.generatePodVastUrl(baseVastUrl, breakDurationMs)
9460
+ ] : this.generateVastUrls(baseVastUrl, 1);
8925
9461
  this.adBreak.pendingAdBreak = _object_spread_props(_object_spread({
8926
9462
  marker: marker
8927
9463
  }, fragmentSn !== void 0 ? {
@@ -8940,10 +9476,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8940
9476
  sendAdDetectTracking(this.config.licenseKey, _object_spread({
8941
9477
  source: "scte35",
8942
9478
  timestamp: /* @__PURE__ */ new Date().toISOString()
8943
- }, detectPayload)).catch(function() {});
9479
+ }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8944
9480
  if (this.debug) {
8945
9481
  console.log("[PREFETCH] Starting ad prefetch for upcoming ad break");
8946
- console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
9482
+ console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " ").concat(optimizedPods ? "pod" : "VAST", " URL(s)"));
9483
+ }
9484
+ if (optimizedPods) {
9485
+ if (this.debug) {
9486
+ console.log("[PREFETCH] Optimized pods enabled - skipping single-ad preload pool");
9487
+ }
9488
+ return;
8947
9489
  }
8948
9490
  this.preloadPool.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
8949
9491
  }
@@ -11752,6 +12294,7 @@ var StormcloudPlayer_default = StormcloudPlayer;
11752
12294
  StormcloudVideoPlayer: StormcloudVideoPlayer,
11753
12295
  StormcloudVideoPlayerComponent: StormcloudVideoPlayerComponent,
11754
12296
  applyMQTTConfig: applyMQTTConfig,
12297
+ buildAnalyticsContext: buildAnalyticsContext,
11755
12298
  buildMQTTBrokerUrl: buildMQTTBrokerUrl,
11756
12299
  buildPlayerTopic: buildPlayerTopic,
11757
12300
  canPlay: canPlay,