stormcloud-video-player 0.3.54 → 0.3.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.
@@ -2661,9 +2661,6 @@ function sendInitialTracking(licenseKey) {
2661
2661
  headers = {
2662
2662
  "Content-Type": "application/json"
2663
2663
  };
2664
- if (licenseKey) {
2665
- headers["Authorization"] = "Bearer ".concat(licenseKey);
2666
- }
2667
2664
  return [
2668
2665
  4,
2669
2666
  fetch("https://adstorm.co/api-adstorm-dev/adstorm/player-tracking/track", {
@@ -2769,6 +2766,234 @@ function sendHeartbeat(licenseKey) {
2769
2766
  });
2770
2767
  })();
2771
2768
  }
2769
+ var TRACK_API_URL = "https://adstorm.co/api-adstorm-dev/adstorm/player-tracking/track";
2770
+ function mapToAdTrackingSource(source, adPlayerType) {
2771
+ if (source === "prebid" || source === "ima" || source === "hls") {
2772
+ return source;
2773
+ }
2774
+ if (source === "preload" || source === "ssp") {
2775
+ return source === "ssp" ? "prebid" : "ima";
2776
+ }
2777
+ return adPlayerType === "hls" ? "hls" : "ima";
2778
+ }
2779
+ function postAdTracking(licenseKey, body) {
2780
+ return _async_to_generator(function() {
2781
+ var headers, response;
2782
+ return _ts_generator(this, function(_state) {
2783
+ switch(_state.label){
2784
+ case 0:
2785
+ headers = {
2786
+ "Content-Type": "application/json"
2787
+ };
2788
+ if (licenseKey) {
2789
+ headers["Authorization"] = "Bearer ".concat(licenseKey);
2790
+ }
2791
+ return [
2792
+ 4,
2793
+ fetch(TRACK_API_URL, {
2794
+ method: "POST",
2795
+ headers: headers,
2796
+ body: JSON.stringify(body)
2797
+ })
2798
+ ];
2799
+ case 1:
2800
+ response = _state.sent();
2801
+ if (!response.ok) {
2802
+ throw new Error("HTTP error! status: ".concat(response.status));
2803
+ }
2804
+ return [
2805
+ 4,
2806
+ response.json()
2807
+ ];
2808
+ case 2:
2809
+ _state.sent();
2810
+ return [
2811
+ 2
2812
+ ];
2813
+ }
2814
+ });
2815
+ })();
2816
+ }
2817
+ function sendAdDetectTracking(licenseKey, payload) {
2818
+ return _async_to_generator(function() {
2819
+ var _payload_source, _payload_timestamp, clientInfo, browserId, adDetectInfo, body, error;
2820
+ return _ts_generator(this, function(_state) {
2821
+ switch(_state.label){
2822
+ case 0:
2823
+ _state.trys.push([
2824
+ 0,
2825
+ 3,
2826
+ ,
2827
+ 4
2828
+ ]);
2829
+ clientInfo = getClientInfo();
2830
+ return [
2831
+ 4,
2832
+ getBrowserID(clientInfo)
2833
+ ];
2834
+ case 1:
2835
+ browserId = _state.sent();
2836
+ adDetectInfo = _object_spread({
2837
+ source: (_payload_source = payload.source) !== null && _payload_source !== void 0 ? _payload_source : "scte35",
2838
+ timestamp: (_payload_timestamp = payload.timestamp) !== null && _payload_timestamp !== void 0 ? _payload_timestamp : /* @__PURE__ */ new Date().toISOString()
2839
+ }, payload.durationSeconds != null && {
2840
+ durationSeconds: payload.durationSeconds
2841
+ }, payload.ptsSeconds != null && {
2842
+ ptsSeconds: payload.ptsSeconds
2843
+ }, payload.detectedAtFragmentSn != null && {
2844
+ detectedAtFragmentSn: payload.detectedAtFragmentSn
2845
+ });
2846
+ body = _object_spread_props(_object_spread({
2847
+ browserId: browserId
2848
+ }, clientInfo, licenseKey && {
2849
+ licenseKey: licenseKey
2850
+ }), {
2851
+ adDetectInfo: adDetectInfo
2852
+ });
2853
+ return [
2854
+ 4,
2855
+ postAdTracking(licenseKey, body)
2856
+ ];
2857
+ case 2:
2858
+ _state.sent();
2859
+ return [
2860
+ 3,
2861
+ 4
2862
+ ];
2863
+ case 3:
2864
+ error = _state.sent();
2865
+ console.error("[StormcloudVideoPlayer] Error sending ad-detect tracking:", error);
2866
+ return [
2867
+ 3,
2868
+ 4
2869
+ ];
2870
+ case 4:
2871
+ return [
2872
+ 2
2873
+ ];
2874
+ }
2875
+ });
2876
+ })();
2877
+ }
2878
+ function sendAdLoadedTracking(licenseKey, payload, adPlayerType) {
2879
+ return _async_to_generator(function() {
2880
+ var clientInfo, browserId, source, adLoadedInfo, body, error;
2881
+ return _ts_generator(this, function(_state) {
2882
+ switch(_state.label){
2883
+ case 0:
2884
+ _state.trys.push([
2885
+ 0,
2886
+ 3,
2887
+ ,
2888
+ 4
2889
+ ]);
2890
+ clientInfo = getClientInfo();
2891
+ return [
2892
+ 4,
2893
+ getBrowserID(clientInfo)
2894
+ ];
2895
+ case 1:
2896
+ browserId = _state.sent();
2897
+ source = mapToAdTrackingSource(payload.source, adPlayerType);
2898
+ adLoadedInfo = _object_spread({
2899
+ source: source,
2900
+ timestamp: /* @__PURE__ */ new Date().toISOString()
2901
+ }, payload.vastUrl != null && {
2902
+ vastUrl: payload.vastUrl
2903
+ }, payload.durationSeconds != null && {
2904
+ durationSeconds: payload.durationSeconds
2905
+ });
2906
+ body = _object_spread_props(_object_spread({
2907
+ browserId: browserId
2908
+ }, clientInfo, licenseKey && {
2909
+ licenseKey: licenseKey
2910
+ }), {
2911
+ adLoadedInfo: adLoadedInfo
2912
+ });
2913
+ return [
2914
+ 4,
2915
+ postAdTracking(licenseKey, body)
2916
+ ];
2917
+ case 2:
2918
+ _state.sent();
2919
+ return [
2920
+ 3,
2921
+ 4
2922
+ ];
2923
+ case 3:
2924
+ error = _state.sent();
2925
+ console.error("[StormcloudVideoPlayer] Error sending ad-loaded tracking:", error);
2926
+ return [
2927
+ 3,
2928
+ 4
2929
+ ];
2930
+ case 4:
2931
+ return [
2932
+ 2
2933
+ ];
2934
+ }
2935
+ });
2936
+ })();
2937
+ }
2938
+ function sendAdImpressionTracking(licenseKey, payload, adPlayerType) {
2939
+ return _async_to_generator(function() {
2940
+ var clientInfo, browserId, source, adImpressionInfo, body, error;
2941
+ return _ts_generator(this, function(_state) {
2942
+ switch(_state.label){
2943
+ case 0:
2944
+ _state.trys.push([
2945
+ 0,
2946
+ 3,
2947
+ ,
2948
+ 4
2949
+ ]);
2950
+ clientInfo = getClientInfo();
2951
+ return [
2952
+ 4,
2953
+ getBrowserID(clientInfo)
2954
+ ];
2955
+ case 1:
2956
+ browserId = _state.sent();
2957
+ source = mapToAdTrackingSource(payload.source, adPlayerType);
2958
+ adImpressionInfo = _object_spread({
2959
+ source: source,
2960
+ adIndex: payload.adIndex,
2961
+ timestamp: /* @__PURE__ */ new Date().toISOString()
2962
+ }, payload.durationSeconds != null && {
2963
+ durationSeconds: payload.durationSeconds
2964
+ });
2965
+ body = _object_spread_props(_object_spread({
2966
+ browserId: browserId
2967
+ }, clientInfo, licenseKey && {
2968
+ licenseKey: licenseKey
2969
+ }), {
2970
+ adImpressionInfo: adImpressionInfo
2971
+ });
2972
+ return [
2973
+ 4,
2974
+ postAdTracking(licenseKey, body)
2975
+ ];
2976
+ case 2:
2977
+ _state.sent();
2978
+ return [
2979
+ 3,
2980
+ 4
2981
+ ];
2982
+ case 3:
2983
+ error = _state.sent();
2984
+ console.error("[StormcloudVideoPlayer] Error sending ad-impression tracking:", error);
2985
+ return [
2986
+ 3,
2987
+ 4
2988
+ ];
2989
+ case 4:
2990
+ return [
2991
+ 2
2992
+ ];
2993
+ }
2994
+ });
2995
+ })();
2996
+ }
2772
2997
  // src/utils/polyfills.ts
