stormcloud-video-player 0.8.55 → 0.8.56

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 (56) hide show
  1. package/dist/stormcloud-vp.min.js +2 -2
  2. package/lib/index.cjs +222 -82
  3. package/lib/index.cjs.map +1 -1
  4. package/lib/index.d.cts +0 -1
  5. package/lib/index.d.ts +0 -1
  6. package/lib/index.js +222 -82
  7. package/lib/index.js.map +1 -1
  8. package/lib/player/AdBreakOrchestrator.cjs +27 -29
  9. package/lib/player/AdBreakOrchestrator.cjs.map +1 -1
  10. package/lib/player/AdBreakOrchestrator.d.cts +1 -1
  11. package/lib/player/AdConfigManager.cjs +3 -14
  12. package/lib/player/AdConfigManager.cjs.map +1 -1
  13. package/lib/player/AdConfigManager.d.cts +1 -1
  14. package/lib/player/AdTimingService.d.cts +1 -1
  15. package/lib/player/HlsEngine.cjs.map +1 -1
  16. package/lib/player/HlsEngine.d.cts +1 -1
  17. package/lib/player/PlaceholderLayer.cjs.map +1 -1
  18. package/lib/player/PlayerControls.cjs.map +1 -1
  19. package/lib/player/PlayerControls.d.cts +1 -1
  20. package/lib/player/Scte35CueManager.cjs.map +1 -1
  21. package/lib/player/Scte35CueManager.d.cts +1 -1
  22. package/lib/player/Scte35Parser.cjs.map +1 -1
  23. package/lib/player/Scte35Parser.d.cts +1 -1
  24. package/lib/player/StormcloudVideoPlayer.cjs +222 -82
  25. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  26. package/lib/player/StormcloudVideoPlayer.d.cts +1 -1
  27. package/lib/player/playerTypes.cjs.map +1 -1
  28. package/lib/player/playerTypes.d.cts +1 -1
  29. package/lib/players/HlsPlayer.cjs +222 -82
  30. package/lib/players/HlsPlayer.cjs.map +1 -1
  31. package/lib/players/HlsPlayer.d.cts +1 -1
  32. package/lib/players/index.cjs +222 -82
  33. package/lib/players/index.cjs.map +1 -1
  34. package/lib/sdk/hlsAdPlayer.cjs +194 -52
  35. package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
  36. package/lib/sdk/hlsAdPlayer.d.cts +1 -1
  37. package/lib/{types-DpbgEvSH.d.cts → types-cTqIKw_D.d.cts} +0 -1
  38. package/lib/ui/StormcloudVideoPlayer.cjs +222 -82
  39. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  40. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  41. package/lib/utils/browserCompat.cjs.map +1 -1
  42. package/lib/utils/ctvVastSignals.cjs.map +1 -1
  43. package/lib/utils/devUrl.cjs.map +1 -1
  44. package/lib/utils/mqttClient.cjs.map +1 -1
  45. package/lib/utils/polyfills.cjs.map +1 -1
  46. package/lib/utils/tracking.cjs +7 -19
  47. package/lib/utils/tracking.cjs.map +1 -1
  48. package/lib/utils/tracking.d.cts +1 -1
  49. package/lib/utils/vastEnvironmentSignals.cjs +0 -4
  50. package/lib/utils/vastEnvironmentSignals.cjs.map +1 -1
  51. package/lib/utils/vastEnvironmentSignals.d.cts +1 -2
  52. package/lib/utils/vastMacros.cjs.map +1 -1
  53. package/lib/utils/vastTrackingMacros.cjs +185 -0
  54. package/lib/utils/vastTrackingMacros.cjs.map +1 -0
  55. package/lib/utils/vastTrackingMacros.d.cts +19 -0
  56. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { Component } from 'react';
2
- import { a as StormcloudVideoPlayerConfig } from '../types-DpbgEvSH.cjs';
2
+ import { a as StormcloudVideoPlayerConfig } from '../types-cTqIKw_D.cjs';
3
3
 
