stormcloud-video-player 0.8.59 → 0.8.60

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 (79) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1369 -1369
  3. package/dist/stormcloud-vp.min.js +3 -1
  4. package/lib/Player.cjs +548 -0
  5. package/lib/Player.cjs.map +1 -0
  6. package/lib/Player.d.cts +146 -0
  7. package/lib/{index.js → StormcloudPlayer.cjs} +1108 -1877
  8. package/lib/StormcloudPlayer.cjs.map +1 -0
  9. package/lib/{index.d.ts → StormcloudPlayer.d.cts} +7 -400
  10. package/lib/index.cjs +979 -251
  11. package/lib/index.cjs.map +1 -1
  12. package/lib/index.d.cts +18 -4032
  13. package/lib/patterns.cjs +111 -0
  14. package/lib/patterns.cjs.map +1 -0
  15. package/lib/patterns.d.cts +14 -0
  16. package/lib/player/AdBreakOrchestrator.cjs +48 -14
  17. package/lib/player/AdBreakOrchestrator.cjs.map +1 -1
  18. package/lib/player/AdBreakOrchestrator.d.cts +3 -1
  19. package/lib/player/AdConfigManager.cjs +155 -17
  20. package/lib/player/AdConfigManager.cjs.map +1 -1
  21. package/lib/player/AdConfigManager.d.cts +1 -1
  22. package/lib/player/AdTimingService.cjs.map +1 -1
  23. package/lib/player/AdTimingService.d.cts +1 -1
  24. package/lib/player/HlsEngine.cjs.map +1 -1
  25. package/lib/player/HlsEngine.d.cts +1 -1
  26. package/lib/player/PlaceholderLayer.cjs.map +1 -1
  27. package/lib/player/PlayerControls.cjs.map +1 -1
  28. package/lib/player/PlayerControls.d.cts +1 -1
  29. package/lib/player/Scte35CueManager.cjs.map +1 -1
  30. package/lib/player/Scte35CueManager.d.cts +1 -1
  31. package/lib/player/Scte35Parser.cjs.map +1 -1
  32. package/lib/player/Scte35Parser.d.cts +1 -1
  33. package/lib/player/StormcloudVideoPlayer.cjs +979 -251
  34. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  35. package/lib/player/StormcloudVideoPlayer.d.cts +1 -1
  36. package/lib/player/playerTypes.cjs.map +1 -1
  37. package/lib/player/playerTypes.d.cts +1 -1
  38. package/lib/players/FilePlayer.cjs.map +1 -1
  39. package/lib/players/HlsPlayer.cjs +979 -251
  40. package/lib/players/HlsPlayer.cjs.map +1 -1
  41. package/lib/players/HlsPlayer.d.cts +1 -1
  42. package/lib/players/index.cjs +979 -251
  43. package/lib/players/index.cjs.map +1 -1
  44. package/lib/props.cjs +186 -0
  45. package/lib/props.cjs.map +1 -0
  46. package/lib/props.d.cts +62 -0
  47. package/lib/sdk/hlsAdPlayer.cjs +884 -234
  48. package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
  49. package/lib/sdk/hlsAdPlayer.d.cts +1 -1
  50. package/lib/sdk/pal.cjs.map +1 -1
  51. package/lib/types.cjs +49 -0
  52. package/lib/types.cjs.map +1 -0
  53. package/lib/{types-52ASml2N.d.cts → types.d.cts} +47 -1
  54. package/lib/ui/StormcloudVideoPlayer.cjs +979 -251
  55. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  56. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  57. package/lib/utils/browserCompat.cjs.map +1 -1
  58. package/lib/utils/ctvVastSignals.cjs +155 -17
  59. package/lib/utils/ctvVastSignals.cjs.map +1 -1
  60. package/lib/utils/devUrl.cjs.map +1 -1
  61. package/lib/utils/polyfills.cjs.map +1 -1
  62. package/lib/utils/tracking.cjs +42 -14
  63. package/lib/utils/tracking.cjs.map +1 -1
  64. package/lib/utils/tracking.d.cts +1 -1
  65. package/lib/utils/trackingIngest.cjs.map +1 -1
  66. package/lib/utils/vastEnvironmentSignals.cjs +167 -17
  67. package/lib/utils/vastEnvironmentSignals.cjs.map +1 -1
  68. package/lib/utils/vastEnvironmentSignals.d.cts +12 -1
  69. package/lib/utils/vastMacros.cjs +26 -0
  70. package/lib/utils/vastMacros.cjs.map +1 -1
  71. package/lib/utils/vastTrackingMacros.cjs +5 -0
  72. package/lib/utils/vastTrackingMacros.cjs.map +1 -1
  73. package/lib/utils/vastTrackingMacros.d.cts +2 -0
  74. package/lib/utils.cjs +292 -0
  75. package/lib/utils.cjs.map +1 -0
  76. package/lib/utils.d.cts +17 -0
  77. package/package.json +91 -91
  78. package/rollup.config.js +90 -90
  79. package/lib/index.js.map +0 -1