2773
2998
  function polyfillURLSearchParams() {
2774
2999
  if (typeof URLSearchParams !== "undefined") {
@@ -3028,6 +3253,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3028
3253
  this.inAdBreak = false;
3029
3254
  this.ptsDriftEmaMs = 0;
3030
3255
  this.adPodQueue = [];
3256
+ this.vmapBreaks = [];
3257
+ this.consumedVmapBreakIds = /* @__PURE__ */ new Set();
3031
3258
  this.lastHeartbeatTime = 0;
3032
3259
  this.currentAdIndex = 0;
3033
3260
  this.totalAdsInBreak = 0;
@@ -3069,6 +3296,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3069
3296
  this.maxPreloadPoolSize = 2;
3070
3297
  this.preloadPoolActive = false;
3071
3298
  this.preloadPoolLoopRunning = false;
3299
+ this.adDetectSentForCurrentBreak = false;
3072
3300
  this.continuousFetchLoopRunning = false;
3073
3301
  initializePolyfills();
3074
3302
  var browserOverrides = getBrowserConfigOverrides();
@@ -3538,6 +3766,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3538
3766
  value: function attachImaEventListeners() {
3539
3767
  var _this = this;
3540
3768
  this.ima.on("all_ads_completed", function() {
3769
+ sendAdImpressionTracking(_this.config.licenseKey, {
3770
+ adIndex: _this.currentAdIndex
3771
+ }, _this.config.adPlayerType).catch(function() {});
3541
3772
  var remaining = _this.getRemainingAdMs();
3542
3773
  _this.consecutiveFailures = 0;
3543
3774
  if (_this.config.debugAdTiming) {
@@ -3600,6 +3831,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3600
3831
  }
3601
3832
  });
3602
3833
  this.ima.on("content_resume", function() {
3834
+ sendAdImpressionTracking(_this.config.licenseKey, {
3835
+ adIndex: _this.currentAdIndex
3836
+ }, _this.config.adPlayerType).catch(function() {});
3603
3837
  if (!_this.video.muted) {
3604
3838
  _this.video.muted = true;
3605
3839
  _this.video.volume = 0;
@@ -3983,6 +4217,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3983
4217
  }
3984
4218
  return;
3985
4219
  }
4220
+ if (!this.adDetectSentForCurrentBreak) {
4221
+ this.adDetectSentForCurrentBreak = true;
4222
+ var detectPayload = {};
4223
+ if (marker.durationSeconds != null) detectPayload.durationSeconds = marker.durationSeconds;
4224
+ if (marker.ptsSeconds != null) detectPayload.ptsSeconds = marker.ptsSeconds;
4225
+ sendAdDetectTracking(this.config.licenseKey, detectPayload).catch(function() {});
4226
+ }
3986
4227
  var hasPrefetchedAds = this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0;
3987
4228
  this.inAdBreak = true;
3988
4229
  var durationMs = marker.durationSeconds != null ? marker.durationSeconds * 1e3 : ((_this_pendingAdBreak = this.pendingAdBreak) === null || _this_pendingAdBreak === void 0 ? void 0 : _this_pendingAdBreak.marker.durationSeconds) != null ? this.pendingAdBreak.marker.durationSeconds * 1e3 : void 0;
@@ -4342,6 +4583,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4342
4583
  return _ts_generator(this, function(_state) {
4343
4584
  switch(_state.label){
4344
4585
  case 0:
4586
+ if (!this.config.vmapUrl) return [
4587
+ 3,
4588
+ 2
4589
+ ];
4590
+ return [
4591
+ 4,
4592
+ this.fetchAndParseVmap(this.config.vmapUrl)
4593
+ ];
4594
+ case 1:
4595
+ _state.sent();
4596
+ _state.label = 2;
4597
+ case 2:
4345
4598
  vastMode = this.config.vastMode || "default";
4346
4599
  if (this.config.debugAdTiming) {
4347
4600
  console.log("[StormcloudVideoPlayer] VAST mode:", vastMode);
@@ -4387,7 +4640,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4387
4640
  headers: headers
4388
4641
  })
4389
4642
  ];
4390
- case 1:
4643
+ case 3:
4391
4644
  response = _state.sent();
4392
4645
  if (!response.ok) {
4393
4646
  if (this.config.debugAdTiming) {
@@ -4401,7 +4654,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4401
4654
  4,
4402
4655
  response.json()
4403
4656
  ];
4404
- case 2:
4657
+ case 4:
4405
4658
  data = _state.sent();
4406
4659
  imaPayload = (_data_response = data.response) === null || _data_response === void 0 ? void 0 : (_data_response_ima = _data_response.ima) === null || _data_response_ima === void 0 ? void 0 : (_data_response_ima_publisherdeskima = _data_response_ima["publisherdesk.ima"]) === null || _data_response_ima_publisherdeskima === void 0 ? void 0 : _data_response_ima_publisherdeskima.payload;
4407
4660
  if (imaPayload) {
@@ -4429,6 +4682,171 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4429
4682
  }).call(this);
4430
4683
  }
4431
4684
  },
4685
+ {
4686
+ key: "fetchAndParseVmap",
4687
+ value: function fetchAndParseVmap(vmapUrl) {
4688
+ return _async_to_generator(function() {
4689
+ var response, vmapXml, error;
4690
+ return _ts_generator(this, function(_state) {
4691
+ switch(_state.label){
4692
+ case 0:
4693
+ if (!vmapUrl.trim()) {
4694
+ return [
4695
+ 2
4696
+ ];
4697
+ }
4698
+ _state.label = 1;
4699
+ case 1:
4700
+ _state.trys.push([
4701
+ 1,
4702
+ 4,
4703
+ ,
4704
+ 5
4705
+ ]);
4706
+ return [
4707
+ 4,
4708
+ fetch(vmapUrl)
4709
+ ];
4710
+ case 2:
4711
+ response = _state.sent();
4712
+ if (!response.ok) {
4713
+ throw new Error("Failed to fetch VMAP (".concat(response.status, ")"));
4714
+ }
4715
+ return [
4716
+ 4,
4717
+ response.text()
4718
+ ];
4719
+ case 3:
4720
+ vmapXml = _state.sent();
4721
+ this.vmapBreaks = this.parseVmapToBreaks(vmapXml);
4722
+ this.consumedVmapBreakIds.clear();
4723
+ if (this.config.debugAdTiming) {
4724
+ console.log("[StormcloudVideoPlayer] Loaded ".concat(this.vmapBreaks.length, " VMAP ad break(s) from:"), vmapUrl);
4725
+ }
4726
+ return [
4727
+ 3,
4728
+ 5
4729
+ ];
4730
+ case 4:
4731
+ error = _state.sent();
4732
+ this.vmapBreaks = [];
4733
+ this.consumedVmapBreakIds.clear();
4734
+ if (this.config.debugAdTiming) {
4735
+ console.warn("[StormcloudVideoPlayer] Failed to load VMAP:", error);
4736
+ }
4737
+ return [
4738
+ 3,
4739
+ 5
4740
+ ];
4741
+ case 5:
4742
+ return [
4743
+ 2
4744
+ ];
4745
+ }
4746
+ });
4747
+ }).call(this);
4748
+ }
4749
+ },
4750
+ {
4751
+ key: "parseVmapToBreaks",
4752
+ value: function parseVmapToBreaks(vmapXml) {
4753
+ var _this = this;
4754
+ if (typeof DOMParser === "undefined") {
4755
+ return [];
4756
+ }
4757
+ var doc = new DOMParser().parseFromString(vmapXml, "application/xml");
4758
+ if (doc.querySelector("parsererror")) {
4759
+ if (this.config.debugAdTiming) {
4760
+ console.warn("[StormcloudVideoPlayer] Invalid VMAP XML received");
4761
+ }
4762
+ return [];
4763
+ }
4764
+ var adBreakNodes = Array.from(doc.querySelectorAll("AdBreak, vmap\\:AdBreak"));
4765
+ var parsed = [];
4766
+ adBreakNodes.forEach(function(node, index) {
4767
+ var timeOffsetRaw = (node.getAttribute("timeOffset") || "").trim();
4768
+ var startTimeMs = _this.parseVmapTimeOffsetToMs(timeOffsetRaw);
4769
+ if (startTimeMs == null) {
4770
+ return;
4771
+ }
4772
+ var adTagNode = node.querySelector("AdTagURI, vmap\\:AdTagURI");
4773
+ var adTagUrl = ((adTagNode === null || adTagNode === void 0 ? void 0 : adTagNode.textContent) || "").trim();
4774
+ if (!adTagUrl) {
4775
+ return;
4776
+ }
4777
+ var breakId = node.getAttribute("breakId") || "vmap-break-".concat(index, "-").concat(timeOffsetRaw || "unknown");
4778
+ parsed.push({
4779
+ id: breakId,
4780
+ startTimeMs: startTimeMs,
4781
+ vastTagUrl: adTagUrl
4782
+ });
4783
+ });
4784
+ parsed.sort(function(a, b) {
4785
+ var aStart = a.startTimeMs < 0 ? Number.MAX_SAFE_INTEGER : a.startTimeMs;
4786
+ var bStart = b.startTimeMs < 0 ? Number.MAX_SAFE_INTEGER : b.startTimeMs;
4787
+ return aStart - bStart;
4788
+ });
4789
+ return parsed;
4790
+ }
4791
+ },
4792
+ {
4793
+ key: "parseVmapTimeOffsetToMs",
4794
+ value: function parseVmapTimeOffsetToMs(timeOffset) {
4795
+ if (!timeOffset) {
4796
+ return void 0;
4797
+ }
4798
+ var normalized = timeOffset.trim().toLowerCase();
4799
+ if (normalized === "start") {
4800
+ return 0;
4801
+ }
4802
+ if (normalized === "end") {
4803
+ return -1;
4804
+ }
4805
+ var hms = timeOffset.match(/^(\d{1,2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?$/);
4806
+ if (hms) {
4807
+ var _hms = _sliced_to_array(hms, 5), hh = _hms[1], mm = _hms[2], ss = _hms[3], tmp = _hms[4], ms = tmp === void 0 ? "0" : tmp;
4808
+ var hours = Number(hh);
4809
+ var minutes = Number(mm);
4810
+ var seconds = Number(ss);
4811
+ var millis = Number(ms.padEnd(3, "0").slice(0, 3));
4812
+ return (hours * 3600 + minutes * 60 + seconds) * 1e3 + millis;
4813
+ }
4814
+ var percent = timeOffset.match(/^(\d+(?:\.\d+)?)%$/);
4815
+ if (percent) {
4816
+ var ratio = Number(percent[1]) / 100;
4817
+ var durationSec = this.video.duration;
4818
+ if (Number.isFinite(durationSec) && durationSec > 0) {
4819
+ return Math.floor(durationSec * 1e3 * ratio);
4820
+ }
4821
+ return void 0;
4822
+ }
4823
+ return void 0;
4824
+ }
4825
+ },
4826
+ {
4827
+ key: "getAdBreakKey",
4828
+ value: function getAdBreakKey(adBreak) {
4829
+ if (adBreak.id) {
4830
+ return adBreak.id;
4831
+ }
4832
+ return "".concat(adBreak.startTimeMs, "-").concat(adBreak.vastTagUrl || "");
4833
+ }
4834
+ },
4835
+ {
4836
+ key: "resolveBreakStartMs",
4837
+ value: function resolveBreakStartMs(adBreak) {
4838
+ if (adBreak.startTimeMs >= 0) {
4839
+ return adBreak.startTimeMs;
4840
+ }
4841
+ if (adBreak.startTimeMs === -1) {
4842
+ var durationSec = this.video.duration;
4843
+ if (Number.isFinite(durationSec) && durationSec > 0) {
4844
+ return Math.floor(durationSec * 1e3);
4845
+ }
4846
+ }
4847
+ return void 0;
4848
+ }
4849
+ },
4432
4850
  {
4433
4851
  key: "getCurrentAdIndex",
4434
4852
  value: function getCurrentAdIndex() {
@@ -4519,10 +4937,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4519
4937
  var scheduled = this.findCurrentOrNextBreak(this.video.currentTime * 1e3);
4520
4938
  var tags = this.selectVastTagsForBreak(scheduled);
4521
4939
  var baseVastUrl;
4522
- if (this.apiVastTagUrl) {
4523
- baseVastUrl = this.apiVastTagUrl;
4524
- } else if (tags && tags.length > 0 && tags[0]) {
4940
+ if (tags && tags.length > 0 && tags[0]) {
4525
4941
  baseVastUrl = tags[0];
4942
+ } else if (this.apiVastTagUrl) {
4943
+ baseVastUrl = this.apiVastTagUrl;
4526
4944
  } else {
4527
4945
  if (this.config.debugAdTiming) {
4528
4946
  console.warn("[StormcloudVideoPlayer] No VAST URL available for prefetch");
@@ -4540,6 +4958,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4540
4958
  isFetching: false,
4541
4959
  fetchStartTime: Date.now()
4542
4960
  });
4961
+ this.adDetectSentForCurrentBreak = true;
4962
+ var detectPayload = {};
4963
+ if (marker.durationSeconds != null) detectPayload.durationSeconds = marker.durationSeconds;
4964
+ if (marker.ptsSeconds != null) detectPayload.ptsSeconds = marker.ptsSeconds;
4965
+ if (fragmentSn !== void 0) detectPayload.detectedAtFragmentSn = fragmentSn;
4966
+ sendAdDetectTracking(this.config.licenseKey, detectPayload).catch(function() {});
4543
4967
  if (this.config.debugAdTiming) {
4544
4968
  console.log("[PREFETCH] \uD83D\uDD04 Starting ad prefetch for upcoming ad break");
4545
4969
  console.log("[PREFETCH] \uD83D\uDCCB Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
@@ -4966,11 +5390,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4966
5390
  switch(_state.label){
4967
5391
  case 0:
4968
5392
  scheduled = this.findCurrentOrNextBreak(this.video.currentTime * 1e3);
5393
+ if (scheduled) {
5394
+ this.consumedVmapBreakIds.add(this.getAdBreakKey(scheduled));
5395
+ }
4969
5396
  tags = this.selectVastTagsForBreak(scheduled);
4970
- if (this.apiVastTagUrl) {
4971
- baseVastUrl = this.apiVastTagUrl;
4972
- } else if (tags && tags.length > 0 && tags[0]) {
5397
+ if (tags && tags.length > 0 && tags[0]) {
4973
5398
  baseVastUrl = tags[0];
5399
+ } else if (this.apiVastTagUrl) {
5400
+ baseVastUrl = this.apiVastTagUrl;
4974
5401
  } else {
4975
5402
  this.clearPendingAdBreak();
4976
5403
  return [
@@ -5015,6 +5442,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5015
5442
  firstAdUrl = preloaded.vastUrl;
5016
5443
  preloadedController = preloaded.imaController;
5017
5444
  usePreloadedAd = true;
5445
+ sendAdLoadedTracking(this.config.licenseKey, {
5446
+ source: "preload",
5447
+ vastUrl: firstAdUrl
5448
+ }, this.config.adPlayerType).catch(function() {});
5018
5449
  if (this.config.debugAdTiming) {
5019
5450
  console.log("[CONTINUOUS-FETCH] \uD83D\uDE80 Using preloaded ad from pool (preloaded in advance, ready immediately!)");
5020
5451
  console.log("[CONTINUOUS-FETCH] Pool still has ".concat(this.preloadPool.length, " preloaded ads ready"));
@@ -5104,6 +5535,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5104
5535
  ];
5105
5536
  case 5:
5106
5537
  _state.sent();
5538
+ sendAdLoadedTracking(this.config.licenseKey, {
5539
+ source: "ssp",
5540
+ vastUrl: firstAdUrl
5541
+ }, this.config.adPlayerType).catch(function() {});
5107
5542
  if (this.config.debugAdTiming) {
5108
5543
  console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
5109
5544
  }
@@ -5500,6 +5935,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5500
5935
  if (this.config.debugAdTiming) {
5501
5936
  console.log("[CONTINUOUS-FETCH] \uD83C\uDFAF Using preloaded ad from pool (".concat(this.preloadPool.length, " remaining in pool)"));
5502
5937
  }
5938
+ sendAdLoadedTracking(this.config.licenseKey, {
5939
+ source: "preload",
5940
+ vastUrl: preloaded.vastUrl
5941
+ }, this.config.adPlayerType).catch(function() {});
5503
5942
  _state.label = 1;
5504
5943
  case 1:
5505
5944
  _state.trys.push([
@@ -5837,15 +6276,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5837
6276
  {
5838
6277
  key: "findCurrentOrNextBreak",
5839
6278
  value: function findCurrentOrNextBreak(nowMs) {
5840
- var schedule = [];
6279
+ var schedule = this.vmapBreaks;
5841
6280
  var candidate;
5842
6281
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
5843
6282
  try {
5844
6283
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
5845
6284
  var b = _step.value;
5846
- var _this_config_driftToleranceMs;
6285
+ var _this_config_driftToleranceMs, _this_resolveBreakStartMs;
6286
+ if (this.consumedVmapBreakIds.has(this.getAdBreakKey(b))) {
6287
+ continue;
6288
+ }
6289
+ var breakStartMs = this.resolveBreakStartMs(b);
6290
+ if (breakStartMs == null) {
6291
+ continue;
6292
+ }
5847
6293
  var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
5848
- if (b.startTimeMs <= nowMs + tol && (candidate == null || b.startTimeMs > (candidate.startTimeMs || 0))) {
6294
+ if (breakStartMs <= nowMs + tol && (candidate == null || breakStartMs > ((_this_resolveBreakStartMs = this.resolveBreakStartMs(candidate)) !== null && _this_resolveBreakStartMs !== void 0 ? _this_resolveBreakStartMs : 0))) {
5849
6295
  candidate = b;
5850
6296
  }
5851
6297
  }
@@ -5869,11 +6315,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5869
6315
  {
5870
6316
  key: "onTimeUpdate",
5871
6317
  value: function onTimeUpdate(currentTimeSec) {
5872
- if (this.ima.isAdPlaying()) return;
6318
+ var _this = this;
6319
+ if (this.ima.isAdPlaying() || this.inAdBreak) return;
5873
6320
  var nowMs = currentTimeSec * 1e3;
5874
6321
  var breakToPlay = this.findBreakForTime(nowMs);
5875
6322
  if (breakToPlay) {
5876
- this.handleMidAdJoin(breakToPlay, nowMs);
6323
+ void this.handleMidAdJoin(breakToPlay, nowMs).catch(function(error) {
6324
+ if (_this.config.debugAdTiming) {
6325
+ console.warn("[StormcloudVideoPlayer] Mid-roll VMAP join failed gracefully:", error);
6326
+ }
6327
+ });
5877
6328
  }
5878
6329
  }
5879
6330
  },
@@ -5881,40 +6332,76 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5881
6332
  key: "handleMidAdJoin",
5882
6333
  value: function handleMidAdJoin(adBreak, nowMs) {
5883
6334
  return _async_to_generator(function() {
5884
- var _adBreak_durationMs, durationMs, endMs, remainingMs, tags, first, rest;
6335
+ var _adBreak_durationMs, _this_config_driftToleranceMs, key, breakStartMs, durationMs, endMs, tol, inWindow, remainingMs, tags, first, rest, error;
5885
6336
  return _ts_generator(this, function(_state) {
5886
6337
  switch(_state.label){
5887
6338
  case 0:
6339
+ key = this.getAdBreakKey(adBreak);
6340
+ if (this.consumedVmapBreakIds.has(key)) {
6341
+ return [
6342
+ 2
6343
+ ];
6344
+ }
6345
+ breakStartMs = this.resolveBreakStartMs(adBreak);
6346
+ if (breakStartMs == null) {
6347
+ return [
6348
+ 2
6349
+ ];
6350
+ }
5888
6351
  durationMs = (_adBreak_durationMs = adBreak.durationMs) !== null && _adBreak_durationMs !== void 0 ? _adBreak_durationMs : 0;
5889
- endMs = adBreak.startTimeMs + durationMs;
5890
- if (!(durationMs > 0 && nowMs > adBreak.startTimeMs && nowMs < endMs)) return [
6352
+ endMs = breakStartMs + durationMs;
6353
+ tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
6354
+ inWindow = durationMs > 0 ? nowMs > breakStartMs && nowMs < endMs : nowMs + tol >= breakStartMs;
6355
+ if (!inWindow) return [
5891
6356
  3,
5892
- 2
6357
+ 4
5893
6358
  ];
5894
- remainingMs = endMs - nowMs;
6359
+ this.consumedVmapBreakIds.add(key);
6360
+ remainingMs = durationMs > 0 ? Math.max(0, endMs - nowMs) : 0;
5895
6361
  tags = this.selectVastTagsForBreak(adBreak) || (this.apiVastTagUrl ? [
5896
6362
  this.apiVastTagUrl
5897
6363
  ] : void 0);
5898
6364
  if (!(tags && tags.length > 0)) return [
5899
6365
  3,
5900
- 2
6366
+ 4
5901
6367
  ];
5902
6368
  first = tags[0];
5903
6369
  rest = tags.slice(1);
5904
6370
  this.adPodQueue = rest;
5905
6371
  this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
6372
+ _state.label = 1;
6373
+ case 1:
6374
+ _state.trys.push([
6375
+ 1,
6376
+ 3,
6377
+ ,
6378
+ 4
6379
+ ]);
5906
6380
  return [
5907
6381
  4,
5908
6382
  this.playSingleAd(first)
5909
6383
  ];
5910
- case 1:
6384
+ case 2:
5911
6385
  _state.sent();
5912
6386
  this.inAdBreak = true;
5913
6387
  this.expectedAdBreakDurationMs = remainingMs;
5914
6388
  this.currentAdBreakStartWallClockMs = Date.now();
5915
6389
  this.scheduleAdStopCountdown(remainingMs);
5916
- _state.label = 2;
5917
- case 2:
6390
+ return [
6391
+ 3,
6392
+ 4
6393
+ ];
6394
+ case 3:
6395
+ error = _state.sent();
6396
+ this.adPodQueue = [];
6397
+ if (this.config.debugAdTiming) {
6398
+ console.warn("[StormcloudVideoPlayer] Mid-roll VMAP ad request failed:", error);
6399
+ }
6400
+ return [
6401
+ 3,
6402
+ 4
6403
+ ];
6404
+ case 4:
5918
6405
  return [
5919
6406
  2
5920
6407
  ];
@@ -6204,6 +6691,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6204
6691
  ];
6205
6692
  case 3:
6206
6693
  _state.sent();
6694
+ sendAdLoadedTracking(this.config.licenseKey, {
6695
+ source: "ssp",
6696
+ vastUrl: vastTagUrl
6697
+ }, this.config.adPlayerType).catch(function() {});
6207
6698
  this.clearAdRequestWatchdog();
6208
6699
  if (this.activeAdRequestToken !== requestToken) {
6209
6700
  return [
@@ -6418,6 +6909,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6418
6909
  }
6419
6910
  this.adRequestQueue = [];
6420
6911
  this.inAdBreak = false;
6912
+ this.adDetectSentForCurrentBreak = false;
6421
6913
  this.expectedAdBreakDurationMs = void 0;
6422
6914
  this.currentAdBreakStartWallClockMs = void 0;
6423
6915
  this.clearAdStartTimer();
@@ -6615,13 +7107,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6615
7107
  {
6616
7108
  key: "findBreakForTime",
6617
7109
  value: function findBreakForTime(nowMs) {
6618
- var schedule = [];
7110
+ var _this_config_driftToleranceMs;
7111
+ var schedule = this.vmapBreaks;
7112
+ var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
6619
7113
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
6620
7114
  try {
6621
7115
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
6622
7116
  var b = _step.value;
6623
- var end = (b.startTimeMs || 0) + (b.durationMs || 0);
6624
- if (nowMs >= (b.startTimeMs || 0) && (b.durationMs ? nowMs < end : true)) {
7117
+ if (this.consumedVmapBreakIds.has(this.getAdBreakKey(b))) {
7118
+ continue;
7119
+ }
7120
+ var breakStartMs = this.resolveBreakStartMs(b);
7121
+ if (breakStartMs == null) {
7122
+ continue;
7123
+ }
7124
+ var end = breakStartMs + (b.durationMs || 0);
7125
+ if (nowMs >= breakStartMs && (b.durationMs ? nowMs < end : nowMs <= breakStartMs + tol)) {
6625
7126
  return b;
6626
7127
  }
6627
7128
  }