4
4
  interface HlsPlayerProps extends StormcloudVideoPlayerConfig {
5
5
  onMount?: (player: any) => void;
@@ -603,15 +603,147 @@ function logBrowserInfo() {
603
603
  userAgent: navigator.userAgent
604
604
  }));
605
605
  }
606
+ // src/utils/vastTrackingMacros.ts
607
+ var VAST_MACRO_PATTERN = /\[([A-Z][A-Z0-9_]*)\]/g;
608
+ var CACHEBUSTING_ALIASES = /\[CACHEBUSTER\]|\[cachebuster\]|\[timestamp\]|\[random\]|\[INSERT_RANDOM_NUMBER\]|%%CACHEBUSTER%%|%%CACHEBUST%%|\$\{CACHEBUSTER\}|\$\{CACHEBUSTING\}|\{cachebuster\}|\{random\}/g;
609
+ var KNOWN_VAST_MACROS = /* @__PURE__ */ new Set([
610
+ "ADCATEGORIES",
611
+ "ADCOUNT",
612
+ "ADPLAYHEAD",
613
+ "ADSERVINGID",
614
+ "ADTYPE",
615
+ "APIFRAMEWORKS",
616
+ "APPBUNDLE",
617
+ "ASSETURI",
618
+ "BLOCKEDADCATEGORIES",
619
+ "BREAKMAXADLENGTH",
620
+ "BREAKMAXADS",
621
+ "BREAKMAXDURATION",
622
+ "BREAKMINADLENGTH",
623
+ "BREAKPOSITION",
624
+ "CACHEBUSTING",
625
+ "CLICKPOS",
626
+ "CLICKTYPE",
627
+ "CLIENTUA",
628
+ "CONTENTID",
629
+ "CONTENTPLAYHEAD",
630
+ "CONTENTURI",
631
+ "DEVICEIP",
632
+ "DEVICEUA",
633
+ "DOMAIN",
634
+ "ERRORCODE",
635
+ "EXTENSIONS",
636
+ "GDPRCONSENT",
637
+ "IFA",
638
+ "IFATYPE",
639
+ "INVENTORYSTATE",
640
+ "LATLONG",
641
+ "LIMITADTRACKING",
642
+ "MEDIAMIME",
643
+ "MEDIAPLAYHEAD",
644
+ "OMIDPARTNER",
645
+ "PAGEURL",
646
+ "PLACEMENTTYPE",
647
+ "PLAYERCAPABILITIES",
648
+ "PLAYERSIZE",
649
+ "PLAYERSTATE",
650
+ "PODSEQUENCE",
651
+ "REGULATIONS",
652
+ "SERVERSIDE",
653
+ "SERVERUA",
654
+ "TIMESTAMP",
655
+ "TRANSACTIONID",
656
+ "UNIVERSALADID",
657
+ "VASTVERSIONS",
658
+ "VERIFICATIONVENDORS"
659
+ ]);
660
+ function generateCachebuster() {
661
+ return String(Math.floor(Math.random() * 9e7) + 1e7);
662
+ }
663
+ function formatPlayhead(seconds) {
664
+ if (seconds == null || !Number.isFinite(seconds) || seconds < 0) {
665
+ return void 0;
666
+ }
667
+ var total = Math.floor(seconds);
668
+ var hh = String(Math.floor(total / 3600)).padStart(2, "0");
669
+ var mm = String(Math.floor(total % 3600 / 60)).padStart(2, "0");
670
+ var ss = String(total % 60).padStart(2, "0");
671
+ var ms = String(Math.floor((seconds - total) * 1e3)).padStart(3, "0");
672
+ return "".concat(hh, ":").concat(mm, ":").concat(ss, ".").concat(ms);
673
+ }
674
+ function resolveMacroValues(ctx) {
675
+ var values = /* @__PURE__ */ new Map();
676
+ var set = function set(key, value) {
677
+ if (value != null && value !== "") values.set(key, value);
678
+ };
679
+ var adPlayhead = formatPlayhead(ctx.adPlayheadSeconds);
680
+ set("ADPLAYHEAD", adPlayhead);
681
+ set("MEDIAPLAYHEAD", adPlayhead);
682
+ set("CONTENTPLAYHEAD", formatPlayhead(ctx.contentPlayheadSeconds));
683
+ set("ASSETURI", ctx.assetUri);
684
+ set("MEDIAMIME", ctx.mediaMime);
685
+ set("TIMESTAMP", (/* @__PURE__ */ new Date()).toISOString());
686
+ set("SERVERSIDE", "0");
687
+ if (ctx.errorCode != null && ctx.errorCode !== "") {
688
+ set("ERRORCODE", String(ctx.errorCode));
689
+ }
690
+ if (ctx.playerWidth != null && ctx.playerHeight != null) {
691
+ set("PLAYERSIZE", "".concat(Math.round(ctx.playerWidth), ",").concat(Math.round(ctx.playerHeight)));
692
+ }
693
+ set("IFA", ctx.deviceId);
694
+ set("IFATYPE", ctx.deviceIdType);
695
+ if (ctx.limitAdTracking != null) {
696
+ set("LIMITADTRACKING", ctx.limitAdTracking ? "1" : "0");
697
+ }
698
+ set("PAGEURL", ctx.pageUrl);
699
+ if (ctx.pageUrl) {
700
+ try {
701
+ set("DOMAIN", new URL(ctx.pageUrl).hostname);
702
+ } catch (unused) {}
703
+ }
704
+ set("CLIENTUA", ctx.clientUa);
705
+ set("DEVICEUA", ctx.clientUa);
706
+ if (ctx.podSequence != null) {
707
+ set("PODSEQUENCE", String(ctx.podSequence));
708
+ }
709
+ return values;
710
+ }
711
+ function substituteVastTrackingMacros(url) {
712
+ var ctx = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
713
+ if (!url) return url;
714
+ var values = resolveMacroValues(ctx);
715
+ return url.replace(CACHEBUSTING_ALIASES, function() {
716
+ return generateCachebuster();
717
+ }).replace(VAST_MACRO_PATTERN, function(match, name) {
718
+ if (name === "CACHEBUSTING") return generateCachebuster();
719
+ var value = values.get(name);
720
+ if (value != null) return encodeURIComponent(value);
721
+ return KNOWN_VAST_MACROS.has(name) ? "" : match;
722
+ });
723
+ }
606
724
  // src/sdk/hlsAdPlayer.ts
