stormcloud-video-player 0.8.25 → 0.8.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/stormcloud-vp.min.js +1 -1
  2. package/lib/index.cjs +714 -128
  3. package/lib/index.cjs.map +1 -1
  4. package/lib/index.d.cts +7 -0
  5. package/lib/index.d.ts +7 -0
  6. package/lib/index.js +714 -128
  7. package/lib/index.js.map +1 -1
  8. package/lib/player/AdBreakOrchestrator.cjs +278 -49
  9. package/lib/player/AdBreakOrchestrator.cjs.map +1 -1
  10. package/lib/player/AdBreakOrchestrator.d.cts +4 -1
  11. package/lib/player/AdConfigManager.cjs +144 -14
  12. package/lib/player/AdConfigManager.cjs.map +1 -1
  13. package/lib/player/AdConfigManager.d.cts +12 -1
  14. package/lib/player/AdPreloadPool.d.cts +1 -1
  15. package/lib/player/AdTimingService.cjs +7 -0
  16. package/lib/player/AdTimingService.cjs.map +1 -1
  17. package/lib/player/AdTimingService.d.cts +2 -1
  18. package/lib/player/HlsEngine.d.cts +1 -1
  19. package/lib/player/PlayerControls.d.cts +1 -1
  20. package/lib/player/Scte35CueManager.d.cts +1 -1
  21. package/lib/player/Scte35Parser.d.cts +1 -1
  22. package/lib/player/StormcloudVideoPlayer.cjs +714 -128
  23. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  24. package/lib/player/StormcloudVideoPlayer.d.cts +2 -1
  25. package/lib/player/playerTypes.d.cts +1 -1
  26. package/lib/players/HlsPlayer.cjs +714 -128
  27. package/lib/players/HlsPlayer.cjs.map +1 -1
  28. package/lib/players/HlsPlayer.d.cts +1 -1
  29. package/lib/players/index.cjs +714 -128
  30. package/lib/players/index.cjs.map +1 -1
  31. package/lib/sdk/hlsAdPlayer.cjs +343 -71
  32. package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
  33. package/lib/sdk/hlsAdPlayer.d.cts +1 -1
  34. package/lib/{types-Bz4aRmzc.d.cts → types-Xgz2_W1C.d.cts} +6 -0
  35. package/lib/ui/StormcloudVideoPlayer.cjs +714 -128
  36. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  37. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  38. package/lib/utils/ctvVastSignals.cjs +80 -8
  39. package/lib/utils/ctvVastSignals.cjs.map +1 -1
  40. package/lib/utils/tracking.cjs +80 -8
  41. package/lib/utils/tracking.cjs.map +1 -1
  42. package/lib/utils/tracking.d.cts +1 -1
  43. package/lib/utils/vastEnvironmentSignals.cjs +83 -13
  44. package/lib/utils/vastEnvironmentSignals.cjs.map +1 -1
  45. package/lib/utils/vastMacros.cjs +15 -1
  46. package/lib/utils/vastMacros.cjs.map +1 -1
  47. package/lib/utils/vastMacros.d.cts +3 -0
  48. package/package.json +1 -1
package/lib/index.cjs CHANGED
@@ -599,6 +599,8 @@ function createHlsAdPlayer(contentVideo, options) {
599
599
  var adHls;
600
600
  var adContainerEl;
601
601
  var currentAd;
602
+ var podAds = [];
603
+ var podIndex = 0;
602
604
  var sessionId;
603
605
  var preloadedAds = /* @__PURE__ */ new Map();
604
606
  var preloadingAds = /* @__PURE__ */ new Map();
@@ -763,14 +765,14 @@ function createHlsAdPlayer(contentVideo, options) {
763
765
  error: []
764
766
  };
765
767
  }