@@ -565,6 +565,7 @@ var KNOWN_VAST_MACROS = /* @__PURE__ */ new Set([
565
565
  "PLAYERSIZE",
566
566
  "PLAYERSTATE",
567
567
  "PODSEQUENCE",
568
+ "REASON",
568
569
  "REGULATIONS",
569
570
  "SERVERSIDE",
570
571
  "SERVERUA",
@@ -623,6 +624,10 @@ function resolveMacroValues(ctx) {
623
624
  if (ctx.podSequence != null) {
624
625
  set("PODSEQUENCE", String(ctx.podSequence));
625
626
  }
627
+ set("ADSERVINGID", ctx.adServingId);
628
+ if (ctx.reason != null && ctx.reason !== "") {
629
+ set("REASON", String(ctx.reason));
630
+ }
626
631
  return values;
627
632
  }
628
633
  function substituteVastTrackingMacros(url) {
@@ -639,10 +644,72 @@ function substituteVastTrackingMacros(url) {
639
644
  });
640
645
  }
641
646
  // src/sdk/hlsAdPlayer.ts
647
+ var LINEAR_TRACKING_EVENTS = [
648
+ "creativeView",
649
+ "loaded",
650
+ "start",
651
+ "firstQuartile",
652
+ "midpoint",
653
+ "thirdQuartile",
654
+ "complete",
655
+ "mute",
656
+ "unmute",
657
+ "pause",
658
+ "resume",
659
+ "fullscreen",
660
+ "exitFullscreen",
661
+ "playerExpand",
662
+ "playerCollapse",
663
+ "notUsed"
664
+ ];
665
+ function textOf(el) {
666
+ var _el_textContent;
667
+ var value = el === null || el === void 0 ? void 0 : (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
668
+ return value ? value : void 0;
669
+ }
670
+ function parseVastTime(value) {
671
+ if (!value) return void 0;
672
+ var parts = value.trim().split(":");
673
+ if (parts.length !== 3) return void 0;
674
+ var _parts_map = _sliced_to_array(parts.map(function(p) {
675
+ return parseFloat(p);
676
+ }), 3), h = _parts_map[0], m = _parts_map[1], s = _parts_map[2];
677
+ if (![
678
+ h,
679
+ m,
680
+ s
681
+ ].every(function(n) {
682
+ return n !== void 0 && Number.isFinite(n);
683
+ })) {
684
+ return void 0;
685
+ }
686
+ return h * 3600 + m * 60 + s;
687
+ }
688
+ function parseProgressOffset(offset) {
689
+ if (!offset) return void 0;
690
+ var trimmed = offset.trim();
691
+ if (trimmed.endsWith("%")) {
692
+ var percent = parseFloat(trimmed);
693
+ return Number.isFinite(percent) ? {
694
+ offsetPercent: percent
695
+ } : void 0;
696
+ }
697
+ var seconds = parseVastTime(trimmed);
698
+ return seconds !== void 0 ? {
699
+ offsetSeconds: seconds
700
+ } : void 0;
701
+ }
642
702
  var MAX_VAST_WRAPPER_DEPTH = 5;
643
703
  var BEACON_TIMEOUT_MS = 3e4;
644
704
  var inFlightBeacons = /* @__PURE__ */ new Set();
645
705
  var VAST_ERROR_MEDIA_DISPLAY = 405;
706
+ var VAST_ERROR_WRAPPER_TIMEOUT = 301;
707
+ var VAST_ERROR_WRAPPER_LIMIT = 302;
708
+ var VAST_ERROR_WRAPPER_NO_ADS = 303;
709
+ var VAST_ERROR_NO_SUPPORTED_MEDIA = 403;
710
+ var VERIFICATION_NOT_SUPPORTED_REASON = 2;
711
+ var VIEWABLE_MIN_VISIBLE_RATIO = 0.5;
712
+ var VIEWABLE_MIN_CONTINUOUS_S = 2;
646
713
  function toVastMediaErrorCode(error) {
647
714
  switch(error === null || error === void 0 ? void 0 : error.code){
648
715
  case 1:
@@ -670,6 +737,7 @@ function createHlsAdPlayer(contentVideo, options) {
670
737
  var adHls;
671
738
  var adContainerEl = (_contentVideo_AD_CONTAINER_PROP = contentVideo[AD_CONTAINER_PROP]) !== null && _contentVideo_AD_CONTAINER_PROP !== void 0 ? _contentVideo_AD_CONTAINER_PROP : void 0;
672
739
  var adEventHandlers = [];
740
+ var containerHandlers = [];
673
741
  var currentAd;
674
742
  var currentAdMediaFile;
675
743
  var podAds = [];
@@ -687,14 +755,28 @@ function createHlsAdPlayer(contentVideo, options) {
687
755
  var sawDecodedVideoFrame = false;
688
756
  var videoFrameCallbackHandle;
689
757
  var pendingPlayPromise;
690
- var trackingFired = {
691
- impression: false,
692
- start: false,
693
- firstQuartile: false,
694
- midpoint: false,
695
- thirdQuartile: false,
696
- complete: false
697
- };
758
+ function createTrackingState() {
759
+ return {
760
+ impression: false,
761
+ loaded: false,
762
+ start: false,
763
+ firstQuartile: false,
764
+ midpoint: false,
765
+ thirdQuartile: false,
766
+ complete: false,
767
+ viewabilitySettled: false,
768
+ lastMuted: void 0,
769
+ progressFired: /* @__PURE__ */ new Set()
770
+ };
771
+ }
772
+ var trackingFired = createTrackingState();
773
+ var resolvedAds = /* @__PURE__ */ new Set();
774
+ var viewObserver;
775
+ var visibleRatio = 0;
776
+ var continuousVisibleS = 0;
777
+ var lastViewSampleTime;
778
+ var lastPlayerExpanded = false;
779
+ var documentHandlers = [];
698
780
  function emit(event, payload) {
699
781
  var set = listeners.get(event);
700
782
  if (!set) return;
@@ -723,7 +805,11 @@ function createHlsAdPlayer(contentVideo, options) {
723
805
  }
724
806
  }
725
807
  }
726
- function buildTrackingMacroContext(errorCode) {
808
+ function buildTrackingMacroContext() {
809
+ var options2 = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
810
+ var _options2_ad;
811
+ var errorCode = options2.errorCode, reason = options2.reason;
812
+ var ad = (_options2_ad = options2.ad) !== null && _options2_ad !== void 0 ? _options2_ad : currentAd;
727
813
  var context = {
728
814
  adPlayheadSeconds: adVideoElement === null || adVideoElement === void 0 ? void 0 : adVideoElement.currentTime,
729
815
  contentPlayheadSeconds: contentVideo.currentTime,
@@ -744,11 +830,17 @@ function createHlsAdPlayer(contentVideo, options) {
744
830
  if (podAds.length > 0) {
745
831
  context.podSequence = podIndex + 1;
746
832
  }
833
+ if (ad === null || ad === void 0 ? void 0 : ad.adServingId) {
834
+ context.adServingId = ad.adServingId;
835
+ }
836
+ if (reason != null) {
837
+ context.reason = reason;
838
+ }
747
839
  return context;
748
840
  }
749
841
  function fireTrackingPixels(urls, options2) {
750
842
  if (!urls || urls.length === 0) return;
751
- var macroContext = buildTrackingMacroContext(options2 === null || options2 === void 0 ? void 0 : options2.errorCode);
843
+ var macroContext = buildTrackingMacroContext(options2);
752
844
  urls.forEach(function(url) {
753
845
  try {
754
846
  var trackingUrl = substituteVastTrackingMacros(url, macroContext);
@@ -859,8 +951,8 @@ function createHlsAdPlayer(contentVideo, options) {
859
951
  var url = (mediaFile.url.toLowerCase().split("?")[0] || "").trim();
860
952
  return type.startsWith("audio/") || url.endsWith(".mp3") || url.endsWith(".aac") || url.endsWith(".m4a") || url.endsWith(".ogg") || url.endsWith(".oga") || url.endsWith(".opus") || url.endsWith(".wav") || url.endsWith(".weba");
861
953
  }
862
- function collectVideoMediaFiles(scope, verbose) {
863
- var mediaFileElements = scope.querySelectorAll("MediaFile");
954
+ function collectVideoMediaFiles(linear, verbose) {
955
+ var mediaFileElements = linear.querySelectorAll(":scope > MediaFiles > MediaFile");
864
956
  var mediaFiles = [];
865
957
  if (verbose) {
866
958
  console.log("[HlsAdPlayer] Found ".concat(mediaFileElements.length, " MediaFile element(s) in VAST XML"));
@@ -880,6 +972,13 @@ function createHlsAdPlayer(contentVideo, options) {
880
972
  }
881
973
  return;
882
974
  }
975
+ var apiFramework = (mf.getAttribute("apiFramework") || "").toUpperCase();
976
+ if (apiFramework === "VPAID") {
977
+ if (verbose) {
978
+ console.log("[HlsAdPlayer] MediaFile ".concat(index, " ignored (VPAID creatives are not supported)"));
979
+ }
980
+ return;
981
+ }
883
982
  var bitrateAttr = mf.getAttribute("bitrate");
884
983
  var bitrateValue = bitrateAttr ? parseInt(bitrateAttr, 10) : void 0;
885
984
  var mediaFile = {
@@ -910,114 +1009,144 @@ function createHlsAdPlayer(contentVideo, options) {
910
1009
  return mediaFiles;
911
1010
  }
912
1011
  function createEmptyTrackingUrls() {
913
- return {
1012
+ var urls = {
914
1013
  impression: [],
915
- start: [],
916
- firstQuartile: [],
917
- midpoint: [],
918
- thirdQuartile: [],
919
- complete: [],
920
- mute: [],
921
- unmute: [],
922
- pause: [],
923
- resume: [],
924
- fullscreen: [],
925
- exitFullscreen: [],
926
- skip: [],
927
- error: []
1014
+ clickTracking: [],
1015
+ error: [],
1016
+ viewable: [],
1017
+ notViewable: [],
1018
+ viewUndetermined: [],
1019
+ verificationNotExecuted: [],
1020
+ progress: []
928
1021
  };
1022
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1023
+ try {
1024
+ for(var _iterator = LINEAR_TRACKING_EVENTS[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1025
+ var event = _step.value;
1026
+ urls[event] = [];
1027
+ }
1028
+ } catch (err) {
1029
+ _didIteratorError = true;
1030
+ _iteratorError = err;
1031
+ } finally{
1032
+ try {
1033
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1034
+ _iterator.return();
1035
+ }
1036
+ } finally{
1037
+ if (_didIteratorError) {
1038
+ throw _iteratorError;
1039
+ }
1040
+ }
1041
+ }
1042
+ return urls;
929
1043
  }
930
- function extractTrackingUrls(scope) {
931
- var trackingUrls = createEmptyTrackingUrls();
932
- scope.querySelectorAll("Impression").forEach(function(el) {
933
- var _el_textContent;
934
- var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
935
- if (url) trackingUrls.impression.push(url);
1044
+ function pushUrls(target, elements) {
1045
+ elements.forEach(function(el) {
1046
+ var url = textOf(el);
1047
+ if (url) target.push(url);
936
1048
  });
937
- scope.querySelectorAll("Tracking").forEach(function(el) {
938
- var _el_textContent;
1049
+ }
1050
+ function collectLinearTracking(linear, into) {
1051
+ linear.querySelectorAll(":scope > TrackingEvents > Tracking").forEach(function(el) {
939
1052
  var event = el.getAttribute("event");
940
- var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
941
- if (event && url) {
942
- var eventKey = event;
943
- if (trackingUrls[eventKey]) {
944
- trackingUrls[eventKey].push(url);
945
- }
1053
+ var url = textOf(el);
1054
+ if (!event || !url) return;
1055
+ if (event === "progress") {
1056
+ var offset = parseProgressOffset(el.getAttribute("offset"));
1057
+ if (offset) into.progress.push(_object_spread({
1058
+ url: url
1059
+ }, offset));
1060
+ return;
1061
+ }
1062
+ if (LINEAR_TRACKING_EVENTS.indexOf(event) !== -1) {
1063
+ into[event].push(url);
946
1064
  }
947
1065
  });
948
- scope.querySelectorAll("Error").forEach(function(el) {
949
- var _el_textContent;
950
- var url = (_el_textContent = el.textContent) === null || _el_textContent === void 0 ? void 0 : _el_textContent.trim();
951
- if (url) trackingUrls.error.push(url);
1066
+ pushUrls(into.clickTracking, linear.querySelectorAll(":scope > VideoClicks > ClickTracking"));
1067
+ }
1068
+ function collectAdLevelTracking(adBody, into) {
1069
+ pushUrls(into.impression, adBody.querySelectorAll(":scope > Impression"));
1070
+ pushUrls(into.error, adBody.querySelectorAll(":scope > Error"));
1071
+ pushUrls(into.viewable, adBody.querySelectorAll(":scope > ViewableImpression > Viewable"));
1072
+ pushUrls(into.notViewable, adBody.querySelectorAll(":scope > ViewableImpression > NotViewable"));
1073
+ pushUrls(into.viewUndetermined, adBody.querySelectorAll(":scope > ViewableImpression > ViewUndetermined"));
1074
+ adBody.querySelectorAll(':scope > AdVerifications > Verification, :scope > Extensions > Extension[type="AdVerifications"] > AdVerifications > Verification').forEach(function(verification) {
1075
+ pushUrls(into.verificationNotExecuted, verification.querySelectorAll(':scope > TrackingEvents > Tracking[event="verificationNotExecuted"]'));
952
1076
  });
953
- return trackingUrls;
954
1077
  }
955
1078
  function mergeTrackingUrls(target, source) {
956
- Object.keys(target).forEach(function(key) {
957
- if (source[key] && source[key].length > 0) {
958
- target[key] = target[key].concat(source[key]);
1079
+ var targetLists = target;
1080
+ var sourceLists = source;
1081
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1082
+ try {
1083
+ for(var _iterator = Object.keys(targetLists)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1084
+ var key = _step.value;
1085
+ var incoming = sourceLists[key];
1086
+ if (incoming && incoming.length > 0) {
1087
+ targetLists[key] = targetLists[key].concat(incoming);
1088
+ }
959
1089
  }
960
- });
1090
+ } catch (err) {
1091
+ _didIteratorError = true;
1092
+ _iteratorError = err;
1093
+ } finally{
1094
+ try {
1095
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1096
+ _iterator.return();
1097
+ }
1098
+ } finally{
1099
+ if (_didIteratorError) {
1100
+ throw _iteratorError;
1101
+ }
1102
+ }
1103
+ }
961
1104
  return target;
962
1105
  }
1106
+ function parseXml(xmlString) {
1107
+ var xmlDoc = new DOMParser().parseFromString(xmlString, "text/xml");
1108
+ var parserError = xmlDoc.querySelector("parsererror");
1109
+ if (parserError) {
1110
+ console.error("[HlsAdPlayer] XML parsing error (malformed VAST XML):", parserError.textContent);
1111
+ return null;
1112
+ }
1113
+ return xmlDoc;
1114
+ }
1115
+ function getAdSequence(adElement) {
1116
+ var sequence = parseInt(adElement.getAttribute("sequence") || "", 10);
1117
+ return Number.isFinite(sequence) ? sequence : void 0;
1118
+ }
1119
+ function emptyVastResult(root) {
1120
+ var errorUrls = [];
1121
+ pushUrls(errorUrls, root.querySelectorAll(":scope > Error"));
1122
+ return {
1123
+ type: "empty",
1124
+ errorUrls: errorUrls,
1125
+ errorCode: VAST_ERROR_WRAPPER_NO_ADS
1126
+ };
1127
+ }
963
1128
  function parseVastXml(xmlString) {
964
1129
  try {
965
- var _xmlDoc_querySelector, _xmlDoc_querySelector_textContent, _xmlDoc_querySelector1, _xmlDoc_querySelector_textContent1, _xmlDoc_querySelector2, _xmlDoc_querySelector3;
966
- var parser = new DOMParser();
967
- var xmlDoc = parser.parseFromString(xmlString, "text/xml");
968
- var parserError = xmlDoc.querySelector("parsererror");
969
- if (parserError) {
970
- console.error("[HlsAdPlayer] XML parsing error (malformed VAST XML):", parserError.textContent);
971
- return {
972
- type: "empty"
973
- };
974
- }
975
- var adElement = xmlDoc.querySelector("Ad");
1130
+ var _adElements_find;
1131
+ var xmlDoc = parseXml(xmlString);
1132
+ if (!xmlDoc) return {
1133
+ type: "empty"
1134
+ };
1135
+ var root = xmlDoc.documentElement;
1136
+ var adElements = Array.from(root.querySelectorAll(":scope > Ad"));
1137
+ var adElement = (_adElements_find = adElements.find(function(el) {
1138
+ return getAdSequence(el) === void 0;
1139
+ })) !== null && _adElements_find !== void 0 ? _adElements_find : adElements[0];
976
1140
  if (!adElement) {
977
- console.warn("[HlsAdPlayer] No Ad element found in VAST XML");
978
- return {
979
- type: "empty"
980
- };
1141
+ var _root_getAttribute;
1142
+ console.warn("[HlsAdPlayer] No Ad element found in VAST ".concat((_root_getAttribute = root.getAttribute("version")) !== null && _root_getAttribute !== void 0 ? _root_getAttribute : "", " response"));
1143
+ return emptyVastResult(root);
981
1144
  }
982
- var adId = adElement.getAttribute("id") || "unknown";
983
- var title = ((_xmlDoc_querySelector = xmlDoc.querySelector("AdTitle")) === null || _xmlDoc_querySelector === void 0 ? void 0 : _xmlDoc_querySelector.textContent) || "Ad";
984
- var clickThrough = (_xmlDoc_querySelector1 = xmlDoc.querySelector("ClickThrough")) === null || _xmlDoc_querySelector1 === void 0 ? void 0 : (_xmlDoc_querySelector_textContent = _xmlDoc_querySelector1.textContent) === null || _xmlDoc_querySelector_textContent === void 0 ? void 0 : _xmlDoc_querySelector_textContent.trim();
985
- var wrapperUri = (_xmlDoc_querySelector2 = xmlDoc.querySelector("VASTAdTagURI")) === null || _xmlDoc_querySelector2 === void 0 ? void 0 : (_xmlDoc_querySelector_textContent1 = _xmlDoc_querySelector2.textContent) === null || _xmlDoc_querySelector_textContent1 === void 0 ? void 0 : _xmlDoc_querySelector_textContent1.trim();
986
- if (wrapperUri) {
987
- console.log("[HlsAdPlayer] VAST wrapper detected, following VASTAdTagURI: ".concat(wrapperUri));
988
- return {
989
- type: "wrapper",
990
- vastAdTagUri: wrapperUri,
991
- trackingUrls: extractTrackingUrls(xmlDoc),
992
- clickThrough: clickThrough
993
- };
1145
+ var parsed = parseAdElement(adElement, true);
1146
+ if (parsed.type === "wrapper") {
1147
+ console.log("[HlsAdPlayer] VAST wrapper detected, following VASTAdTagURI: ".concat(parsed.vastAdTagUri));
994
1148
  }
995
- var isNoAdAvailable = adId === "empty" || title.toLowerCase().includes("no ad available") || title.toLowerCase() === "no ad available";
996
- var durationText = ((_xmlDoc_querySelector3 = xmlDoc.querySelector("Duration")) === null || _xmlDoc_querySelector3 === void 0 ? void 0 : _xmlDoc_querySelector3.textContent) || "00:00:30";
997
- var durationParts = durationText.split(":");
998
- var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
999
- var mediaFiles = collectVideoMediaFiles(xmlDoc, true);
1000
- if (mediaFiles.length === 0) {
1001
- if (isNoAdAvailable) {
1002
- console.warn("[HlsAdPlayer] No ads available (VAST response indicates no ads)");
1003
- } else {
1004
- console.warn("[HlsAdPlayer] No supported media files found in VAST XML");
1005
- }
1006
- return {
1007
- type: "empty"
1008
- };
1009
- }
1010
- return {
1011
- type: "inline",
1012
- ad: {
1013
- id: adId,
1014
- title: title,
1015
- duration: duration,
1016
- mediaFiles: mediaFiles,
1017
- trackingUrls: extractTrackingUrls(xmlDoc),
1018
- clickThrough: clickThrough
1019
- }
1020
- };
1149
+ return parsed;
1021
1150
  } catch (error) {
1022
1151
  console.error("[HlsAdPlayer] Error parsing VAST XML:", error);
1023
1152
  return {
@@ -1026,48 +1155,101 @@ function createHlsAdPlayer(contentVideo, options) {
1026
1155
  }
1027
1156
  }
1028
1157
  function parseAdElement(adElement) {
1158
+ var verbose = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;
1029
1159
  try {
1030
- var _adElement_querySelector, _adElement_querySelector_textContent, _adElement_querySelector1, _adElement_querySelector_textContent1, _adElement_querySelector2, _adElement_querySelector3;
1031
- var adId = adElement.getAttribute("id") || "unknown";
1032
- var title = ((_adElement_querySelector = adElement.querySelector("AdTitle")) === null || _adElement_querySelector === void 0 ? void 0 : _adElement_querySelector.textContent) || "Ad";
1033
- 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();
1034
- 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();
1035
- if (wrapperUri) {
1160
+ var _parseVastTime;
1161
+ var wrapper = adElement.querySelector(":scope > Wrapper");
1162
+ if (wrapper) {
1163
+ var vastAdTagUri = textOf(wrapper.querySelector(":scope > VASTAdTagURI"));
1164
+ if (!vastAdTagUri) {
1165
+ console.warn("[HlsAdPlayer] <Wrapper> has no VASTAdTagURI");
1166
+ return {
1167
+ type: "empty"
1168
+ };
1169
+ }
1170
+ var trackingUrls2 = createEmptyTrackingUrls();
1171
+ collectAdLevelTracking(wrapper, trackingUrls2);
1172
+ wrapper.querySelectorAll(":scope > Creatives > Creative > Linear").forEach(function(linear2) {
1173
+ return collectLinearTracking(linear2, trackingUrls2);
1174
+ });
1036
1175
  return {
1037
1176
  type: "wrapper",
1038
- vastAdTagUri: wrapperUri,
1039
- trackingUrls: extractTrackingUrls(adElement),
1040
- clickThrough: clickThrough
1177
+ vastAdTagUri: vastAdTagUri,
1178
+ trackingUrls: trackingUrls2,
1179
+ rules: {
1180
+ followAdditionalWrappers: wrapper.getAttribute("followAdditionalWrappers") !== "false"
1181
+ }
1041
1182
  };
1042
1183
  }
1043
- var isNoAdAvailable = adId === "empty" || title.toLowerCase().includes("no ad available") || title.toLowerCase() === "no ad available";
1044
- var durationText = ((_adElement_querySelector3 = adElement.querySelector("Duration")) === null || _adElement_querySelector3 === void 0 ? void 0 : _adElement_querySelector3.textContent) || "00:00:30";
1045
- var durationParts = durationText.split(":");
1046
- var duration = parseInt(durationParts[0] || "0", 10) * 3600 + parseInt(durationParts[1] || "0", 10) * 60 + parseInt(durationParts[2] || "0", 10);
1047
- var mediaFiles = collectVideoMediaFiles(adElement, false);
1048
- if (mediaFiles.length === 0) {
1049
- if (isNoAdAvailable) {
1050
- console.warn("[HlsAdPlayer] Pod <Ad> indicates no ad available");
1051
- } else {
1052
- console.warn("[HlsAdPlayer] No supported media files in pod <Ad>");
1053
- }
1184
+ var inLine = adElement.querySelector(":scope > InLine");
1185
+ if (!inLine) {
1186
+ console.warn("[HlsAdPlayer] <Ad> has neither <InLine> nor <Wrapper>");
1054
1187
  return {
1055
1188
  type: "empty"
1056
1189
  };
1057
1190
  }
1191
+ var errorUrls = [];
1192
+ pushUrls(errorUrls, inLine.querySelectorAll(":scope > Error"));
1193
+ if (adElement.getAttribute("conditionalAd") === "true") {
1194
+ console.warn("[HlsAdPlayer] Skipping conditional <Ad> (VPAID-only)");
1195
+ return {
1196
+ type: "empty",
1197
+ errorUrls: errorUrls,
1198
+ errorCode: VAST_ERROR_NO_SUPPORTED_MEDIA
1199
+ };
1200
+ }
1201
+ var linear;
1202
+ var mediaFiles = [];
1203
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1204
+ try {
1205
+ for(var _iterator = Array.from(inLine.querySelectorAll(":scope > Creatives > Creative > Linear"))[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1206
+ var candidate = _step.value;
1207
+ var files = collectVideoMediaFiles(candidate, verbose);
1208
+ if (files.length > 0) {
1209
+ linear = candidate;
1210
+ mediaFiles = files;
1211
+ break;
1212
+ }
1213
+ }
1214
+ } catch (err) {
1215
+ _didIteratorError = true;
1216
+ _iteratorError = err;
1217
+ } finally{
1218
+ try {
1219
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1220
+ _iterator.return();
1221
+ }
1222
+ } finally{
1223
+ if (_didIteratorError) {
1224
+ throw _iteratorError;
1225
+ }
1226
+ }
1227
+ }
1228
+ if (!linear) {
1229
+ console.warn("[HlsAdPlayer] No supported linear media files in <Ad>");
1230
+ return {
1231
+ type: "empty",
1232
+ errorUrls: errorUrls,
1233
+ errorCode: VAST_ERROR_NO_SUPPORTED_MEDIA
1234
+ };
1235
+ }
1236
+ var trackingUrls = createEmptyTrackingUrls();
1237
+ collectAdLevelTracking(inLine, trackingUrls);
1238
+ collectLinearTracking(linear, trackingUrls);
1058
1239
  return {
1059
1240
  type: "inline",
1060
1241
  ad: {
1061
- id: adId,
1062
- title: title,
1063
- duration: duration,
1242
+ id: adElement.getAttribute("id") || "unknown",
1243
+ title: textOf(inLine.querySelector(":scope > AdTitle")) || "Ad",
1244
+ duration: (_parseVastTime = parseVastTime(textOf(linear.querySelector(":scope > Duration")))) !== null && _parseVastTime !== void 0 ? _parseVastTime : 30,
1064
1245
  mediaFiles: mediaFiles,
1065
- trackingUrls: extractTrackingUrls(adElement),
1066
- clickThrough: clickThrough
1246
+ trackingUrls: trackingUrls,
1247
+ clickThrough: textOf(linear.querySelector(":scope > VideoClicks > ClickThrough")),
1248
+ adServingId: textOf(inLine.querySelector(":scope > AdServingId"))
1067
1249
  }
1068
1250
  };
1069
1251
  } catch (error) {
1070
- console.error("[HlsAdPlayer] Error parsing pod <Ad> element:", error);
1252
+ console.error("[HlsAdPlayer] Error parsing <Ad> element:", error);
1071
1253
  return {
1072
1254
  type: "empty"
1073
1255
  };
@@ -1146,30 +1328,63 @@ function createHlsAdPlayer(contentVideo, options) {
1146
1328
  }
1147
1329
  function fetchAndParseVastAd(_0) {
1148
1330
  return _async_to_generator(function(vastTagUrl) {
1149
- var depth, accumulatedTracking, vastXml, parsed;
1331
+ var depth, accumulatedTracking, rules, vastXml, error, parsed, _parsed_errorUrls, _parsed_errorCode;
1150
1332
  var _arguments = arguments;
1151
1333
  return _ts_generator(this, function(_state) {
1152
1334
  switch(_state.label){
1153
1335
  case 0:
1154
- depth = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : 0, accumulatedTracking = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : createEmptyTrackingUrls();
1336
+ depth = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : 0, accumulatedTracking = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : createEmptyTrackingUrls(), rules = _arguments.length > 3 && _arguments[3] !== void 0 ? _arguments[3] : {
1337
+ followAdditionalWrappers: true
1338
+ };
1339
+ _state.label = 1;
1340
+ case 1:
1341
+ _state.trys.push([
1342
+ 1,
1343
+ 3,
1344
+ ,
1345
+ 4
1346
+ ]);
1155
1347
  return [
1156
1348
  4,
1157
1349
  fetchVastXml(vastTagUrl, depth === 0 ? "include" : "omit")
1158
1350
  ];
1159
- case 1:
1351
+ case 2:
1160
1352
  vastXml = _state.sent();
1353
+ return [
1354
+ 3,
1355
+ 4
1356
+ ];
1357
+ case 3:
1358
+ error = _state.sent();
1359
+ if (depth === 0) throw error;
1360
+ console.warn("[HlsAdPlayer] Wrapped VAST request failed:", error);
1361
+ fireTrackingPixels(accumulatedTracking.error, {
1362
+ errorCode: VAST_ERROR_WRAPPER_TIMEOUT
1363
+ });
1364
+ return [
1365
+ 2,
1366
+ null
1367
+ ];
1368
+ case 4:
1161
1369
  console.log("[HlsAdPlayer] VAST XML received");
1162
1370
  console.log("[HlsAdPlayer] VAST XML content (first 2000 chars):", vastXml.substring(0, 2e3));
1163
1371
  parsed = parseVastXml(vastXml);
1164
1372
  if (parsed.type === "empty") {
1373
+ ;
1374
+ fireTrackingPixels(((_parsed_errorUrls = parsed.errorUrls) !== null && _parsed_errorUrls !== void 0 ? _parsed_errorUrls : []).concat(accumulatedTracking.error), {
1375
+ errorCode: (_parsed_errorCode = parsed.errorCode) !== null && _parsed_errorCode !== void 0 ? _parsed_errorCode : VAST_ERROR_WRAPPER_NO_ADS
1376
+ });
1165
1377
  return [
1166
1378
  2,
1167
1379
  null
1168
1380
  ];
1169
1381
  }
1170
1382
  if (parsed.type === "wrapper") {
1171
- if (depth >= MAX_VAST_WRAPPER_DEPTH) {
1172
- console.warn("[HlsAdPlayer] VAST wrapper depth limit (".concat(MAX_VAST_WRAPPER_DEPTH, ") reached, aborting redirect chain"));
1383
+ if (depth >= MAX_VAST_WRAPPER_DEPTH || !rules.followAdditionalWrappers) {
1384
+ console.warn(rules.followAdditionalWrappers ? "[HlsAdPlayer] VAST wrapper depth limit (".concat(MAX_VAST_WRAPPER_DEPTH, ") reached, aborting redirect chain") : '[HlsAdPlayer] Parent wrapper set followAdditionalWrappers="false"; rejecting nested wrapper');
1385
+ fireTrackingPixels(accumulatedTracking.error.concat(parsed.trackingUrls.error), {
1386
+ errorCode: VAST_ERROR_WRAPPER_LIMIT
1387
+ });
1173
1388
  return [
1174
1389
  2,
1175
1390
  null
@@ -1178,7 +1393,7 @@ function createHlsAdPlayer(contentVideo, options) {
1178
1393
  mergeTrackingUrls(accumulatedTracking, parsed.trackingUrls);
1179
1394
  return [
1180
1395
  2,
1181
- fetchAndParseVastAd(parsed.vastAdTagUri, depth + 1, accumulatedTracking)
1396
+ fetchAndParseVastAd(parsed.vastAdTagUri, depth + 1, accumulatedTracking, parsed.rules)
1182
1397
  ];
1183
1398
  }
1184
1399
  mergeTrackingUrls(parsed.ad.trackingUrls, accumulatedTracking);
@@ -1192,7 +1407,7 @@ function createHlsAdPlayer(contentVideo, options) {
1192
1407
  }
1193
1408
  function fetchAndParsePod(vastTagUrl) {
1194
1409
  return _async_to_generator(function() {
1195
- var vastXml, trimmedXml, parser, xmlDoc, adElements, ads, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, adElement, parsed, resolved, error, err;
1410
+ var _root_getAttribute, vastXml, trimmedXml, xmlDoc, root, adElements, _root_getAttribute1, _empty_errorUrls, empty, _empty_errorCode, resolveAd, podElements, buffet, ads, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, adElement, ad, err, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, adElement1, ad1, err;
1196
1411
  return _ts_generator(this, function(_state) {
1197
1412
  switch(_state.label){
1198
1413
  case 0:
@@ -1211,125 +1426,212 @@ function createHlsAdPlayer(contentVideo, options) {
1211
1426
  []
1212
1427
  ];
1213
1428
  }
1214
- parser = new DOMParser();
1215
- xmlDoc = parser.parseFromString(vastXml, "text/xml");
1216
- if (xmlDoc.querySelector("parsererror")) {
1217
- console.error("[HlsAdPlayer] Pod VAST XML parsing error (malformed). Content (first 2000 chars):", vastXml.substring(0, 2e3));
1429
+ xmlDoc = parseXml(vastXml);
1430
+ if (!xmlDoc) {
1431
+ console.error("[HlsAdPlayer] Pod VAST XML content (first 2000 chars):", vastXml.substring(0, 2e3));
1218
1432
  return [
1219
1433
  2,
1220
1434
  []
1221
1435
  ];
1222
1436
  }
1223
- adElements = Array.from(xmlDoc.querySelectorAll("Ad"));
1437
+ root = xmlDoc.documentElement;
1438
+ adElements = Array.from(root.querySelectorAll(":scope > Ad"));
1224
1439
  if (adElements.length === 0) {
1225
- console.warn("[HlsAdPlayer] No <Ad> elements found in pod VAST response (no fill). Content (first 2000 chars):", vastXml.substring(0, 2e3));
1440
+ ;
1441
+ ;
1442
+ console.warn("[HlsAdPlayer] No <Ad> elements in VAST ".concat((_root_getAttribute1 = root.getAttribute("version")) !== null && _root_getAttribute1 !== void 0 ? _root_getAttribute1 : "", " pod response (no fill)"));
1443
+ empty = emptyVastResult(root);
1444
+ if (empty.type === "empty" && ((_empty_errorUrls = empty.errorUrls) === null || _empty_errorUrls === void 0 ? void 0 : _empty_errorUrls.length)) {
1445
+ ;
1446
+ fireTrackingPixels(empty.errorUrls, {
1447
+ errorCode: (_empty_errorCode = empty.errorCode) !== null && _empty_errorCode !== void 0 ? _empty_errorCode : VAST_ERROR_WRAPPER_NO_ADS
1448
+ });
1449
+ }
1226
1450
  return [
1227
1451
  2,
1228
1452
  []
1229
1453
  ];
1230
1454
  }
1231
- adElements.sort(function(a, b) {
1232
- var aSeq = parseInt(a.getAttribute("sequence") || "", 10);
1233
- var bSeq = parseInt(b.getAttribute("sequence") || "", 10);
1234
- var aVal = Number.isFinite(aSeq) ? aSeq : Number.MAX_SAFE_INTEGER;
1235
- var bVal = Number.isFinite(bSeq) ? bSeq : Number.MAX_SAFE_INTEGER;
1236
- return aVal - bVal;
1455
+ resolveAd = function resolveAd(adElement) {
1456
+ return _async_to_generator(function() {
1457
+ var _parsed_errorUrls, parsed, _parsed_errorCode;
1458
+ return _ts_generator(this, function(_state) {
1459
+ parsed = parseAdElement(adElement);
1460
+ if (parsed.type === "inline") return [
1461
+ 2,
1462
+ parsed.ad
1463
+ ];
1464
+ if (parsed.type === "wrapper") {
1465
+ return [
1466
+ 2,
1467
+ fetchAndParseVastAd(parsed.vastAdTagUri, 1, parsed.trackingUrls, parsed.rules)
1468
+ ];
1469
+ }
1470
+ if ((_parsed_errorUrls = parsed.errorUrls) === null || _parsed_errorUrls === void 0 ? void 0 : _parsed_errorUrls.length) {
1471
+ ;
1472
+ fireTrackingPixels(parsed.errorUrls, {
1473
+ errorCode: (_parsed_errorCode = parsed.errorCode) !== null && _parsed_errorCode !== void 0 ? _parsed_errorCode : VAST_ERROR_NO_SUPPORTED_MEDIA
1474
+ });
1475
+ }
1476
+ return [
1477
+ 2,
1478
+ null
1479
+ ];
1480
+ });
1481
+ })();
1482
+ };
1483
+ podElements = adElements.filter(function(el) {
1484
+ return getAdSequence(el) !== void 0;
1485
+ }).sort(function(a, b) {
1486
+ return getAdSequence(a) - getAdSequence(b);
1487
+ });
1488
+ buffet = adElements.filter(function(el) {
1489
+ return getAdSequence(el) === void 0;
1237
1490
  });
1238
1491
  ads = [];
1492
+ if (!(podElements.length === 0)) return [
1493
+ 3,
1494
+ 10
1495
+ ];
1239
1496
  _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1240
1497
  _state.label = 2;
1241
1498
  case 2:
1242
1499
  _state.trys.push([
1243
1500
  2,
1244
- 10,
1245
- 11,
1246
- 12
1501
+ 7,
1502
+ 8,
1503
+ 9
1247
1504
  ]);
1248
- _iterator = adElements[Symbol.iterator]();
1505
+ _iterator = buffet[Symbol.iterator]();
1249
1506
  _state.label = 3;
1250
1507
  case 3:
1251
1508
  if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [
1252
1509
  3,
1253
- 9
1510
+ 6
1254
1511
  ];
1255
1512
  adElement = _step.value;
1256
- parsed = parseAdElement(adElement);
1257
- if (!(parsed.type === "inline")) return [
1258
- 3,
1259
- 4
1260
- ];
1261
- ads.push(parsed.ad);
1262
1513
  return [
1263
- 3,
1264
- 8
1514
+ 4,
1515
+ resolveAd(adElement)
1265
1516
  ];
1266
1517
  case 4:
1267
- if (!(parsed.type === "wrapper")) return [
1268
- 3,
1269
- 8
1270
- ];
1518
+ ad = _state.sent();
1519
+ if (ad) ads.push(ad);
1271
1520
  _state.label = 5;
1272
1521
  case 5:
1273
- _state.trys.push([
1274
- 5,
1275
- 7,
1276
- ,
1277
- 8
1278
- ]);
1522
+ _iteratorNormalCompletion = true;
1279
1523
  return [
1280
- 4,
1281
- fetchAndParseVastAd(parsed.vastAdTagUri, 1, parsed.trackingUrls)
1524
+ 3,
1525
+ 3
1282
1526
  ];
1283
1527
  case 6:
1284
- resolved = _state.sent();
1285
- if (resolved) {
1286
- ads.push(resolved);
1287
- }
1288
1528
  return [
1289
1529
  3,
1290
- 8
1530
+ 9
1291
1531
  ];
1292
1532
  case 7:
1293
- error = _state.sent();
1294
- console.warn("[HlsAdPlayer] Failed to resolve wrapper <Ad> in pod:", error);
1533
+ err = _state.sent();
1534
+ _didIteratorError = true;
1535
+ _iteratorError = err;
1295
1536
  return [
1296
1537
  3,
1297
- 8
1538
+ 9
1298
1539
  ];
1299
1540
  case 8:
1300
- _iteratorNormalCompletion = true;
1541
+ try {
1542
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1543
+ _iterator.return();
1544
+ }
1545
+ } finally{
1546
+ if (_didIteratorError) {
1547
+ throw _iteratorError;
1548
+ }
1549
+ }
1301
1550
  return [
1302
- 3,
1303
- 3
1551
+ 7
1304
1552
  ];
1305
1553
  case 9:
1306
1554
  return [
1307
1555
  3,
1308
- 12
1556
+ 21
1309
1557
  ];
1310
1558
  case 10:
1311
- err = _state.sent();
1312
- _didIteratorError = true;
1313
- _iteratorError = err;
1559
+ _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
1560
+ _state.label = 11;
1561
+ case 11:
1562
+ _state.trys.push([
1563
+ 11,
1564
+ 19,
1565
+ 20,
1566
+ 21
1567
+ ]);
1568
+ _iterator1 = podElements[Symbol.iterator]();
1569
+ _state.label = 12;
1570
+ case 12:
1571
+ if (!!(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done)) return [
1572
+ 3,
1573
+ 18
1574
+ ];
1575
+ adElement1 = _step1.value;
1576
+ return [
1577
+ 4,
1578
+ resolveAd(adElement1)
1579
+ ];
1580
+ case 13:
1581
+ ad1 = _state.sent();
1582
+ _state.label = 14;
1583
+ case 14:
1584
+ if (!(!ad1 && buffet.length > 0)) return [
1585
+ 3,
1586
+ 16
1587
+ ];
1588
+ return [
1589
+ 4,
1590
+ resolveAd(buffet.shift())
1591
+ ];
1592
+ case 15:
1593
+ ad1 = _state.sent();
1594
+ return [
1595
+ 3,
1596
+ 14
1597
+ ];
1598
+ case 16:
1599
+ if (ad1) ads.push(ad1);
1600
+ _state.label = 17;
1601
+ case 17:
1602
+ _iteratorNormalCompletion1 = true;
1314
1603
  return [
1315
1604
  3,
1316
1605
  12
1317
1606
  ];
1318
- case 11:
1607
+ case 18:
1608
+ return [
1609
+ 3,
1610
+ 21
1611
+ ];
1612
+ case 19:
1613
+ err = _state.sent();
1614
+ _didIteratorError1 = true;
1615
+ _iteratorError1 = err;
1616
+ return [
1617
+ 3,
1618
+ 21
1619
+ ];
1620
+ case 20:
1319
1621
  try {
1320
- if (!_iteratorNormalCompletion && _iterator.return != null) {
1321
- _iterator.return();
1622
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
1623
+ _iterator1.return();
1322
1624
  }
1323
1625
  } finally{
1324
- if (_didIteratorError) {
1325
- throw _iteratorError;
1626
+ if (_didIteratorError1) {
1627
+ throw _iteratorError1;
1326
1628
  }
1327
1629
  }
1328
1630
  return [
1329
1631
  7
1330
1632
  ];
1331
- case 12:
1332
- console.log("[HlsAdPlayer] Parsed ".concat(ads.length, " ad(s) from pod VAST response"));
1633
+ case 21:
1634
+ console.log("[HlsAdPlayer] Parsed ".concat(ads.length, " ad(s) from VAST ").concat((_root_getAttribute = root.getAttribute("version")) !== null && _root_getAttribute !== void 0 ? _root_getAttribute : "", " pod response"));
1333
1635
  return [
1334
1636
  2,
1335
1637
  ads
@@ -1359,7 +1661,40 @@ function createHlsAdPlayer(contentVideo, options) {
1359
1661
  var onTimeUpdate = function onTimeUpdate() {
1360
1662
  if (!currentAd || !adVideoElement) return;
1361
1663
  noteAdProgress();
1362
- var progress = adVideoElement.currentTime / currentAd.duration;
1664
+ sampleViewability();
1665
+ var currentTime = adVideoElement.currentTime;
1666
+ if (trackingFired.start) {
1667
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1668
+ try {
1669
+ for(var _iterator = currentAd.trackingUrls.progress[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1670
+ var event = _step.value;
1671
+ var _event_offsetSeconds, _event_offsetPercent;
1672
+ if (trackingFired.progressFired.has(event)) continue;
1673
+ var threshold = (_event_offsetSeconds = event.offsetSeconds) !== null && _event_offsetSeconds !== void 0 ? _event_offsetSeconds : ((_event_offsetPercent = event.offsetPercent) !== null && _event_offsetPercent !== void 0 ? _event_offsetPercent : 0) / 100 * currentAd.duration;
1674
+ if (currentTime >= threshold) {
1675
+ trackingFired.progressFired.add(event);
1676
+ fireTrackingPixels([
1677
+ event.url
1678
+ ]);
1679
+ }
1680
+ }
1681
+ } catch (err) {
1682
+ _didIteratorError = true;
1683
+ _iteratorError = err;
1684
+ } finally{
1685
+ try {
1686
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1687
+ _iterator.return();
1688
+ }
1689
+ } finally{
1690
+ if (_didIteratorError) {
1691
+ throw _iteratorError;
1692
+ }
1693
+ }
1694
+ }
1695
+ }
1696
+ if (!(currentAd.duration > 0)) return;
1697
+ var progress = currentTime / currentAd.duration;
1363
1698
  if (progress >= 0.25 && !trackingFired.firstQuartile) {
1364
1699
  trackingFired.firstQuartile = true;
1365
1700
  fireTrackingPixels(currentAd.trackingUrls.firstQuartile);
@@ -1381,15 +1716,38 @@ function createHlsAdPlayer(contentVideo, options) {
1381
1716
  handleVideoDecodeFailure();
1382
1717
  }
1383
1718
  };
1719
+ var onCanPlay = function onCanPlay() {
1720
+ if (!currentAd || trackingFired.loaded) return;
1721
+ trackingFired.loaded = true;
1722
+ fireTrackingPixels(currentAd.trackingUrls.loaded);
1723
+ };
1384
1724
  var onPlaying = function onPlaying() {
1385
1725
  startStallWatchdog();
1386
- if (!currentAd || trackingFired.start) return;
1726
+ if (!currentAd || !adVideoElement) return;
1727
+ if (!trackingFired.impression) {
1728
+ trackingFired.impression = true;
1729
+ resolvedAds.add(currentAd);
1730
+ fireTrackingPixels(currentAd.trackingUrls.impression);
1731
+ fireTrackingPixels(currentAd.trackingUrls.creativeView);
1732
+ fireTrackingPixels(currentAd.trackingUrls.verificationNotExecuted, {
1733
+ reason: VERIFICATION_NOT_SUPPORTED_REASON
1734
+ });
1735
+ emit("ad_impression", {
1736
+ adId: currentAd.id,
1737
+ index: podIndex,
1738
+ total: podAds.length || 1
1739
+ });
1740
+ }
1741
+ if (trackingFired.start) return;
1387
1742
  trackingFired.start = true;
1743
+ trackingFired.lastMuted = adVideoElement.muted || adVideoElement.volume === 0;
1388
1744
  fireTrackingPixels(currentAd.trackingUrls.start);
1389
1745
  console.log("[HlsAdPlayer] Ad started playing");
1390
1746
  };
1391
1747
  var onEnded = function onEnded() {
1392
1748
  if (!currentAd || trackingFired.complete) return;
1749
+ sampleViewability();
1750
+ settleViewability();
1393
1751
  trackingFired.complete = true;
1394
1752
  fireTrackingPixels(currentAd.trackingUrls.complete);
1395
1753
  console.log("[HlsAdPlayer] Ad completed");
@@ -1401,11 +1759,7 @@ function createHlsAdPlayer(contentVideo, options) {
1401
1759
  var onError = function onError(e) {
1402
1760
  if (!adPlaying) return;
1403
1761
  console.error("[HlsAdPlayer] Ad video error:", e);
1404
- if (currentAd) {
1405
- fireTrackingPixels(currentAd.trackingUrls.error, {
1406
- errorCode: toVastMediaErrorCode(adVideoElement === null || adVideoElement === void 0 ? void 0 : adVideoElement.error)
1407
- });
1408
- }
1762
+ reportCurrentAdError(toVastMediaErrorCode(adVideoElement === null || adVideoElement === void 0 ? void 0 : adVideoElement.error));
1409
1763
  if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1410
1764
  return;
1411
1765
  }
@@ -1413,19 +1767,26 @@ function createHlsAdPlayer(contentVideo, options) {
1413
1767
  };
1414
1768
  var onVolumeChange = function onVolumeChange() {
1415
1769
  if (!currentAd || !adVideoElement) return;
1416
- if (adVideoElement.muted) {
1417
- fireTrackingPixels(currentAd.trackingUrls.mute);
1418
- } else {
1419
- fireTrackingPixels(currentAd.trackingUrls.unmute);
1770
+ var muted = adVideoElement.muted || adVideoElement.volume === 0;
1771
+ if (!trackingFired.start) {
1772
+ trackingFired.lastMuted = muted;
1773
+ return;
1420
1774
  }
1775
+ if (muted === trackingFired.lastMuted) return;
1776
+ trackingFired.lastMuted = muted;
1777
+ fireTrackingPixels(muted ? currentAd.trackingUrls.mute : currentAd.trackingUrls.unmute);
1778
+ };
1779
+ var isTrackablePlayback = function isTrackablePlayback() {
1780
+ return adPlaying && !!currentAd && !!adVideoElement && trackingFired.start && !trackingFired.complete && !adVideoElement.ended;
1421
1781
  };
1422
1782
  var onPause = function onPause() {
1423
- if (currentAd && adVideoElement && !adVideoElement.ended) {
1783
+ continuousVisibleS = 0;
1784
+ if (isTrackablePlayback()) {
1424
1785
  fireTrackingPixels(currentAd.trackingUrls.pause);
1425
1786
  }
1426
1787
  };
1427
1788
  var onPlay = function onPlay() {
1428
- if (currentAd && adVideoElement && adVideoElement.currentTime > 0) {
1789
+ if (isTrackablePlayback()) {
1429
1790
  fireTrackingPixels(currentAd.trackingUrls.resume);
1430
1791
  }
1431
1792
  };
@@ -1438,6 +1799,10 @@ function createHlsAdPlayer(contentVideo, options) {
1438
1799
  type: "loadedmetadata",
1439
1800
  handler: onLoadedMetadata
1440
1801
  },
1802
+ {
1803
+ type: "canplay",
1804
+ handler: onCanPlay
1805
+ },
1441
1806
  {
1442
1807
  type: "playing",
1443
1808
  handler: onPlaying
@@ -1509,6 +1874,229 @@ function createHlsAdPlayer(contentVideo, options) {
1509
1874
  }
1510
1875
  adEventHandlers = [];
1511
1876
  }
1877
+ function handleAdClick() {
1878
+ if (!adPlaying || !currentAd) return;
1879
+ fireTrackingPixels(currentAd.trackingUrls.clickTracking);
1880
+ emit("ad_click", {
1881
+ adId: currentAd.id,
1882
+ clickThrough: currentAd.clickThrough
1883
+ });
1884
+ if (currentAd.clickThrough) {
1885
+ try {
1886
+ window.open(currentAd.clickThrough, "_blank", "noopener");
1887
+ } catch (error) {
1888
+ console.warn("[HlsAdPlayer] Failed to open ClickThrough URL:", error);
1889
+ }
1890
+ }
1891
+ }
1892
+ function reportCurrentAdError(errorCode) {
1893
+ if (!currentAd) return;
1894
+ resolvedAds.add(currentAd);
1895
+ fireTrackingPixels(currentAd.trackingUrls.error, {
1896
+ errorCode: errorCode
1897
+ });
1898
+ }
1899
+ function fireNotUsedForUnplayedAds() {
1900
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1901
+ try {
1902
+ for(var _iterator = podAds[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1903
+ var ad = _step.value;
1904
+ if (resolvedAds.has(ad)) continue;
1905
+ resolvedAds.add(ad);
1906
+ fireTrackingPixels(ad.trackingUrls.notUsed, {
1907
+ ad: ad
1908
+ });
1909
+ }
1910
+ } catch (err) {
1911
+ _didIteratorError = true;
1912
+ _iteratorError = err;
1913
+ } finally{
1914
+ try {
1915
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1916
+ _iterator.return();
1917
+ }
1918
+ } finally{
1919
+ if (_didIteratorError) {
1920
+ throw _iteratorError;
1921
+ }
1922
+ }
1923
+ }
1924
+ }
1925
+ function sampleViewability() {
1926
+ if (!currentAd || !adVideoElement || !viewObserver || !trackingFired.impression || trackingFired.viewabilitySettled) {
1927
+ return;
1928
+ }
1929
+ var now = adVideoElement.currentTime;
1930
+ var inView = visibleRatio >= VIEWABLE_MIN_VISIBLE_RATIO && document.visibilityState === "visible" && !adVideoElement.paused;
1931
+ if (!inView) {
1932
+ continuousVisibleS = 0;
1933
+ } else if (lastViewSampleTime !== void 0 && now >= lastViewSampleTime) {
1934
+ continuousVisibleS += now - lastViewSampleTime;
1935
+ }
1936
+ lastViewSampleTime = now;
1937
+ if (continuousVisibleS >= VIEWABLE_MIN_CONTINUOUS_S) {
1938
+ trackingFired.viewabilitySettled = true;
1939
+ fireTrackingPixels(currentAd.trackingUrls.viewable);
1940
+ }
1941
+ }
1942
+ function settleViewability() {
1943
+ if (!currentAd || !trackingFired.impression || trackingFired.viewabilitySettled) {
1944
+ return;
1945
+ }
1946
+ trackingFired.viewabilitySettled = true;
1947
+ fireTrackingPixels(viewObserver ? currentAd.trackingUrls.notViewable : currentAd.trackingUrls.viewUndetermined);
1948
+ }
1949
+ function handleFullscreenChange() {
1950
+ var _document_fullscreenElement;
1951
+ if (!adPlaying || !currentAd || !adContainerEl || !trackingFired.start) return;
1952
+ var fullscreenEl = (_document_fullscreenElement = document.fullscreenElement) !== null && _document_fullscreenElement !== void 0 ? _document_fullscreenElement : document.webkitFullscreenElement;
1953
+ var expanded = !!fullscreenEl && fullscreenEl.contains(adContainerEl);
1954
+ if (expanded === lastPlayerExpanded) return;
1955
+ lastPlayerExpanded = expanded;
1956
+ var urls = currentAd.trackingUrls;
1957
+ fireTrackingPixels(expanded ? urls.fullscreen.concat(urls.playerExpand) : urls.exitFullscreen.concat(urls.playerCollapse));
1958
+ }
1959
+ function attachContainerHandlers() {
1960
+ if (!adContainerEl || containerHandlers.length > 0) return;
1961
+ var onClick = function onClick() {
1962
+ return handleAdClick();
1963
+ };
1964
+ var onInteraction = function onInteraction(event) {
1965
+ if (adPlaying) emit("ad_interaction", event);
1966
+ };
1967
+ containerHandlers = [
1968
+ {
1969
+ type: "click",
1970
+ handler: onClick
1971
+ },
1972
+ {
1973
+ type: "mousedown",
1974
+ handler: onInteraction
1975
+ },
1976
+ {
1977
+ type: "touchstart",
1978
+ handler: onInteraction
1979
+ }
1980
+ ];
1981
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1982
+ try {
1983
+ for(var _iterator = containerHandlers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1984
+ var _step_value = _step.value, type = _step_value.type, handler = _step_value.handler;
1985
+ adContainerEl.addEventListener(type, handler);
1986
+ }
1987
+ } catch (err) {
1988
+ _didIteratorError = true;
1989
+ _iteratorError = err;
1990
+ } finally{
1991
+ try {
1992
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1993
+ _iterator.return();
1994
+ }
1995
+ } finally{
1996
+ if (_didIteratorError) {
1997
+ throw _iteratorError;
1998
+ }
1999
+ }
2000
+ }
2001
+ var onFullscreenChange = function onFullscreenChange() {
2002
+ return handleFullscreenChange();
2003
+ };
2004
+ documentHandlers = [
2005
+ {
2006
+ type: "fullscreenchange",
2007
+ handler: onFullscreenChange
2008
+ },
2009
+ {
2010
+ type: "webkitfullscreenchange",
2011
+ handler: onFullscreenChange
2012
+ }
2013
+ ];
2014
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
2015
+ try {
2016
+ for(var _iterator1 = documentHandlers[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
2017
+ var _step_value1 = _step1.value, type1 = _step_value1.type, handler1 = _step_value1.handler;
2018
+ document.addEventListener(type1, handler1);
2019
+ }
2020
+ } catch (err) {
2021
+ _didIteratorError1 = true;
2022
+ _iteratorError1 = err;
2023
+ } finally{
2024
+ try {
2025
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
2026
+ _iterator1.return();
2027
+ }
2028
+ } finally{
2029
+ if (_didIteratorError1) {
2030
+ throw _iteratorError1;
2031
+ }
2032
+ }
2033
+ }
2034
+ if (typeof IntersectionObserver !== "undefined") {
2035
+ viewObserver = new IntersectionObserver(function(entries) {
2036
+ var entry = entries[entries.length - 1];
2037
+ if (entry) {
2038
+ visibleRatio = entry.isIntersecting ? entry.intersectionRatio : 0;
2039
+ }
2040
+ }, {
2041
+ threshold: [
2042
+ 0,
2043
+ 0.25,
2044
+ 0.5,
2045
+ 0.75,
2046
+ 1
2047
+ ]
2048
+ });
2049
+ viewObserver.observe(adContainerEl);
2050
+ }
2051
+ }
2052
+ function detachContainerHandlers() {
2053
+ if (adContainerEl) {
2054
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2055
+ try {
2056
+ for(var _iterator = containerHandlers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
2057
+ var _step_value = _step.value, type = _step_value.type, handler = _step_value.handler;
2058
+ adContainerEl.removeEventListener(type, handler);
2059
+ }
2060
+ } catch (err) {
2061
+ _didIteratorError = true;
2062
+ _iteratorError = err;
2063
+ } finally{
2064
+ try {
2065
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
2066
+ _iterator.return();
2067
+ }
2068
+ } finally{
2069
+ if (_didIteratorError) {
2070
+ throw _iteratorError;
2071
+ }
2072
+ }
2073
+ }
2074
+ }
2075
+ containerHandlers = [];
2076
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
2077
+ try {
2078
+ for(var _iterator1 = documentHandlers[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
2079
+ var _step_value1 = _step1.value, type1 = _step_value1.type, handler1 = _step_value1.handler;
2080
+ document.removeEventListener(type1, handler1);
2081
+ }
2082
+ } catch (err) {
2083
+ _didIteratorError1 = true;
2084
+ _iteratorError1 = err;
2085
+ } finally{
2086
+ try {
2087
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
2088
+ _iterator1.return();
2089
+ }
2090
+ } finally{
2091
+ if (_didIteratorError1) {
2092
+ throw _iteratorError1;
2093
+ }
2094
+ }
2095
+ }
2096
+ documentHandlers = [];
2097
+ viewObserver === null || viewObserver === void 0 ? void 0 : viewObserver.disconnect();
2098
+ viewObserver = void 0;
2099
+ }
1512
2100
  function setAdPlayingFlag(isPlaying) {
1513
2101
  if (isPlaying) {
1514
2102
  contentVideo.dataset.stormcloudAdPlaying = "true";
@@ -1516,15 +2104,10 @@ function createHlsAdPlayer(contentVideo, options) {
1516
2104
  delete contentVideo.dataset.stormcloudAdPlaying;
1517
2105
  }
1518
2106
  }
1519
- function resetQuartileTracking(preserveImpression) {
1520
- trackingFired = {
1521
- impression: preserveImpression ? trackingFired.impression : false,
1522
- start: false,
1523
- firstQuartile: false,
1524
- midpoint: false,
1525
- thirdQuartile: false,
1526
- complete: false
1527
- };
2107
+ function resetAdTracking() {
2108
+ trackingFired = createTrackingState();
2109
+ continuousVisibleS = 0;
2110
+ lastViewSampleTime = void 0;
1528
2111
  }
1529
2112
  function isRvfcSupported(video) {
1530
2113
  return typeof video.requestVideoFrameCallback === "function";
@@ -1630,13 +2213,12 @@ function createHlsAdPlayer(contentVideo, options) {
1630
2213
  if (podIndex >= podAds.length - 1) {
1631
2214
  return false;
1632
2215
  }
2216
+ settleViewability();
1633
2217
  podIndex++;
1634
2218
  currentAd = podAds[podIndex];
1635
2219
  currentAdMediaFile = void 0;
1636
- resetQuartileTracking(false);
2220
+ resetAdTracking();
1637
2221
  if (currentAd) {
1638
- fireTrackingPixels(currentAd.trackingUrls.impression);
1639
- trackingFired.impression = true;
1640
2222
  console.log("[HlsAdPlayer] Advancing to next pod ad (".concat(podIndex + 1, "/").concat(podAds.length, "): ").concat(currentAd.title, ", duration: ").concat(currentAd.duration, "s"));
1641
2223
  }
1642
2224
  clearStallWatchdog();
@@ -1674,11 +2256,7 @@ function createHlsAdPlayer(contentVideo, options) {
1674
2256
  function handleAdStall() {
1675
2257
  console.warn("[HlsAdPlayer] Ad playback stalled (no progress) - recovering to avoid a blank frame");
1676
2258
  clearStallWatchdog();
1677
- if (currentAd) {
1678
- fireTrackingPixels(currentAd.trackingUrls.error, {
1679
- errorCode: VAST_ERROR_MEDIA_DISPLAY
1680
- });
1681
- }
2259
+ reportCurrentAdError(VAST_ERROR_MEDIA_DISPLAY);
1682
2260
  if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1683
2261
  return;
1684
2262
  }
@@ -1744,11 +2322,7 @@ function createHlsAdPlayer(contentVideo, options) {
1744
2322
  function handleVideoDecodeFailure() {
1745
2323
  console.warn("[HlsAdPlayer] Ad audio is advancing but no video frames decoded (black screen) - skipping this creative");
1746
2324
  clearStallWatchdog();
1747
- if (currentAd) {
1748
- fireTrackingPixels(currentAd.trackingUrls.error, {
1749
- errorCode: VAST_ERROR_MEDIA_DISPLAY
1750
- });
1751
- }
2325
+ reportCurrentAdError(VAST_ERROR_MEDIA_DISPLAY);
1752
2326
  if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1753
2327
  return;
1754
2328
  }
@@ -1803,6 +2377,7 @@ function createHlsAdPlayer(contentVideo, options) {
1803
2377
  }
1804
2378
  function handleAdComplete() {
1805
2379
  console.log("[HlsAdPlayer] Handling ad completion");
2380
+ settleViewability();
1806
2381
  clearStallWatchdog();
1807
2382
  adPlaying = false;
1808
2383
  setAdPlayingFlag(false);
@@ -1826,6 +2401,7 @@ function createHlsAdPlayer(contentVideo, options) {
1826
2401
  }
1827
2402
  function handleAdError() {
1828
2403
  console.log("[HlsAdPlayer] Handling ad error");
2404
+ settleViewability();
1829
2405
  clearStallWatchdog();
1830
2406
  adPlaying = false;
1831
2407
  setAdPlayingFlag(false);
@@ -1858,6 +2434,7 @@ function createHlsAdPlayer(contentVideo, options) {
1858
2434
  adContainerEl = container;
1859
2435
  }
1860
2436
  contentVideo[AD_CONTAINER_PROP] = adContainerEl;
2437
+ attachContainerHandlers();
1861
2438
  },
1862
2439
  requestAds: function requestAds(vastTagUrl) {
1863
2440
  return _async_to_generator(function() {
@@ -1920,10 +2497,6 @@ function createHlsAdPlayer(contentVideo, options) {
1920
2497
  currentAd = ads[0];
1921
2498
  currentAdMediaFile = void 0;
1922
2499
  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"));
1923
- if (currentAd) {
1924
- fireTrackingPixels(currentAd.trackingUrls.impression);
1925
- trackingFired.impression = true;
1926
- }
1927
2500
  return [
1928
2501
  2,
1929
2502
  Promise.resolve()
@@ -1963,7 +2536,7 @@ function createHlsAdPlayer(contentVideo, options) {
1963
2536
  contentVideo[AD_VIDEO_PROP] = adVideoElement;
1964
2537
  }
1965
2538
  setupAdEventListeners();
1966
- resetQuartileTracking(true);
2539
+ resetAdTracking();
1967
2540
  contentVolume = contentVideo.volume;
1968
2541
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1969
2542
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
@@ -2034,6 +2607,8 @@ function createHlsAdPlayer(contentVideo, options) {
2034
2607
  var previousMutedState;
2035
2608
  return _ts_generator(this, function(_state) {
2036
2609
  console.log("[HlsAdPlayer] Stopping ad");
2610
+ settleViewability();
2611
+ fireNotUsedForUnplayedAds();
2037
2612
  clearStallWatchdog();
2038
2613
  adPlaying = false;
2039
2614
  setAdPlayingFlag(false);
@@ -2064,6 +2639,8 @@ function createHlsAdPlayer(contentVideo, options) {
2064
2639
  },
2065
2640
  destroy: function destroy() {
2066
2641
  console.log("[HlsAdPlayer] Destroying");
2642
+ settleViewability();
2643
+ fireNotUsedForUnplayedAds();
2067
2644
  destroyed = true;
2068
2645
  clearStallWatchdog();
2069
2646
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
@@ -2092,6 +2669,7 @@ function createHlsAdPlayer(contentVideo, options) {
2092
2669
  contentVideo.muted = originalMutedState;
2093
2670
  contentVideo.volume = Math.max(0, Math.min(1, originalVolume));
2094
2671
  teardownAdEventListeners();
2672
+ detachContainerHandlers();
2095
2673
  releaseAdDecoder();
2096
2674
  currentAd = void 0;
2097
2675
  currentAdMediaFile = void 0;
@@ -2533,6 +3111,7 @@ function resolveVastEnvironmentSignals(isCtv) {
2533
3111
  limitAdTracking = (_ref1 = platformLat !== null && platformLat !== void 0 ? platformLat : readStoredLimitAdTracking()) !== null && _ref1 !== void 0 ? _ref1 : false;
2534
3112
  }
2535
3113
  }
3114
+ var deviceTypeHint = resolveDeviceTypeHint(isCtv);
2536
3115
  if (isCtv) {
2537
3116
  return _object_spread_props(_object_spread({
2538
3117
  isCtv: true,
@@ -2544,10 +3123,10 @@ function resolveVastEnvironmentSignals(isCtv) {
2544
3123
  } : {}, deviceId && limitAdTracking != null ? {
2545
3124
  limitAdTracking: limitAdTracking
2546
3125
  } : {}), {
2547
- deviceTypeHint: 5
3126
+ deviceTypeHint: deviceTypeHint
2548
3127
  });
2549
3128
  }
2550
- return _object_spread({
3129
+ return _object_spread_props(_object_spread({
2551
3130
  isCtv: false
2552
3131
  }, deviceId ? {
2553
3132
  deviceId: deviceId
@@ -2555,7 +3134,142 @@ function resolveVastEnvironmentSignals(isCtv) {
2555
3134
  deviceIdType: deviceIdType
2556
3135
  } : {}, deviceId && limitAdTracking != null ? {
2557
3136
  limitAdTracking: limitAdTracking
2558
- } : {});
3137
+ } : {}), {
3138
+ deviceTypeHint: deviceTypeHint
3139
+ });
3140
+ }
3141
+ var DEVICE_TYPE_HINT = {
3142
+ FEATURE_PHONE: 1,
3143
+ SMARTPHONE: 2,
3144
+ DESKTOP: 3,
3145
+ TABLET: 4,
3146
+ CONNECTED_TV: 5,
3147
+ GAME_CONSOLE: 6,
3148
+ SET_TOP_BOX: 7
3149
+ };
3150
+ var DEVICE_TYPE_HINT_ALIASES = {
3151
+ featurephone: DEVICE_TYPE_HINT.FEATURE_PHONE,
3152
+ smartphone: DEVICE_TYPE_HINT.SMARTPHONE,
3153
+ mobile: DEVICE_TYPE_HINT.SMARTPHONE,
3154
+ phone: DEVICE_TYPE_HINT.SMARTPHONE,
3155
+ desktop: DEVICE_TYPE_HINT.DESKTOP,
3156
+ pc: DEVICE_TYPE_HINT.DESKTOP,
3157
+ tablet: DEVICE_TYPE_HINT.TABLET,
3158
+ ctv: DEVICE_TYPE_HINT.CONNECTED_TV,
3159
+ tv: DEVICE_TYPE_HINT.CONNECTED_TV,
3160
+ connectedtv: DEVICE_TYPE_HINT.CONNECTED_TV,
3161
+ smarttv: DEVICE_TYPE_HINT.CONNECTED_TV,
3162
+ console: DEVICE_TYPE_HINT.GAME_CONSOLE,
3163
+ gameconsole: DEVICE_TYPE_HINT.GAME_CONSOLE,
3164
+ stb: DEVICE_TYPE_HINT.SET_TOP_BOX,
3165
+ settopbox: DEVICE_TYPE_HINT.SET_TOP_BOX
3166
+ };
3167
+ var SET_TOP_BOX_UA = /LGSTB|\bSTB\b|STB\d|Set-?Top|SetTopBox|TV ?Box|Kaon|Humax|Arris|Technicolor|Sagemcom|Infomir|MAG\d{3}|KreaTV|Amino|Formuler|Dune ?HD|TiVo|MiBOX|Mi Box|SHIELD Android TV|\bAFT[A-Z]|Roku|AppleTV|Apple TV|CrKey/i;
3168
+ var GAME_CONSOLE_UA = /PlayStation|Xbox|Nintendo/i;
3169
+ var CONNECTED_TV_UA = /Web0S|webOS|Tizen|SMART-TV|SmartTV|NetCast|BRAVIA|HbbTV|VIDAA|Hisense|Philips|Viera|AQUOS|Android TV|Google TV|\bTV\b/i;
3170
+ var FEATURE_PHONE_UA = /KAIOS|MIDP|J2ME|Series40|Opera Mini/i;
3171
+ function resolveDeviceTypeHint(isCtv) {
3172
+ var override = readDeviceTypeHintOverride();
3173
+ if (override != null) return override;
3174
+ return inferDeviceTypeHint(isCtv);
3175
+ }
3176
+ function inferDeviceTypeHint(isCtv) {
3177
+ var fallback = isCtv ? DEVICE_TYPE_HINT.CONNECTED_TV : DEVICE_TYPE_HINT.DESKTOP;
3178
+ if (typeof navigator === "undefined") return fallback;
3179
+ var ua = navigator.userAgent || "";
3180
+ var maxTouchPoints = navigator.maxTouchPoints || 0;
3181
+ if (SET_TOP_BOX_UA.test(ua)) return DEVICE_TYPE_HINT.SET_TOP_BOX;
3182
+ if (GAME_CONSOLE_UA.test(ua)) return DEVICE_TYPE_HINT.GAME_CONSOLE;
3183
+ if (CONNECTED_TV_UA.test(ua) && !/Mobile/i.test(ua)) {
3184
+ return DEVICE_TYPE_HINT.CONNECTED_TV;
3185
+ }
3186
+ if (FEATURE_PHONE_UA.test(ua)) return DEVICE_TYPE_HINT.FEATURE_PHONE;
3187
+ if (/iPad/i.test(ua) || /Macintosh/i.test(ua) && maxTouchPoints > 1) {
3188
+ return DEVICE_TYPE_HINT.TABLET;
3189
+ }
3190
+ if (/iPhone|iPod/i.test(ua)) return DEVICE_TYPE_HINT.SMARTPHONE;
3191
+ if (/Android/i.test(ua)) {
3192
+ if (/Mobile/i.test(ua)) return DEVICE_TYPE_HINT.SMARTPHONE;
3193
+ if (maxTouchPoints === 0) return DEVICE_TYPE_HINT.SET_TOP_BOX;
3194
+ return DEVICE_TYPE_HINT.TABLET;
3195
+ }
3196
+ if (/Mobile|Windows Phone|BlackBerry/i.test(ua)) return DEVICE_TYPE_HINT.SMARTPHONE;
3197
+ return fallback;
3198
+ }
3199
+ function readDeviceTypeHintOverride() {
3200
+ if (typeof window === "undefined") return void 0;
3201
+ var keys = [
3202
+ "dth",
3203
+ "deviceTypeHint"
3204
+ ];
3205
+ var fromGlobal = normalizeDeviceTypeHint(window.__STORMCLOUD_DTH__);
3206
+ if (fromGlobal != null) return fromGlobal;
3207
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3208
+ try {
3209
+ for(var _iterator = getNativeBridgeSources()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
3210
+ var source = _step.value;
3211
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
3212
+ try {
3213
+ for(var _iterator1 = keys[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
3214
+ var key = _step1.value;
3215
+ var value = source === null || source === void 0 ? void 0 : source[key];
3216
+ var normalized = normalizeDeviceTypeHint(typeof value === "function" ? safeCall(value, source) : value);
3217
+ if (normalized != null) return normalized;
3218
+ }
3219
+ } catch (err) {
3220
+ _didIteratorError1 = true;
3221
+ _iteratorError1 = err;
3222
+ } finally{
3223
+ try {
3224
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
3225
+ _iterator1.return();
3226
+ }
3227
+ } finally{
3228
+ if (_didIteratorError1) {
3229
+ throw _iteratorError1;
3230
+ }
3231
+ }
3232
+ }
3233
+ }
3234
+ } catch (err) {
3235
+ _didIteratorError = true;
3236
+ _iteratorError = err;
3237
+ } finally{
3238
+ try {
3239
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
3240
+ _iterator.return();
3241
+ }
3242
+ } finally{
3243
+ if (_didIteratorError) {
3244
+ throw _iteratorError;
3245
+ }
3246
+ }
3247
+ }
3248
+ for(var _i = 0, _iter = [
3249
+ "stormcloud.dth",
3250
+ "dth"
3251
+ ]; _i < _iter.length; _i++){
3252
+ var key1 = _iter[_i];
3253
+ var normalized1 = normalizeDeviceTypeHint(readStoredString(key1));
3254
+ if (normalized1 != null) return normalized1;
3255
+ }
3256
+ return void 0;
3257
+ }
3258
+ function normalizeDeviceTypeHint(value) {
3259
+ if (typeof value === "number") {
3260
+ return Number.isInteger(value) && value >= 1 && value <= 7 ? value : void 0;
3261
+ }
3262
+ if (typeof value !== "string") return void 0;
3263
+ var trimmed = value.trim();
3264
+ if (/^[1-7]$/.test(trimmed)) return parseInt(trimmed, 10);
3265
+ return DEVICE_TYPE_HINT_ALIASES[trimmed.toLowerCase().replace(/[\s_-]/g, "")];
3266
+ }
3267
+ function safeCall(fn, thisArg) {
3268
+ try {
3269
+ return fn.call(thisArg);
3270
+ } catch (unused) {
3271
+ return void 0;
3272
+ }
2559
3273
  }
2560
3274
  function inferDeviceIdType() {
2561
3275
  if (typeof navigator === "undefined") {
@@ -2761,22 +3475,9 @@ function isZeroedAdId(value) {
2761
3475
  return ZEROED_AD_ID_PATTERNS.has(value.trim().toLowerCase());
2762
3476
  }
2763
3477
  function readNativeBridgeSignals() {
2764
- if (typeof window === "undefined") {
2765
- return {};
2766
- }
2767
- var candidates = [
2768
- window.__STORMCLOUD_CTV_AD_INFO__,
2769
- window.__STORMCLOUD_CTV__,
2770
- window.stormcloudCtv,
2771
- window.AiryTV,
2772
- window.Android,
2773
- window.webOS,
2774
- window.tizen,
2775
- window.amazon
2776
- ].filter(Boolean);
2777
3478
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2778
3479
  try {
2779
- for(var _iterator = candidates[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
3480
+ for(var _iterator = getNativeBridgeSources()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
2780
3481
  var source = _step.value;
2781
3482
  var contentUrl = readBridgeValue(source, [
2782
3483
  "contentUrl",
@@ -2851,6 +3552,21 @@ function readNativeBridgeSignals() {
2851
3552
  }
2852
3553
  return {};
2853
3554
  }
3555
+ function getNativeBridgeSources() {
3556
+ if (typeof window === "undefined") {
3557
+ return [];
3558
+ }
3559
+ return [
3560
+ window.__STORMCLOUD_CTV_AD_INFO__,
3561
+ window.__STORMCLOUD_CTV__,
3562
+ window.stormcloudCtv,
3563
+ window.AiryTV,
3564
+ window.Android,
3565
+ window.webOS,
3566
+ window.tizen,
3567
+ window.amazon
3568
+ ].filter(Boolean);
3569
+ }
2854
3570
  function readBridgeValue(source, keys) {
2855
3571
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2856
3572
  try {
@@ -7307,6 +8023,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7307
8023
  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 : "?"));
7308
8024
  }
7309
8025
  });
8026
+ adPlayer.on("ad_click", function() {
8027
+ _this.host.onAdClick();
8028
+ });
8029
+ adPlayer.on("ad_interaction", function(event) {
8030
+ if (event) _this.host.onAdInteraction(event);
8031
+ });
7310
8032
  adPlayer.on("all_ads_completed", function() {
7311
8033
  sendAdImpressionTracking(_this.host.config.licenseKey, {
7312
8034
  source: "hls",
@@ -8836,6 +9558,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8836
9558
  },
8837
9559
  generatePodVastUrl: function generatePodVastUrl(base, breakDurationMs) {
8838
9560
  return _this.generatePodVastUrl(base, breakDurationMs);
9561
+ },
9562
+ onAdClick: function onAdClick() {
9563
+ return _this.palNonce.sendAdClick();
9564
+ },
9565
+ onAdInteraction: function onAdInteraction(event) {
9566
+ return _this.palNonce.sendAdTouch(event);
8839
9567
  }
8840
9568
  }, this.timing, this.adConfig, this.cueManager, this.placeholder);
8841
9569
  }