607
725
  var MAX_VAST_WRAPPER_DEPTH = 5;
726
+ var BEACON_TIMEOUT_MS = 3e4;
727
+ var inFlightBeacons = /* @__PURE__ */ new Set();
728
+ var VAST_ERROR_MEDIA_DISPLAY = 405;
729
+ function toVastMediaErrorCode(error) {
730
+ switch(error === null || error === void 0 ? void 0 : error.code){
731
+ case 1:
732
+ return 400;
733
+ case 2:
734
+ return 401;
735
+ case 4:
736
+ return 403;
737
+ default:
738
+ return VAST_ERROR_MEDIA_DISPLAY;
739
+ }
740
+ }
608
741
  function createHlsAdPlayer(contentVideo, options) {
609
742
  var _contentVideo_AD_VIDEO_PROP, _contentVideo_AD_CONTAINER_PROP;
610
743
  var adPlaying = false;
611
744
  var originalMutedState = false;
612
745
  var originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));
613
746
  var listeners = /* @__PURE__ */ new Map();
614
- var licenseKey = options === null || options === void 0 ? void 0 : options.licenseKey;
615
747
  var mainHlsInstance = options === null || options === void 0 ? void 0 : options.mainHlsInstance;
616
748
  var AD_CONTAINER_PROP = "__stormcloudAdContainer";
617
749
  var AD_VIDEO_PROP = "__stormcloudAdVideo";