766
- function extractTrackingUrls(xmlDoc) {
768
+ function extractTrackingUrls(scope) {
767
769
  var trackingUrls = createEmptyTrackingUrls();
768
- xmlDoc.querySelectorAll("Impression").forEach(function(el) {
770
+ scope.querySelectorAll("Impression").forEach(function(el) {
769
771
  var _el_textContent;
770
772
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
771
773
  if (url) trackingUrls.impression.push(url);
772
774
  });
773
- xmlDoc.querySelectorAll("Tracking").forEach(function(el) {
775
+ scope.querySelectorAll("Tracking").forEach(function(el) {
774
776
  var _el_textContent;
775
777
  var event = el.getAttribute("event");
776
778
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
@@ -781,7 +783,7 @@ function createHlsAdPlayer(contentVideo, options) {
781
783
  }
782
784
  }
783
785
  });
784
- xmlDoc.querySelectorAll("Error").forEach(function(el) {
786
+ scope.querySelectorAll("Error").forEach(function(el) {
785
787
  var _el_textContent;
786
788
  var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
787
789
  if (url) trackingUrls.error.push(url);
@@ -891,6 +893,75 @@ function createHlsAdPlayer(contentVideo, options) {
891
893
  };
892
894
  }
893
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
+ }
894
965
  function fetchVastXml(vastTagUrl) {
895
966
  return _async_to_generator(function() {
896
967
  var response;
@@ -967,6 +1038,146 @@ function createHlsAdPlayer(contentVideo, options) {
967
1038
  });
968
1039
  }).apply(this, arguments);
969
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
+ }
970
1181
  function createAdVideoElement() {
971
1182
  var video = document.createElement("video");
972
1183
  video.style.position = "absolute";
@@ -1011,6 +1222,9 @@ function createHlsAdPlayer(contentVideo, options) {
1011
1222
  trackingFired.complete = true;
1012
1223
  fireTrackingPixels(currentAd.trackingUrls.complete);
1013
1224
  console.log("[HlsAdPlayer] Ad completed");
1225
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1226
+ return;
1227
+ }
1014
1228
  handleAdComplete();
1015
1229
  });
1016
1230
  adVideoElement.addEventListener("error", function(e) {
@@ -1019,6 +1233,9 @@ function createHlsAdPlayer(contentVideo, options) {
1019
1233
  if (currentAd) {
1020
1234
  fireTrackingPixels(currentAd.trackingUrls.error);
1021
1235
  }
1236
+ if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1237
+ return;
1238
+ }
1022
1239
  handleAdError();
1023
1240
  });
1024
1241
  adVideoElement.addEventListener("volumechange", function() {
@@ -1047,6 +1264,94 @@ function createHlsAdPlayer(contentVideo, options) {
1047
1264
  delete contentVideo.dataset.stormcloudAdPlaying;
1048
1265
  }
1049
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
+ }
1050
1355
  function handleAdComplete() {
1051
1356
  console.log("[HlsAdPlayer] Handling ad completion");
1052
1357
  adPlaying = false;
@@ -1100,7 +1405,7 @@ function createHlsAdPlayer(contentVideo, options) {
1100
1405
  },
1101
1406
  requestAds: function requestAds(vastTagUrl) {
1102
1407
  return _async_to_generator(function() {
1103
- var ad, error;
1408
+ var ads, preloaded, error;
1104
1409
  return _ts_generator(this, function(_state) {
1105
1410
  switch(_state.label){
1106
1411
  case 0:
@@ -1121,12 +1426,18 @@ function createHlsAdPlayer(contentVideo, options) {
1121
1426
  6
1122
1427
  ]);
1123
1428
  sessionId = generateSessionId();
1429
+ ads = [];
1124
1430
  if (!preloadedAds.has(vastTagUrl)) return [
1125
1431
  3,
1126
1432
  2
1127
1433
  ];
1128
- ad = preloadedAds.get(vastTagUrl);
1434
+ preloaded = preloadedAds.get(vastTagUrl);
1129
1435
  preloadedAds.delete(vastTagUrl);
1436
+ if (preloaded) {
1437
+ ads = [
1438
+ preloaded
1439
+ ];
1440
+ }
1130
1441
  console.log("[HlsAdPlayer] Using preloaded VAST response:", vastTagUrl);
1131
1442
  return [
1132
1443
  3,
@@ -1135,13 +1446,13 @@ function createHlsAdPlayer(contentVideo, options) {
1135
1446
  case 2:
1136
1447
  return [
1137
1448
  4,
1138
- fetchAndParseVastAd(vastTagUrl)
1449
+ fetchAndParsePod(vastTagUrl)
1139
1450
  ];
1140
1451
  case 3:
1141
- ad = _state.sent();
1452
+ ads = _state.sent();
1142
1453
  _state.label = 4;
1143
1454
  case 4:
1144
- if (!ad) {
1455
+ if (!ads.length) {
1145
1456
  console.warn("[HlsAdPlayer] No ads available from VAST response");
1146
1457
  emit("ad_error");
1147
1458
  return [
@@ -1149,10 +1460,14 @@ function createHlsAdPlayer(contentVideo, options) {
1149
1460
  Promise.reject(new Error("No ads available from VAST response"))
1150
1461
  ];
1151
1462
  }
1152
- currentAd = ad;
1153
- console.log("[HlsAdPlayer] Ad parsed: ".concat(ad.title, ", duration: ").concat(ad.duration, "s"));
1154
- fireTrackingPixels(ad.trackingUrls.impression);
1155
- 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
+ }
1156
1471
  return [
1157
1472
  2,
1158
1473
  Promise.resolve()
@@ -1175,7 +1490,7 @@ function createHlsAdPlayer(contentVideo, options) {
1175
1490
  },
1176
1491
  play: function play() {
1177
1492
  return _async_to_generator(function() {
1178
- var contentVolume, mediaFile, isHlsAd;
1493
+ var contentVolume;
1179
1494
  return _ts_generator(this, function(_state) {
1180
1495
  if (!currentAd) {
1181
1496
  console.warn("[HlsAdPlayer] Cannot play: No ad loaded (no ads available)");
@@ -1191,14 +1506,7 @@ function createHlsAdPlayer(contentVideo, options) {
1191
1506
  adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.appendChild(adVideoElement);
1192
1507
  setupAdEventListeners();
1193
1508
  }
1194
- trackingFired = {
1195
- impression: trackingFired.impression,
1196
- start: false,
1197
- firstQuartile: false,
1198
- midpoint: false,
1199
- thirdQuartile: false,
1200
- complete: false
1201
- };
1509
+ resetQuartileTracking(true);
1202
1510
  contentVolume = contentVideo.volume;
1203
1511
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1204
1512
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
@@ -1216,60 +1524,12 @@ function createHlsAdPlayer(contentVideo, options) {
1216
1524
  adVideoElement.muted = originalMutedState;
1217
1525
  console.log("[HlsAdPlayer] Set ad video volume to ".concat(adVideoElement.volume, ", muted: ").concat(adVideoElement.muted, ", originalMutedState: ").concat(originalMutedState, ", contentVolume: ").concat(contentVolume));
1218
1526
  }
1219
- mediaFile = selectBestMediaFile(currentAd.mediaFiles);
1220
- if (!mediaFile) {
1221
- throw new Error("No media file available for ad");
1222
- }
1223
1527
  if (adContainerEl) {
1224
1528
  adContainerEl.style.display = "flex";
1225
1529
  adContainerEl.style.pointerEvents = "auto";
1226
1530
  }
1227
1531
  emit("content_pause");
1228
- isHlsAd = isHlsMediaFile(mediaFile);
1229
- console.log("[HlsAdPlayer] Loading ".concat(isHlsAd ? "HLS" : "progressive", " ad from: ").concat(mediaFile.url));
1230
- if (isHlsAd && import_hls.default.isSupported()) {
1231
- if (adHls) {
1232
- adHls.destroy();
1233
- }
1234
- adHls = new import_hls.default({
1235
- enableWorker: true,
1236
- lowLatencyMode: false
1237
- });
1238
- adHls.loadSource(mediaFile.url);
1239
- adHls.attachMedia(adVideoElement);
1240
- adHls.on(import_hls.default.Events.MANIFEST_PARSED, function() {
1241
- console.log("[HlsAdPlayer] HLS manifest parsed, starting playback");
1242
- adVideoElement.play().catch(function(error) {
1243
- console.error("[HlsAdPlayer] Error starting ad playback:", error);
1244
- handleAdError();
1245
- });
1246
- });
1247
- adHls.on(import_hls.default.Events.ERROR, function(event, data) {
1248
- console.error("[HlsAdPlayer] HLS error:", data);
1249
- if (data.fatal) {
1250
- handleAdError();
1251
- }
1252
- });
1253
- } else if (isHlsAd && adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1254
- adVideoElement.src = mediaFile.url;
1255
- adVideoElement.play().catch(function(error) {
1256
- console.error("[HlsAdPlayer] Error starting ad playback:", error);
1257
- handleAdError();
1258
- });
1259
- } else if (!isHlsAd && isProgressiveMediaFile(mediaFile)) {
1260
- if (adHls) {
1261
- adHls.destroy();
1262
- adHls = void 0;
1263
- }
1264
- adVideoElement.src = mediaFile.url;
1265
- adVideoElement.load();
1266
- adVideoElement.play().catch(function(error) {
1267
- console.error("[HlsAdPlayer] Error starting progressive ad playback:", error);
1268
- handleAdError();
1269
- });
1270
- } else {
1271
- throw new Error("Unsupported ad media file type: ".concat(mediaFile.type));
1272
- }
1532
+ loadCurrentAdMedia();
1273
1533
  return [
1274
1534
  2,
1275
1535
  Promise.resolve()
@@ -1344,6 +1604,8 @@ function createHlsAdPlayer(contentVideo, options) {
1344
1604
  adVideoElement.src = "";
1345
1605
  }
1346
1606
  currentAd = void 0;
1607
+ podAds = [];
1608
+ podIndex = 0;
1347
1609
  return [
1348
1610
  2
1349
1611
  ];
@@ -1393,6 +1655,8 @@ function createHlsAdPlayer(contentVideo, options) {
1393
1655
  }
1394
1656
  adContainerEl = void 0;
1395
1657
  currentAd = void 0;
1658
+ podAds = [];
1659
+ podIndex = 0;
1396
1660
  listeners.clear();
1397
1661
  preloadedAds.clear();
1398
1662
  preloadingAds.clear();
@@ -1484,6 +1748,14 @@ function createHlsAdPlayer(contentVideo, options) {
1484
1748
  },
1485
1749
  getLoadedAdId: function getLoadedAdId() {
1486
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);
1487
1759
  }
1488
1760
  };
1489
1761
  }
@@ -1887,38 +2159,108 @@ function getOrCreateStoredUuid(storageKey) {
1887
2159
  if (typeof window === "undefined") {
1888
2160
  return void 0;
1889
2161
  }
1890
- var existing = readStoredString(storageKey);
2162
+ var existing = readPersistentId(storageKey);
1891
2163
  if (existing) {
2164
+ writePersistentId(storageKey, existing);
1892
2165
  return existing;
1893
2166
  }
1894
2167
  var id = createUuid();
1895
- try {
1896
- var _window_localStorage;
1897
- (_window_localStorage = window.localStorage) === null || _window_localStorage === void 0 ? void 0 : _window_localStorage.setItem(storageKey, id);
1898
- } catch (unused) {}
2168
+ writePersistentId(storageKey, id);
1899
2169
  return id;
1900
2170
  }
1901
2171
  function getOrCreateCtvSessionId() {
1902
- var existing = readStoredString(CTV_SESSION_STORAGE_KEY);
2172
+ var existing = readPersistentId(CTV_SESSION_STORAGE_KEY);
1903
2173
  if (existing) {
2174
+ writePersistentId(CTV_SESSION_STORAGE_KEY, existing);
1904
2175
  return existing;
1905
2176
  }
1906
2177
  var sessionId = createUuid();
2178
+ writePersistentId(CTV_SESSION_STORAGE_KEY, sessionId);
2179
+ return sessionId;
2180
+ }
2181
+ function readPersistentId(storageKey) {
2182
+ return readStoredString(storageKey) || readCookie(storageKey);
2183
+ }
2184
+ function writePersistentId(storageKey, value) {
1907
2185
  try {
1908
2186
  var _window_localStorage;
1909
- (_window_localStorage = window.localStorage) === null || _window_localStorage === void 0 ? void 0 : _window_localStorage.setItem(CTV_SESSION_STORAGE_KEY, sessionId);
2187
+ (_window_localStorage = window.localStorage) === null || _window_localStorage === void 0 ? void 0 : _window_localStorage.setItem(storageKey, value);
2188
+ } catch (unused) {}
2189
+ writeCookie(storageKey, value);
2190
+ }
2191
+ var PERSISTENT_ID_COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5;
2192
+ function readCookie(name) {
2193
+ if (typeof document === "undefined") {
2194
+ return void 0;
2195
+ }
2196
+ try {
2197
+ var prefix = encodeURIComponent(name) + "=";
2198
+ var parts = document.cookie ? document.cookie.split(";") : [];
2199
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2200
+ try {
2201
+ for(var _iterator = parts[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
2202
+ var part = _step.value;
2203
+ var entry = part.trim();
2204
+ if (entry.startsWith(prefix)) {
2205
+ return decodeURIComponent(entry.slice(prefix.length)) || void 0;
2206
+ }
2207
+ }
2208
+ } catch (err) {
2209
+ _didIteratorError = true;
2210
+ _iteratorError = err;
2211
+ } finally{
2212
+ try {
2213
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
2214
+ _iterator.return();
2215
+ }
2216
+ } finally{
2217
+ if (_didIteratorError) {
2218
+ throw _iteratorError;
2219
+ }
2220
+ }
2221
+ }
2222
+ } catch (unused) {}
2223
+ return void 0;
2224
+ }
2225
+ function writeCookie(name, value) {
2226
+ if (typeof document === "undefined") {
2227
+ return;
2228
+ }
2229
+ try {
2230
+ document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + ";path=/;max-age=" + PERSISTENT_ID_COOKIE_MAX_AGE + ";SameSite=Lax";
1910
2231
  } catch (unused) {}
1911
- return sessionId;
1912
2232
  }
1913
2233
  function createUuid() {
2234
+ var _bytes_, _bytes_1;
1914
2235
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1915
2236
  return crypto.randomUUID();
1916
2237
  }
1917
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(char) {
1918
- var value = Math.floor(Math.random() * 16);
1919
- var nibble = char === "x" ? value : value & 3 | 8;
1920
- return nibble.toString(16);
1921
- });
2238
+ var bytes = new Uint8Array(16);
2239
+ if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
2240
+ crypto.getRandomValues(bytes);
2241
+ } else {
2242
+ var seed = Date.now() ^ Math.floor(performanceNow() * 1e3);
2243
+ for(var i = 0; i < 16; i++){
2244
+ seed = seed * 1103515245 + 12345 & 2147483647;
2245
+ bytes[i] = (seed ^ Math.floor(Math.random() * 256)) & 255;
2246
+ }
2247
+ }
2248
+ bytes[6] = ((_bytes_ = bytes[6]) !== null && _bytes_ !== void 0 ? _bytes_ : 0) & 15 | 64;
2249
+ bytes[8] = ((_bytes_1 = bytes[8]) !== null && _bytes_1 !== void 0 ? _bytes_1 : 0) & 63 | 128;
2250
+ var hex = "";
2251
+ for(var i1 = 0; i1 < 16; i1++){
2252
+ var _bytes_i;
2253
+ hex += ((_bytes_i = bytes[i1]) !== null && _bytes_i !== void 0 ? _bytes_i : 0).toString(16).padStart(2, "0");
2254
+ }
2255
+ return hex.slice(0, 8) + "-" + hex.slice(8, 12) + "-" + hex.slice(12, 16) + "-" + hex.slice(16, 20) + "-" + hex.slice(20, 32);
2256
+ }
2257
+ function performanceNow() {
2258
+ try {
2259
+ if (typeof performance !== "undefined" && typeof performance.now === "function") {
2260
+ return performance.now();
2261
+ }
2262
+ } catch (unused) {}
2263
+ return 0;
1922
2264
  }
1923
2265
  function readPlatformNativeDeviceId() {
1924
2266
  if (typeof window === "undefined") return {};
@@ -3336,7 +3678,20 @@ function applyVastMacros(baseUrl, ctx) {
3336
3678
  if (ctx.pod != null) {
3337
3679
  params.set("pod", String(ctx.pod));
3338
3680
  }
3339
- if (ctx.adPosition != null) {
3681
+ var optimizedPodMode = ctx.podMaxAds != null || ctx.podMinDurationMs != null || ctx.podMaxDurationMs != null;
3682
+ if (optimizedPodMode) {
3683
+ params.delete("ppos");
3684
+ params.delete("lip");
3685
+ if (ctx.podMinDurationMs != null) {
3686
+ params.set("pmnd", String(Math.max(0, Math.round(ctx.podMinDurationMs))));
3687
+ }
3688
+ if (ctx.podMaxDurationMs != null) {
3689
+ params.set("pmxd", String(Math.max(0, Math.round(ctx.podMaxDurationMs))));
3690
+ }
3691
+ if (ctx.podMaxAds != null) {
3692
+ params.set("pmad", String(Math.max(1, Math.round(ctx.podMaxAds))));
3693
+ }
3694
+ } else if (ctx.adPosition != null) {
3340
3695
  params.set("ppos", String(ctx.adPosition));
3341
3696
  }
3342
3697
  var requestUrl = ctx.contentUrl || ctx.pageUrl;
@@ -4701,6 +5056,7 @@ var AdConfigManager = /*#__PURE__*/ function() {
4701
5056
  this.podAssignedByPrefetch = false;
4702
5057
  this.adRequestPositionInBreak = 0;
4703
5058
  this.lastHeartbeatTime = 0;
5059
+ this.defaultPodMaxAds = 6;
4704
5060
  this.config = config;
4705
5061
  this.video = video;
4706
5062
  }
@@ -5156,6 +5512,52 @@ var AdConfigManager = /*#__PURE__*/ function() {
5156
5512
  return urls;
5157
5513
  }
5158
5514
  },
5515
+ {
5516
+ key: "resolvePodParams",
5517
+ value: function resolvePodParams(breakDurationMs) {
5518
+ var maxDurationMs = breakDurationMs != null && breakDurationMs > 0 ? breakDurationMs : this.config.podMaxDurationMs;
5519
+ var maxAds = this.config.podMaxAds && this.config.podMaxAds > 0 ? this.config.podMaxAds : this.apiNumberAds && this.apiNumberAds > 0 ? this.apiNumberAds : this.defaultPodMaxAds;
5520
+ var minDurationMs = this.config.podExactDuration && maxDurationMs != null ? maxDurationMs : 0;
5521
+ return {
5522
+ maxAds: maxAds,
5523
+ minDurationMs: minDurationMs,
5524
+ maxDurationMs: maxDurationMs
5525
+ };
5526
+ }
5527
+ },
5528
+ {
5529
+ key: "generatePodVastUrl",
5530
+ value: function generatePodVastUrl(baseUrl, podParams, adPlayer, palNonce, inAdBreak) {
5531
+ var _podParams_maxDurationMs;
5532
+ var adWillPlayMuted = inAdBreak ? adPlayer.getOriginalMutedState() : this.video.muted;
5533
+ var envSignals = resolveVastEnvironmentSignals(!!this.config.ctvAdRequest);
5534
+ var urlWithMacros = applyVastMacros(baseUrl, {
5535
+ correlator: generateCorrelator(),
5536
+ streamCorrelator: this.streamCorrelator,
5537
+ pod: this.podCounter > 0 ? this.podCounter : void 0,
5538
+ podMaxAds: podParams.maxAds,
5539
+ podMinDurationMs: podParams.minDurationMs,
5540
+ podMaxDurationMs: podParams.maxDurationMs,
5541
+ isCtv: envSignals.isCtv,
5542
+ contentUrl: envSignals.contentUrl,
5543
+ pageUrl: !envSignals.isCtv && typeof window !== "undefined" ? window.location.href : void 0,
5544
+ adWillPlayMuted: adWillPlayMuted,
5545
+ adWillAutoPlay: !!this.config.autoplay,
5546
+ appId: envSignals.appId,
5547
+ appName: envSignals.appName,
5548
+ sessionId: envSignals.sessionId,
5549
+ deviceId: envSignals.deviceId,
5550
+ deviceIdType: envSignals.deviceIdType,
5551
+ limitAdTracking: envSignals.limitAdTracking,
5552
+ deviceTypeHint: envSignals.deviceTypeHint,
5553
+ adTest: this.config.adTest,
5554
+ consent: this.consentSignals
5555
+ });
5556
+ var finalUrl = palNonce.injectNonce(urlWithMacros);
5557
+ 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);
5558
+ return finalUrl;
5559
+ }
5560
+ },
5159
5561
  {
5160
5562
  // --- Tracking ---
5161
5563
  key: "initializeTracking",
@@ -5461,6 +5863,13 @@ var AdTimingService = /*#__PURE__*/ function() {
5461
5863
  this.lastAdRequestTime = Date.now();
5462
5864
  }
5463
5865
  },
5866
+ {
5867
+ key: "notePodAdStarted",
5868
+ value: function notePodAdStarted() {
5869
+ this.currentAdIndex++;
5870
+ this.totalAdsInBreak = this.currentAdIndex;
5871
+ }
5872
+ },
5464
5873
  {
5465
5874
  key: "getRemainingAdMs",
5466
5875
  value: function getRemainingAdMs() {
@@ -6969,13 +7378,26 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
6969
7378
  }
6970
7379
  },
6971
7380
  {
6972
- // ───────────────────────────────────────────────────────────────
7381
+ key: "optimizedPodsEnabled",
7382
+ get: // ───────────────────────────────────────────────────────────────
6973
7383
  // IMA event listeners
6974
7384
  // ───────────────────────────────────────────────────────────────
7385
+ function get() {
7386
+ return !!this.host.config.optimizedPods;
7387
+ }
7388
+ },
7389
+ {
6975
7390
  key: "attachImaEventListeners",
6976
7391
  value: function attachImaEventListeners() {
6977
7392
  var _this = this;
6978
7393
  var adPlayer = this.host.getAdPlayer();
7394
+ adPlayer.on("pod_ad_started", function(payload) {
7395
+ _this.timing.notePodAdStarted();
7396
+ if (_this.debug) {
7397
+ var _ref;
7398
+ 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 : "?"));
7399
+ }
7400
+ });
6979
7401
  adPlayer.on("all_ads_completed", function() {
6980
7402
  sendAdImpressionTracking(_this.host.config.licenseKey, {
6981
7403
  source: "hls",
@@ -7133,7 +7555,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7133
7555
  // ───────────────────────────────────────────────────────────────
7134
7556
  function handleAdStart(_marker) {
7135
7557
  return _async_to_generator(function() {
7136
- var scheduled, tags, baseVastUrl, adBreakDurationMs, mode, currentMuted, currentVolume, firstAdUrl, usePreloadedAd, preloadedController, preloaded, firstAdUrlArray, adPlayer, preservedMuted, preservedVolume, adVolume, adPlayer1, adVolume1, error, fallbackPreloaded, ap, adVolume2, fallbackError;
7558
+ 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;
7137
7559
  return _ts_generator(this, function(_state) {
7138
7560
  switch(_state.label){
7139
7561
  case 0:
@@ -7180,6 +7602,26 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7180
7602
  if (this.timing.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
7181
7603
  this.timing.expectedAdBreakDurationMs = adBreakDurationMs;
7182
7604
  }
7605
+ if (!this.optimizedPodsEnabled) return [
7606
+ 3,
7607
+ 2
7608
+ ];
7609
+ return [
7610
+ 4,
7611
+ this.startOptimizedPod(baseVastUrl, adBreakDurationMs, currentMuted, currentVolume)
7612
+ ];
7613
+ case 1:
7614
+ podPlayed = _state.sent();
7615
+ if (podPlayed) {
7616
+ return [
7617
+ 2
7618
+ ];
7619
+ }
7620
+ if (this.debug) {
7621
+ console.log("[POD] Optimized pod unavailable, falling back to sequential single-ad path");
7622
+ }
7623
+ _state.label = 2;
7624
+ case 2:
7183
7625
  usePreloadedAd = false;
7184
7626
  preloaded = this.preloadPool.getPreloadedAd();
7185
7627
  if (preloaded) {
@@ -7221,21 +7663,21 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7221
7663
  if (this.debug) {
7222
7664
  console.log("[CONTINUOUS-FETCH] First VAST URL:", firstAdUrl);
7223
7665
  }
7224
- _state.label = 1;
7225
- case 1:
7666
+ _state.label = 3;
7667
+ case 3:
7226
7668
  _state.trys.push([
7227
- 1,
7228
- 12,
7669
+ 3,
7670
+ 14,
7229
7671
  ,
7230
- 20
7672
+ 22
7231
7673
  ]);
7232
7674
  if (!(usePreloadedAd && preloadedController)) return [
7233
7675
  3,
7234
- 5
7676
+ 7
7235
7677
  ];
7236
7678
  if (!!this.timing.ensureLoadedAdFitsBudget(preloadedController)) return [
7237
7679
  3,
7238
- 3
7680
+ 5
7239
7681
  ];
7240
7682
  this.timing.rejectLoadedAdForDuration(preloadedController);
7241
7683
  this.startContinuousFetching(baseVastUrl);
@@ -7243,12 +7685,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7243
7685
  4,
7244
7686
  this.tryNextAvailableAdWithRateLimit()
7245
7687
  ];
7246
- case 2:
7688
+ case 4:
7247
7689
  _state.sent();
7248
7690
  return [
7249
7691
  2
7250
7692
  ];
7251
- case 3:
7693
+ case 5:
7252
7694
  adPlayer = this.host.getAdPlayer();
7253
7695
  preservedMuted = adPlayer.getOriginalMutedState();
7254
7696
  preservedVolume = adPlayer.getOriginalVolume();
@@ -7273,7 +7715,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7273
7715
  4,
7274
7716
  preloadedController.play()
7275
7717
  ];
7276
- case 4:
7718
+ case 6:
7277
7719
  _state.sent();
7278
7720
  this.timing.markAdStarted();
7279
7721
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7283,22 +7725,22 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7283
7725
  preloadedController.setAdVolume(adVolume);
7284
7726
  return [
7285
7727
  3,
7286
- 11
7728
+ 13
7287
7729
  ];
7288
- case 5:
7730
+ case 7:
7289
7731
  adPlayer1 = this.host.getAdPlayer();
7290
7732
  return [
7291
7733
  4,
7292
7734
  this.timing.enforceGlobalRateLimit()
7293
7735
  ];
7294
- case 6:
7736
+ case 8:
7295
7737
  _state.sent();
7296
7738
  this.timing.lastAdRequestTime = Date.now();
7297
7739
  return [
7298
7740
  4,
7299
7741
  adPlayer1.requestAds(firstAdUrl)
7300
7742
  ];
7301
- case 7:
7743
+ case 9:
7302
7744
  _state.sent();
7303
7745
  sendAdLoadedTracking(this.host.config.licenseKey, {
7304
7746
  source: "hls",
@@ -7310,7 +7752,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7310
7752
  }
7311
7753
  if (!!this.timing.ensureLoadedAdFitsBudget(adPlayer1)) return [
7312
7754
  3,
7313
- 9
7755
+ 11
7314
7756
  ];
7315
7757
  this.timing.rejectLoadedAdForDuration(adPlayer1);
7316
7758
  this.recreateAdController();
@@ -7319,12 +7761,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7319
7761
  4,
7320
7762
  this.tryNextAvailableAdWithRateLimit()
7321
7763
  ];
7322
- case 8:
7764
+ case 10:
7323
7765
  _state.sent();
7324
7766
  return [
7325
7767
  2
7326
7768
  ];
7327
- case 9:
7769
+ case 11:
7328
7770
  this.timing.consecutiveFailures = 0;
7329
7771
  this.startContinuousFetching(baseVastUrl);
7330
7772
  if (!this.preloadPool.active) {
@@ -7334,7 +7776,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7334
7776
  4,
7335
7777
  adPlayer1.play()
7336
7778
  ];
7337
- case 10:
7779
+ case 12:
7338
7780
  _state.sent();
7339
7781
  this.timing.markAdStarted();
7340
7782
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7342,40 +7784,40 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7342
7784
  }
7343
7785
  adVolume1 = currentMuted ? 0 : currentVolume;
7344
7786
  adPlayer1.setAdVolume(adVolume1);
7345
- _state.label = 11;
7346
- case 11:
7787
+ _state.label = 13;
7788
+ case 13:
7347
7789
  return [
7348
7790
  3,
7349
- 20
7791
+ 22
7350
7792
  ];
7351
- case 12:
7793
+ case 14:
7352
7794
  error = _state.sent();
7353
7795
  if (this.debug) {
7354
7796
  console.warn("[CONTINUOUS-FETCH] First ad request failed:", error);
7355
7797
  }
7356
7798
  if (!!usePreloadedAd) return [
7357
7799
  3,
7358
- 18
7800
+ 20
7359
7801
  ];
7360
7802
  fallbackPreloaded = this.preloadPool.getPreloadedAd();
7361
7803
  if (!fallbackPreloaded) return [
7362
7804
  3,
7363
- 18
7805
+ 20
7364
7806
  ];
7365
7807
  if (this.debug) {
7366
7808
  console.log("[CONTINUOUS-FETCH] First ad failed, using preloaded fallback");
7367
7809
  }
7368
- _state.label = 13;
7369
- case 13:
7810
+ _state.label = 15;
7811
+ case 15:
7370
7812
  _state.trys.push([
7371
- 13,
7372
- 17,
7813
+ 15,
7814
+ 19,
7373
7815
  ,
7374
- 18
7816
+ 20
7375
7817
  ]);
7376
7818
  if (!!this.timing.ensureLoadedAdFitsBudget(fallbackPreloaded.adController)) return [
7377
7819
  3,
7378
- 15
7820
+ 17
7379
7821
  ];
7380
7822
  this.timing.rejectLoadedAdForDuration(fallbackPreloaded.adController);
7381
7823
  this.startContinuousFetching(baseVastUrl);
@@ -7383,12 +7825,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7383
7825
  4,
7384
7826
  this.tryNextAvailableAdWithRateLimit()
7385
7827
  ];
7386
- case 14:
7828
+ case 16:
7387
7829
  _state.sent();
7388
7830
  return [
7389
7831
  2
7390
7832
  ];
7391
- case 15:
7833
+ case 17:
7392
7834
  this.swapToPreloadedAdPlayer(fallbackPreloaded.adController);
7393
7835
  this.timing.consecutiveFailures = 0;
7394
7836
  this.startContinuousFetching(baseVastUrl);
@@ -7401,7 +7843,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7401
7843
  4,
7402
7844
  ap.play()
7403
7845
  ];
7404
- case 16:
7846
+ case 18:
7405
7847
  _state.sent();
7406
7848
  this.timing.markAdStarted();
7407
7849
  if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
@@ -7412,16 +7854,16 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7412
7854
  return [
7413
7855
  2
7414
7856
  ];
7415
- case 17:
7857
+ case 19:
7416
7858
  fallbackError = _state.sent();
7417
7859
  if (this.debug) {
7418
7860
  console.warn("[CONTINUOUS-FETCH] Preloaded fallback also failed:", fallbackError);
7419
7861
  }
7420
7862
  return [
7421
7863
  3,
7422
- 18
7864
+ 20
7423
7865
  ];
7424
- case 18:
7866
+ case 20:
7425
7867
  if (this.timing.isTemporaryAdError(error)) {
7426
7868
  this.timing.temporaryFailureUrls.set(firstAdUrl, Date.now());
7427
7869
  if (this.debug) {
@@ -7439,13 +7881,137 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7439
7881
  4,
7440
7882
  this.tryNextAvailableAdWithRateLimit()
7441
7883
  ];
7442
- case 19:
7884
+ case 21:
7443
7885
  _state.sent();
7444
7886
  return [
7445
7887
  3,
7446
- 20
7888
+ 22
7447
7889
  ];
7448
- case 20:
7890
+ case 22:
7891
+ return [
7892
+ 2
7893
+ ];
7894
+ }
7895
+ });
7896
+ }).call(this);
7897
+ }
7898
+ },
7899
+ {
7900
+ key: "startOptimizedPod",
7901
+ value: // ───────────────────────────────────────────────────────────────
7902
+ // Optimized pod (single multi-ad request)
7903
+ // ───────────────────────────────────────────────────────────────
7904
+ function startOptimizedPod(baseVastUrl, breakDurationMs, currentMuted, currentVolume) {
7905
+ return _async_to_generator(function() {
7906
+ var podUrl, adPlayer, requestToken, _ref, _adPlayer_getPodAdCount, podCount, error;
7907
+ return _ts_generator(this, function(_state) {
7908
+ switch(_state.label){
7909
+ case 0:
7910
+ if (this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0) {
7911
+ podUrl = this.pendingAdBreak.vastUrls[0];
7912
+ this.clearPendingAdBreak();
7913
+ } else {
7914
+ podUrl = this.host.generatePodVastUrl(baseVastUrl, breakDurationMs);
7915
+ }
7916
+ if (!podUrl) {
7917
+ return [
7918
+ 2,
7919
+ false
7920
+ ];
7921
+ }
7922
+ this.continuousFetchingActive = false;
7923
+ this.recreateAdController();
7924
+ adPlayer = this.host.getAdPlayer();
7925
+ requestToken = ++this.timing.adRequestTokenCounter;
7926
+ this.timing.activeAdRequestToken = requestToken;
7927
+ this.timing.startAdRequestWatchdog(requestToken);
7928
+ _state.label = 1;
7929
+ case 1:
7930
+ _state.trys.push([
7931
+ 1,
7932
+ 5,
7933
+ ,
7934
+ 6
7935
+ ]);
7936
+ return [
7937
+ 4,
7938
+ this.timing.enforceGlobalRateLimit()
7939
+ ];
7940
+ case 2:
7941
+ _state.sent();
7942
+ this.timing.lastAdRequestTime = Date.now();
7943
+ return [
7944
+ 4,
7945
+ adPlayer.requestAds(podUrl)
7946
+ ];
7947
+ case 3:
7948
+ _state.sent();
7949
+ this.timing.clearAdRequestWatchdog();
7950
+ if (this.timing.activeAdRequestToken !== requestToken) {
7951
+ return [
7952
+ 2,
7953
+ true
7954
+ ];
7955
+ }
7956
+ podCount = (_ref = (_adPlayer_getPodAdCount = adPlayer.getPodAdCount) === null || _adPlayer_getPodAdCount === void 0 ? void 0 : _adPlayer_getPodAdCount.call(adPlayer)) !== null && _ref !== void 0 ? _ref : 1;
7957
+ if (podCount < 1) {
7958
+ return [
7959
+ 2,
7960
+ false
7961
+ ];
7962
+ }
7963
+ if (this.debug) {
7964
+ 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"));
7965
+ }
7966
+ sendAdLoadedTracking(this.host.config.licenseKey, {
7967
+ source: "hls",
7968
+ vastUrl: podUrl,
7969
+ timestamp: /* @__PURE__ */ new Date().toISOString()
7970
+ }, this.analyticsContext).catch(function() {});
7971
+ this.timing.startAdFailsafeTimer(requestToken);
7972
+ return [
7973
+ 4,
7974
+ adPlayer.play()
7975
+ ];
7976
+ case 4:
7977
+ _state.sent();
7978
+ this.timing.markAdStarted();
7979
+ if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
7980
+ this.timing.scheduleAdStopAtBreakBoundary();
7981
+ }
7982
+ adPlayer.setAdVolume(currentMuted ? 0 : currentVolume);
7983
+ this.timing.consecutiveFailures = 0;
7984
+ if (podCount === 1) {
7985
+ this.continuousFetchingActive = true;
7986
+ this.startContinuousFetching(baseVastUrl);
7987
+ if (!this.preloadPool.active) {
7988
+ this.preloadPool.startPreloadPool(baseVastUrl, []);
7989
+ }
7990
+ }
7991
+ return [
7992
+ 2,
7993
+ true
7994
+ ];
7995
+ case 5:
7996
+ error = _state.sent();
7997
+ this.timing.clearAdRequestWatchdog();
7998
+ this.timing.clearAdFailsafeTimer();
7999
+ if (this.timing.activeAdRequestToken === requestToken) {
8000
+ this.timing.activeAdRequestToken = null;
8001
+ }
8002
+ if (this.debug) {
8003
+ console.warn("[POD] Optimized pod request/play failed:", error);
8004
+ }
8005
+ if (this.timing.isTemporaryAdError(error)) {
8006
+ this.timing.temporaryFailureUrls.set(podUrl, Date.now());
8007
+ }
8008
+ this.continuousFetchingActive = true;
8009
+ this.recreateAdController();
8010
+ return [
8011
+ 2,
8012
+ false
8013
+ ];
8014
+ case 6:
7449
8015
  return [
7450
8016
  2
7451
8017
  ];
@@ -8527,6 +9093,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8527
9093
  },
8528
9094
  generateVastUrls: function generateVastUrls(base, count) {
8529
9095
  return _this.generateVastUrls(base, count);
9096
+ },
9097
+ generatePodVastUrl: function generatePodVastUrl(base, breakDurationMs) {
9098
+ return _this.generatePodVastUrl(base, breakDurationMs);
8530
9099
  }
8531
9100
  }, this.timing, this.preloadPool, this.adConfig, this.cueManager, this.placeholder);
8532
9101
  }
@@ -8559,6 +9128,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8559
9128
  return this.adConfig.generateVastUrlsWithCorrelators(baseUrl, count, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
8560
9129
  }
8561
9130
  },
9131
+ {
9132
+ key: "generatePodVastUrl",
9133
+ value: function generatePodVastUrl(baseUrl, breakDurationMs) {
9134
+ var podParams = this.adConfig.resolvePodParams(breakDurationMs);
9135
+ return this.adConfig.generatePodVastUrl(baseUrl, podParams, this.adPlayer, this.palNonce, this.adBreak.inAdBreak);
9136
+ }
9137
+ },
8562
9138
  {
8563
9139
  key: "load",
8564
9140
  value: // ───────────────────────────────────────────────────────────────
@@ -8947,7 +9523,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8947
9523
  }
8948
9524
  this.adConfig.beginNewAdPod();
8949
9525
  this.adConfig.podAssignedByPrefetch = true;
8950
- var generatedUrls = this.generateVastUrls(baseVastUrl, 1);
9526
+ var optimizedPods = !!this.config.optimizedPods;
9527
+ var breakDurationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : void 0;
9528
+ var generatedUrls = optimizedPods ? [
9529
+ this.generatePodVastUrl(baseVastUrl, breakDurationMs)
9530
+ ] : this.generateVastUrls(baseVastUrl, 1);
8951
9531
  this.adBreak.pendingAdBreak = _object_spread_props(_object_spread({
8952
9532
  marker: marker
8953
9533
  }, fragmentSn !== void 0 ? {
@@ -8969,7 +9549,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8969
9549
  }, detectPayload), buildAnalyticsContext(this.config)).catch(function() {});
8970
9550
  if (this.debug) {
8971
9551
  console.log("[PREFETCH] Starting ad prefetch for upcoming ad break");
8972
- console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
9552
+ console.log("[PREFETCH] Pre-generated ".concat(generatedUrls.length, " ").concat(optimizedPods ? "pod" : "VAST", " URL(s)"));
9553
+ }
9554
+ if (optimizedPods) {
9555
+ if (this.debug) {
9556
+ console.log("[PREFETCH] Optimized pods enabled - skipping single-ad preload pool");
9557
+ }
9558
+ return;
8973
9559
  }
8974
9560
  this.preloadPool.startPreloadPool(baseVastUrl, generatedUrls.slice(0, 1), false);
8975
9561
  }