@@ -622,10 +754,9 @@ function createHlsAdPlayer(contentVideo, options) {
622
754
  var adContainerEl = (_contentVideo_AD_CONTAINER_PROP = contentVideo[AD_CONTAINER_PROP]) !== null && _contentVideo_AD_CONTAINER_PROP !== void 0 ? _contentVideo_AD_CONTAINER_PROP : void 0;
623
755
  var adEventHandlers = [];
624
756
  var currentAd;
625
- var currentAdMediaUrl;
757
+ var currentAdMediaFile;
626
758
  var podAds = [];
627
759
  var podIndex = 0;
628
- var sessionId;
629
760
  var preloadedAds = /* @__PURE__ */ new Map();
630
761
  var preloadingAds = /* @__PURE__ */ new Map();
631
762
  var destroyed = false;
@@ -675,21 +806,47 @@ function createHlsAdPlayer(contentVideo, options) {
675
806
  }
676
807
  }
677
808
  }
678
- function generateSessionId() {
679
- return "session-".concat(Date.now(), "-").concat(Math.random().toString(36).substr(2, 9));
809
+ function buildTrackingMacroContext(errorCode) {
810
+ var context = {
811
+ adPlayheadSeconds: adVideoElement === null || adVideoElement === void 0 ? void 0 : adVideoElement.currentTime,
812
+ contentPlayheadSeconds: contentVideo.currentTime,
813
+ assetUri: currentAdMediaFile === null || currentAdMediaFile === void 0 ? void 0 : currentAdMediaFile.url,
814
+ mediaMime: currentAdMediaFile === null || currentAdMediaFile === void 0 ? void 0 : currentAdMediaFile.type,
815
+ playerWidth: (adVideoElement === null || adVideoElement === void 0 ? void 0 : adVideoElement.clientWidth) || (adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.clientWidth),
816
+ playerHeight: (adVideoElement === null || adVideoElement === void 0 ? void 0 : adVideoElement.clientHeight) || (adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.clientHeight)
817
+ };
818
+ if (errorCode != null) {
819
+ context.errorCode = errorCode;
820
+ }
821
+ if (typeof navigator !== "undefined" && navigator.userAgent) {
822
+ context.clientUa = navigator.userAgent;
823
+ }
824
+ if (typeof location !== "undefined" && location.href) {
825
+ context.pageUrl = location.href;
826
+ }
827
+ if (podAds.length > 0) {
828
+ context.podSequence = podIndex + 1;
829
+ }
830
+ return context;
680
831
  }
681
- function fireTrackingPixels(urls) {
832
+ function fireTrackingPixels(urls, options2) {
682
833
  if (!urls || urls.length === 0) return;
834
+ var macroContext = buildTrackingMacroContext(options2 === null || options2 === void 0 ? void 0 : options2.errorCode);
683
835
  urls.forEach(function(url) {
684
836
  try {
685
- var trackingUrl = url;
686
- if (sessionId) {
687
- trackingUrl = "".concat(trackingUrl).concat(trackingUrl.includes("?") ? "&" : "?", "session_id=").concat(sessionId);
688
- }
689
- if (licenseKey) {
690
- trackingUrl = "".concat(trackingUrl).concat(trackingUrl.includes("?") ? "&" : "?", "license_key=").concat(licenseKey);
691
- }
837
+ var trackingUrl = substituteVastTrackingMacros(url, macroContext);
692
838
  var img = new Image(1, 1);
839
+ inFlightBeacons.add(img);
840
+ var timeoutId;
841
+ var release = function release() {
842
+ if (timeoutId !== void 0) clearTimeout(timeoutId);
843
+ img.onload = null;
844
+ img.onerror = null;
845
+ inFlightBeacons.delete(img);
846
+ };
847
+ img.onload = release;
848
+ img.onerror = release;
849
+ timeoutId = setTimeout(release, BEACON_TIMEOUT_MS);
693
850
  img.src = trackingUrl;
694
851
  console.log("[HlsAdPlayer] Fired tracking pixel: ".concat(trackingUrl));
695
852
  } catch (error) {
@@ -1328,7 +1485,9 @@ function createHlsAdPlayer(contentVideo, options) {
1328
1485
  if (!adPlaying) return;
1329
1486
  console.error("[HlsAdPlayer] Ad video error:", e);
1330
1487
  if (currentAd) {
1331
- fireTrackingPixels(currentAd.trackingUrls.error);
1488
+ fireTrackingPixels(currentAd.trackingUrls.error, {
1489
+ errorCode: toVastMediaErrorCode(adVideoElement === null || adVideoElement === void 0 ? void 0 : adVideoElement.error)
1490
+ });
1332
1491
  }
1333
1492
  if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1334
1493
  return;
@@ -1440,9 +1599,9 @@ function createHlsAdPlayer(contentVideo, options) {
1440
1599
  delete contentVideo.dataset.stormcloudAdPlaying;
1441
1600
  }
1442
1601
  }
1443
- function resetQuartileTracking() {
1602
+ function resetQuartileTracking(preserveImpression) {
1444
1603
  trackingFired = {
1445
- impression: false,
1604
+ impression: preserveImpression ? trackingFired.impression : false,
1446
1605
  start: false,
1447
1606
  firstQuartile: false,
1448
1607
  midpoint: false,
@@ -1450,27 +1609,6 @@ function createHlsAdPlayer(contentVideo, options) {
1450
1609
  complete: false
1451
1610
  };
1452
1611
  }
1453
- function markCreativeRendered(reason) {
1454
- if (!currentAd || trackingFired.impression) return;
1455
- trackingFired.impression = true;
1456
- fireTrackingPixels(currentAd.trackingUrls.impression);
1457
- console.log("[HlsAdPlayer] Impression (pod ".concat(podIndex + 1, "/").concat(podAds.length || 1, ", reason: ").concat(reason, "): ").concat(currentAd.title));
1458
- emit("ad_impression", {
1459
- adId: currentAd.id,
1460
- title: currentAd.title,
1461
- durationSeconds: currentAd.duration,
1462
- mediaUrl: currentAdMediaUrl,
1463
- podIndex: podIndex,
1464
- podSize: podAds.length || 1,
1465
- reason: reason
1466
- });
1467
- }
1468
- function canDetectBlackFrames() {
1469
- if (!adVideoElement) return false;
1470
- if (requiresMediaPipelineResetAfterAds()) return false;
1471
- if (isRvfcSupported(adVideoElement)) return true;
1472
- return getDecodedVideoFrameCount(adVideoElement) !== void 0;
1473
- }
1474
1612
  function isRvfcSupported(video) {
1475
1613
  return typeof video.requestVideoFrameCallback === "function";
1476
1614
  }
@@ -1533,7 +1671,7 @@ function createHlsAdPlayer(contentVideo, options) {
1533
1671
  if (!mediaFile) {
1534
1672
  throw new Error("No media file available for ad");
1535
1673
  }
1536
- currentAdMediaUrl = mediaFile.url;
1674
+ currentAdMediaFile = mediaFile;
1537
1675
  var isHlsAd = isHlsMediaFile(mediaFile);
1538
1676
  console.log("[HlsAdPlayer] Loading ".concat(isHlsAd ? "HLS" : "progressive", " ad (pod ").concat(podIndex + 1, "/").concat(podAds.length || 1, ") from: ").concat(mediaFile.url));
1539
1677
  if (isHlsAd && import_hls.default.isSupported()) {
@@ -1577,9 +1715,11 @@ function createHlsAdPlayer(contentVideo, options) {
1577
1715
  }
1578
1716
  podIndex++;
1579
1717
  currentAd = podAds[podIndex];
1580
- currentAdMediaUrl = void 0;
1581
- resetQuartileTracking();
1718
+ currentAdMediaFile = void 0;
1719
+ resetQuartileTracking(false);
1582
1720
  if (currentAd) {
1721
+ fireTrackingPixels(currentAd.trackingUrls.impression);
1722
+ trackingFired.impression = true;
1583
1723
  console.log("[HlsAdPlayer] Advancing to next pod ad (".concat(podIndex + 1, "/").concat(podAds.length, "): ").concat(currentAd.title, ", duration: ").concat(currentAd.duration, "s"));
1584
1724
  }
1585
1725
  clearStallWatchdog();
@@ -1618,7 +1758,9 @@ function createHlsAdPlayer(contentVideo, options) {
1618
1758
  console.warn("[HlsAdPlayer] Ad playback stalled (no progress) - recovering to avoid a blank frame");
1619
1759
  clearStallWatchdog();
1620
1760
  if (currentAd) {
1621
- fireTrackingPixels(currentAd.trackingUrls.error);
1761
+ fireTrackingPixels(currentAd.trackingUrls.error, {
1762
+ errorCode: VAST_ERROR_MEDIA_DISPLAY
1763
+ });
1622
1764
  }
1623
1765
  if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1624
1766
  return;
@@ -1674,7 +1816,6 @@ function createHlsAdPlayer(contentVideo, options) {
1674
1816
  if (!adPlaying) return;
1675
1817
  sawDecodedVideoFrame = true;
1676
1818
  markRvfcReliable();
1677
- markCreativeRendered("first_presented_frame");
1678
1819
  };
1679
1820
  try {
1680
1821
  videoFrameCallbackHandle = rvfc.call(adVideoElement, onPresentedFrame);
@@ -1687,7 +1828,9 @@ function createHlsAdPlayer(contentVideo, options) {
1687
1828
  console.warn("[HlsAdPlayer] Ad audio is advancing but no video frames decoded (black screen) - skipping this creative");
1688
1829
  clearStallWatchdog();
1689
1830
  if (currentAd) {
1690
- fireTrackingPixels(currentAd.trackingUrls.error);
1831
+ fireTrackingPixels(currentAd.trackingUrls.error, {
1832
+ errorCode: VAST_ERROR_MEDIA_DISPLAY
1833
+ });
1691
1834
  }
1692
1835
  if (podIndex < podAds.length - 1 && advanceToNextPodAd()) {
1693
1836
  return;
@@ -1709,7 +1852,6 @@ function createHlsAdPlayer(contentVideo, options) {
1709
1852
  if (decodedFrames !== void 0 && decodedFrames > 0) {
1710
1853
  sawDecodedVideoFrame = true;
1711
1854
  markFrameCounterReliable();
1712
- markCreativeRendered("decoded_frames");
1713
1855
  } else if (adVideoElement.currentTime >= BLACKFRAME_MIN_PLAYBACK_S) {
1714
1856
  var timeAdvancing = adVideoElement.currentTime > lastAdProgressPosition + 0.05;
1715
1857
  if (isRvfcSupported(adVideoElement) && timeAdvancing && isRvfcReliable()) {
@@ -1722,9 +1864,6 @@ function createHlsAdPlayer(contentVideo, options) {
1722
1864
  }
1723
1865
  }
1724
1866
  }
1725
- if (!trackingFired.impression && !adVideoElement.paused && adVideoElement.currentTime >= BLACKFRAME_MIN_PLAYBACK_S && (sawDecodedVideoFrame || !canDetectBlackFrames())) {
1726
- markCreativeRendered(sawDecodedVideoFrame ? "decoded_frames" : "sustained_playback");
1727
- }
1728
1867
  if (adVideoElement.currentTime > lastAdProgressPosition + 0.05) {
1729
1868
  noteAdProgress();
1730
1869
  return;
@@ -1825,7 +1964,6 @@ function createHlsAdPlayer(contentVideo, options) {
1825
1964
  ,
1826
1965
  6
1827
1966
  ]);
1828
- sessionId = generateSessionId();
1829
1967
  ads = [];
1830
1968
  if (!preloadedAds.has(vastTagUrl)) return [
1831
1969
  3,
@@ -1863,9 +2001,12 @@ function createHlsAdPlayer(contentVideo, options) {
1863
2001
  podAds = ads;
1864
2002
  podIndex = 0;
1865
2003
  currentAd = ads[0];
1866
- currentAdMediaUrl = void 0;
1867
- resetQuartileTracking();
2004
+ currentAdMediaFile = void 0;
1868
2005
  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"));
2006
+ if (currentAd) {
2007
+ fireTrackingPixels(currentAd.trackingUrls.impression);
2008
+ trackingFired.impression = true;
2009
+ }
1869
2010
  return [
1870
2011
  2,
1871
2012
  Promise.resolve()
@@ -1905,7 +2046,7 @@ function createHlsAdPlayer(contentVideo, options) {
1905
2046
  contentVideo[AD_VIDEO_PROP] = adVideoElement;
1906
2047
  }
1907
2048
  setupAdEventListeners();
1908
- resetQuartileTracking();
2049
+ resetQuartileTracking(true);
1909
2050
  contentVolume = contentVideo.volume;
1910
2051
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1911
2052
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
@@ -1995,7 +2136,7 @@ function createHlsAdPlayer(contentVideo, options) {
1995
2136
  }
1996
2137
  }
1997
2138
  currentAd = void 0;
1998
- currentAdMediaUrl = void 0;
2139
+ currentAdMediaFile = void 0;
1999
2140
  podAds = [];
2000
2141
  podIndex = 0;
2001
2142
  return [
@@ -2036,6 +2177,7 @@ function createHlsAdPlayer(contentVideo, options) {
2036
2177
  teardownAdEventListeners();
2037
2178
  releaseAdDecoder();
2038
2179
  currentAd = void 0;
2180
+ currentAdMediaFile = void 0;
2039
2181
  podAds = [];
2040
2182
  podIndex = 0;
2041
2183
  listeners.clear();
@@ -3329,19 +3471,8 @@ function buildPlayerMetricEvent() {
3329
3471
  }).apply(this, arguments);
3330
3472
  }
3331
3473
  function publishTracking(licenseKey, channel, body) {
3332
- var debug = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : false;
3333
3474
  ensureMQTTClient();
3334
- var topic = buildPlayerTopic(licenseKey, channel);
3335
- var published = publishMQTT(topic, body);
3336
- if (debug) {
3337
- console.log("[StormcloudVideoPlayer] publishTracking:", JSON.stringify({
3338
- licenseKey: licenseKey,
3339
- channel: channel,
3340
- topic: topic,
3341
- body: body,
3342
- published: published
3343
- }, null, 2));
3344
- }
3475
+ publishMQTT(buildPlayerTopic(licenseKey, channel), body);
3345
3476
  }
3346
3477
  function sendInitialTracking(_0) {
3347
3478
  return _async_to_generator(function(licenseKey) {
@@ -3375,7 +3506,7 @@ function sendInitialTracking(_0) {
3375
3506
  events: [
3376
3507
  metricEvent
3377
3508
  ]
3378
- }, !!context.debugAdTiming);
3509
+ });
3379
3510
  return [
3380
3511
  3,
3381
3512
  4
@@ -3485,7 +3616,7 @@ function sendAdLoadedTracking(_0, _1) {
3485
3616
  }
3486
3617
  function sendAdImpressionTracking(_0, _1) {
3487
3618
  return _async_to_generator(function(licenseKey, adImpressionInfo) {
3488
- var context, _adImpressionInfo_adUrl, metricEvent, error;
3619
+ var context, metricEvent, error;
3489
3620
  var _arguments = arguments;
3490
3621
  return _ts_generator(this, function(_state) {
3491
3622
  switch(_state.label){
@@ -3510,18 +3641,17 @@ function sendAdImpressionTracking(_0, _1) {
3510
3641
  ];
3511
3642
  case 2:
3512
3643
  metricEvent = _state.sent();
3513
- publishTracking(licenseKey, "heartbeat", metricEvent, !!context.debugAdTiming);
3644
+ publishTracking(licenseKey, "heartbeat", metricEvent);
3514
3645
  publishTracking(licenseKey, "impressions", {
3515
3646
  events: [
3516
3647
  {
3517
- event_id: createUuid(),
3518
3648
  player_id: metricEvent.player_id,
3519
3649
  ad_played_count: 1,
3520
- ad_url: (_adImpressionInfo_adUrl = adImpressionInfo.adUrl) !== null && _adImpressionInfo_adUrl !== void 0 ? _adImpressionInfo_adUrl : adImpressionInfo.adId,
3650
+ ad_url: adImpressionInfo.adUrl,
3521
3651
  capture_at: adImpressionInfo.timestamp
3522
3652
  }
3523
3653
  ]
3524
- }, !!context.debugAdTiming);
3654
+ });
3525
3655
  return [
3526
3656
  3,
3527
3657
  4
@@ -3566,7 +3696,7 @@ function sendHeartbeat(_0) {
3566
3696
  ];
3567
3697
  case 2:
3568
3698
  heartbeatData = _state.sent();
3569
- publishTracking(licenseKey, "heartbeat", heartbeatData, !!context.debugAdTiming);
3699
+ publishTracking(licenseKey, "heartbeat", heartbeatData);
3570
3700
  return [
3571
3701
  3,
3572
3702
  4
@@ -7360,22 +7490,27 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7360
7490
  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 : "?"));
7361
7491
  }
7362
7492
  });
7363
- adPlayer.on("ad_impression", function(payload) {
7364
- var impressionInfo = {
7493
+ adPlayer.on("all_ads_completed", function() {
7494
+ sendAdImpressionTracking(_this.host.config.licenseKey, {
7365
7495
  source: "hls",
7366
7496
  adIndex: _this.timing.currentAdIndex,
7367
7497
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
7368
- };
7369
- if (payload === null || payload === void 0 ? void 0 : payload.adId) impressionInfo.adId = payload.adId;
7370
- if (payload === null || payload === void 0 ? void 0 : payload.mediaUrl) impressionInfo.adUrl = payload.mediaUrl;
7371
- if ((payload === null || payload === void 0 ? void 0 : payload.durationSeconds) != null) {
7372
- impressionInfo.durationSeconds = payload.durationSeconds;
7498
+ }, _this.analyticsContext).catch(function() {});
7499
+ _this.timing.recordPlayedAdDuration(_this.host.getAdPlayer());
7500
+ var remaining = _this.timing.getRemainingAdMs();
7501
+ _this.timing.consecutiveFailures = 0;
7502
+ if (_this.debug) {
7503
+ console.log("[StormcloudVideoPlayer] all_ads_completed event received - remaining=".concat(remaining, "ms, queued ads=").concat(_this.adRequestQueue.length), _this.adRequestQueue.length > 0 ? {
7504
+ queuedUrls: _to_consumable_array(_this.adRequestQueue)
7505
+ } : {});
7373
7506
  }
7374
- sendAdImpressionTracking(_this.host.config.licenseKey, impressionInfo, _this.analyticsContext).catch(function(error) {
7375
- if (_this.debug) {
7376
- console.warn("[AdBreakOrchestrator] Failed to send ad impression tracking:", error);
7507
+ if (_this.inAdBreak) {
7508
+ if (_this.timing.hasTimeToStartAnotherAd()) {
7509
+ _this.tryNextAvailableAdWithRateLimit();
7510
+ } else {
7511
+ _this.handleAdPodComplete();
7377
7512
  }
7378
- });
7513
+ }
7379
7514
  });
7380
7515
  adPlayer.on("ad_error", function(errorPayload) {
7381
7516
  var errorMessage = "Ad playback failed";
@@ -7418,6 +7553,11 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
7418
7553
  }
7419
7554
  });
7420
7555
  adPlayer.on("content_resume", function() {
7556
+ sendAdImpressionTracking(_this.host.config.licenseKey, {
7557
+ source: "hls",
7558
+ adIndex: _this.timing.currentAdIndex,
7559
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
7560
+ }, _this.analyticsContext).catch(function() {});
7421
7561
  if (!_this.host.video.muted) {
7422
7562
  _this.host.video.muted = true;
7423
7563
  if (_this.debug) {