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.
@@ -2697,9 +2697,6 @@ function sendInitialTracking(licenseKey) {
2697
2697
  headers = {
2698
2698
  "Content-Type": "application/json"
2699
2699
  };
2700
- if (licenseKey) {
2701
- headers["Authorization"] = "Bearer ".concat(licenseKey);
2702
- }
2703
2700
  return [
2704
2701
  4,
2705
2702
  fetch("https://adstorm.co/api-adstorm-dev/adstorm/player-tracking/track", {
@@ -2805,6 +2802,234 @@ function sendHeartbeat(licenseKey) {
2805
2802
  });
2806
2803
  })();
2807
2804
  }
2805
+ var TRACK_API_URL = "https://adstorm.co/api-adstorm-dev/adstorm/player-tracking/track";
2806
+ function mapToAdTrackingSource(source, adPlayerType) {
2807
+ if (source === "prebid" || source === "ima" || source === "hls") {
2808
+ return source;
2809
+ }
2810
+ if (source === "preload" || source === "ssp") {
2811
+ return source === "ssp" ? "prebid" : "ima";
2812
+ }
2813
+ return adPlayerType === "hls" ? "hls" : "ima";
2814
+ }
2815
+ function postAdTracking(licenseKey, body) {
2816
+ return _async_to_generator(function() {
2817
+ var headers, response;
2818
+ return _ts_generator(this, function(_state) {
2819
+ switch(_state.label){
2820
+ case 0:
2821
+ headers = {
2822
+ "Content-Type": "application/json"
2823
+ };
2824
+ if (licenseKey) {
2825
+ headers["Authorization"] = "Bearer ".concat(licenseKey);
2826
+ }
2827
+ return [
2828
+ 4,
2829
+ fetch(TRACK_API_URL, {
2830
+ method: "POST",
2831
+ headers: headers,
2832
+ body: JSON.stringify(body)
2833
+ })
2834
+ ];
2835
+ case 1:
2836
+ response = _state.sent();
2837
+ if (!response.ok) {
2838
+ throw new Error("HTTP error! status: ".concat(response.status));
2839
+ }
2840
+ return [
2841
+ 4,
2842
+ response.json()
2843
+ ];
2844
+ case 2:
2845
+ _state.sent();
2846
+ return [
2847
+ 2
2848
+ ];
2849
+ }
2850
+ });
2851
+ })();
2852
+ }
2853
+ function sendAdDetectTracking(licenseKey, payload) {
2854
+ return _async_to_generator(function() {
2855
+ var _payload_source, _payload_timestamp, clientInfo, browserId, adDetectInfo, body, error;
2856
+ return _ts_generator(this, function(_state) {
2857
+ switch(_state.label){
2858
+ case 0:
2859
+ _state.trys.push([
2860
+ 0,
2861
+ 3,
2862
+ ,
2863
+ 4
2864
+ ]);
2865
+ clientInfo = getClientInfo();
2866
+ return [
2867
+ 4,
2868
+ getBrowserID(clientInfo)
2869
+ ];
2870
+ case 1:
2871
+ browserId = _state.sent();
2872
+ adDetectInfo = _object_spread({
2873
+ source: (_payload_source = payload.source) !== null && _payload_source !== void 0 ? _payload_source : "scte35",
2874
+ timestamp: (_payload_timestamp = payload.timestamp) !== null && _payload_timestamp !== void 0 ? _payload_timestamp : /* @__PURE__ */ new Date().toISOString()
2875
+ }, payload.durationSeconds != null && {
2876
+ durationSeconds: payload.durationSeconds
2877
+ }, payload.ptsSeconds != null && {
2878
+ ptsSeconds: payload.ptsSeconds
2879
+ }, payload.detectedAtFragmentSn != null && {
2880
+ detectedAtFragmentSn: payload.detectedAtFragmentSn
2881
+ });
2882
+ body = _object_spread_props(_object_spread({
2883
+ browserId: browserId
2884
+ }, clientInfo, licenseKey && {
2885
+ licenseKey: licenseKey
2886
+ }), {
2887
+ adDetectInfo: adDetectInfo
2888
+ });
2889
+ return [
2890
+ 4,
2891
+ postAdTracking(licenseKey, body)
2892
+ ];
2893
+ case 2:
2894
+ _state.sent();
2895
+ return [
2896
+ 3,
2897
+ 4
2898
+ ];
2899
+ case 3:
2900
+ error = _state.sent();
2901
+ console.error("[StormcloudVideoPlayer] Error sending ad-detect tracking:", error);
2902
+ return [
2903
+ 3,
2904
+ 4
2905
+ ];
2906
+ case 4:
2907
+ return [
2908
+ 2
2909
+ ];
2910
+ }
2911
+ });
2912
+ })();
2913
+ }
2914
+ function sendAdLoadedTracking(licenseKey, payload, adPlayerType) {
2915
+ return _async_to_generator(function() {
2916
+ var clientInfo, browserId, source, adLoadedInfo, body, error;
2917
+ return _ts_generator(this, function(_state) {
2918
+ switch(_state.label){
2919
+ case 0:
2920
+ _state.trys.push([
2921
+ 0,
2922
+ 3,
2923
+ ,
2924
+ 4
2925
+ ]);
2926
+ clientInfo = getClientInfo();
2927
+ return [
2928
+ 4,
2929
+ getBrowserID(clientInfo)
2930
+ ];
2931
+ case 1:
2932
+ browserId = _state.sent();
2933
+ source = mapToAdTrackingSource(payload.source, adPlayerType);
2934
+ adLoadedInfo = _object_spread({
2935
+ source: source,
2936
+ timestamp: /* @__PURE__ */ new Date().toISOString()
2937
+ }, payload.vastUrl != null && {
2938
+ vastUrl: payload.vastUrl
2939
+ }, payload.durationSeconds != null && {
2940
+ durationSeconds: payload.durationSeconds
2941
+ });
2942
+ body = _object_spread_props(_object_spread({
2943
+ browserId: browserId
2944
+ }, clientInfo, licenseKey && {
2945
+ licenseKey: licenseKey
2946
+ }), {
2947
+ adLoadedInfo: adLoadedInfo
2948
+ });
2949
+ return [
2950
+ 4,
2951
+ postAdTracking(licenseKey, body)
2952
+ ];
2953
+ case 2:
2954
+ _state.sent();
2955
+ return [
2956
+ 3,
2957
+ 4
2958
+ ];
2959
+ case 3:
2960
+ error = _state.sent();
2961
+ console.error("[StormcloudVideoPlayer] Error sending ad-loaded tracking:", error);
2962
+ return [
2963
+ 3,
2964
+ 4
2965
+ ];
2966
+ case 4:
2967
+ return [
2968
+ 2
2969
+ ];
2970
+ }
2971
+ });
2972
+ })();
2973
+ }
2974
+ function sendAdImpressionTracking(licenseKey, payload, adPlayerType) {
2975
+ return _async_to_generator(function() {
2976
+ var clientInfo, browserId, source, adImpressionInfo, body, error;
2977
+ return _ts_generator(this, function(_state) {
2978
+ switch(_state.label){
2979
+ case 0:
2980
+ _state.trys.push([
2981
+ 0,
2982
+ 3,
2983
+ ,
2984
+ 4
2985
+ ]);
2986
+ clientInfo = getClientInfo();
2987
+ return [
2988
+ 4,
2989
+ getBrowserID(clientInfo)
2990
+ ];
2991
+ case 1:
2992
+ browserId = _state.sent();
2993
+ source = mapToAdTrackingSource(payload.source, adPlayerType);
2994
+ adImpressionInfo = _object_spread({
2995
+ source: source,
2996
+ adIndex: payload.adIndex,
2997
+ timestamp: /* @__PURE__ */ new Date().toISOString()
2998
+ }, payload.durationSeconds != null && {
2999
+ durationSeconds: payload.durationSeconds
3000
+ });
3001
+ body = _object_spread_props(_object_spread({
3002
+ browserId: browserId
3003
+ }, clientInfo, licenseKey && {
3004
+ licenseKey: licenseKey
3005
+ }), {
3006
+ adImpressionInfo: adImpressionInfo
3007
+ });
3008
+ return [
3009
+ 4,
3010
+ postAdTracking(licenseKey, body)
3011
+ ];
3012
+ case 2:
3013
+ _state.sent();
3014
+ return [
3015
+ 3,
3016
+ 4
3017
+ ];
3018
+ case 3:
3019
+ error = _state.sent();
3020
+ console.error("[StormcloudVideoPlayer] Error sending ad-impression tracking:", error);
3021
+ return [
3022
+ 3,
3023
+ 4
3024
+ ];
3025
+ case 4:
3026
+ return [
3027
+ 2
3028
+ ];
3029
+ }
3030
+ });
3031
+ })();
3032
+ }
2808
3033
  // src/utils/polyfills.ts
2809
3034
  function polyfillURLSearchParams() {
2810
3035
  if (typeof URLSearchParams !== "undefined") {
@@ -3064,6 +3289,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3064
3289
  this.inAdBreak = false;
3065
3290
  this.ptsDriftEmaMs = 0;
3066
3291
  this.adPodQueue = [];
3292
+ this.vmapBreaks = [];
3293
+ this.consumedVmapBreakIds = /* @__PURE__ */ new Set();
3067
3294
  this.lastHeartbeatTime = 0;
3068
3295
  this.currentAdIndex = 0;
3069
3296
  this.totalAdsInBreak = 0;
@@ -3105,6 +3332,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3105
3332
  this.maxPreloadPoolSize = 2;
3106
3333
  this.preloadPoolActive = false;
3107
3334
  this.preloadPoolLoopRunning = false;
3335
+ this.adDetectSentForCurrentBreak = false;
3108
3336
  this.continuousFetchLoopRunning = false;
3109
3337
  initializePolyfills();
3110
3338
  var browserOverrides = getBrowserConfigOverrides();
@@ -3574,6 +3802,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3574
3802
  value: function attachImaEventListeners() {
3575
3803
  var _this = this;
3576
3804
  this.ima.on("all_ads_completed", function() {
3805
+ sendAdImpressionTracking(_this.config.licenseKey, {
3806
+ adIndex: _this.currentAdIndex
3807
+ }, _this.config.adPlayerType).catch(function() {});
3577
3808
  var remaining = _this.getRemainingAdMs();
3578
3809
  _this.consecutiveFailures = 0;
3579
3810
  if (_this.config.debugAdTiming) {
@@ -3636,6 +3867,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3636
3867
  }
3637
3868
  });
3638
3869
  this.ima.on("content_resume", function() {
3870
+ sendAdImpressionTracking(_this.config.licenseKey, {
3871
+ adIndex: _this.currentAdIndex
3872
+ }, _this.config.adPlayerType).catch(function() {});
3639
3873
  if (!_this.video.muted) {
3640
3874
  _this.video.muted = true;
3641
3875
  _this.video.volume = 0;
@@ -4019,6 +4253,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4019
4253
  }
4020
4254
  return;
4021
4255
  }
4256
+ if (!this.adDetectSentForCurrentBreak) {
4257
+ this.adDetectSentForCurrentBreak = true;
4258
+ var detectPayload = {};
4259
+ if (marker.durationSeconds != null) detectPayload.durationSeconds = marker.durationSeconds;
4260
+ if (marker.ptsSeconds != null) detectPayload.ptsSeconds = marker.ptsSeconds;
4261
+ sendAdDetectTracking(this.config.licenseKey, detectPayload).catch(function() {});
4262
+ }
4022
4263
  var hasPrefetchedAds = this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0;
4023
4264
  this.inAdBreak = true;
4024
4265
  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;
@@ -4378,6 +4619,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4378
4619
  return _ts_generator(this, function(_state) {
4379
4620
  switch(_state.label){
4380
4621
  case 0:
4622
+ if (!this.config.vmapUrl) return [
4623
+ 3,
4624
+ 2
4625
+ ];
4626
+ return [
4627
+ 4,
4628
+ this.fetchAndParseVmap(this.config.vmapUrl)
4629
+ ];
4630
+ case 1:
4631
+ _state.sent();
4632
+ _state.label = 2;
4633
+ case 2:
4381
4634
  vastMode = this.config.vastMode || "default";
4382
4635
  if (this.config.debugAdTiming) {
4383
4636
  console.log("[StormcloudVideoPlayer] VAST mode:", vastMode);
@@ -4423,7 +4676,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4423
4676
  headers: headers
4424
4677
  })
4425
4678
  ];
4426
- case 1:
4679
+ case 3:
4427
4680
  response = _state.sent();
4428
4681
  if (!response.ok) {
4429
4682
  if (this.config.debugAdTiming) {
@@ -4437,7 +4690,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4437
4690
  4,
4438
4691
  response.json()
4439
4692
  ];
4440
- case 2:
4693
+ case 4:
4441
4694
  data = _state.sent();
4442
4695
  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;
4443
4696
  if (imaPayload) {
@@ -4465,6 +4718,171 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4465
4718
  }).call(this);
4466
4719
  }
4467
4720
  },
4721
+ {
4722
+ key: "fetchAndParseVmap",
4723
+ value: function fetchAndParseVmap(vmapUrl) {
4724
+ return _async_to_generator(function() {
4725
+ var response, vmapXml, error;
4726
+ return _ts_generator(this, function(_state) {
4727
+ switch(_state.label){
4728
+ case 0:
4729
+ if (!vmapUrl.trim()) {
4730
+ return [
4731
+ 2
4732
+ ];
4733
+ }
4734
+ _state.label = 1;
4735
+ case 1:
4736
+ _state.trys.push([
4737
+ 1,
4738
+ 4,
4739
+ ,
4740
+ 5
4741
+ ]);
4742
+ return [
4743
+ 4,
4744
+ fetch(vmapUrl)
4745
+ ];
4746
+ case 2:
4747
+ response = _state.sent();
4748
+ if (!response.ok) {
4749
+ throw new Error("Failed to fetch VMAP (".concat(response.status, ")"));
4750
+ }
4751
+ return [
4752
+ 4,
4753
+ response.text()
4754
+ ];
4755
+ case 3:
4756
+ vmapXml = _state.sent();
4757
+ this.vmapBreaks = this.parseVmapToBreaks(vmapXml);
4758
+ this.consumedVmapBreakIds.clear();
4759
+ if (this.config.debugAdTiming) {
4760
+ console.log("[StormcloudVideoPlayer] Loaded ".concat(this.vmapBreaks.length, " VMAP ad break(s) from:"), vmapUrl);
4761
+ }
4762
+ return [
4763
+ 3,
4764
+ 5
4765
+ ];
4766
+ case 4:
4767
+ error = _state.sent();
4768
+ this.vmapBreaks = [];
4769
+ this.consumedVmapBreakIds.clear();
4770
+ if (this.config.debugAdTiming) {
4771
+ console.warn("[StormcloudVideoPlayer] Failed to load VMAP:", error);
4772
+ }
4773
+ return [
4774
+ 3,
4775
+ 5
4776
+ ];
4777
+ case 5:
4778
+ return [
4779
+ 2
4780
+ ];
4781
+ }
4782
+ });
4783
+ }).call(this);
4784
+ }
4785
+ },
4786
+ {
4787
+ key: "parseVmapToBreaks",
4788
+ value: function parseVmapToBreaks(vmapXml) {
4789
+ var _this = this;
4790
+ if (typeof DOMParser === "undefined") {
4791
+ return [];
4792
+ }
4793
+ var doc = new DOMParser().parseFromString(vmapXml, "application/xml");
4794
+ if (doc.querySelector("parsererror")) {
4795
+ if (this.config.debugAdTiming) {
4796
+ console.warn("[StormcloudVideoPlayer] Invalid VMAP XML received");
4797
+ }
4798
+ return [];
4799
+ }
4800
+ var adBreakNodes = Array.from(doc.querySelectorAll("AdBreak, vmap\\:AdBreak"));
4801
+ var parsed = [];
4802
+ adBreakNodes.forEach(function(node, index) {
4803
+ var timeOffsetRaw = (node.getAttribute("timeOffset") || "").trim();
4804
+ var startTimeMs = _this.parseVmapTimeOffsetToMs(timeOffsetRaw);
4805
+ if (startTimeMs == null) {
4806
+ return;
4807
+ }
4808
+ var adTagNode = node.querySelector("AdTagURI, vmap\\:AdTagURI");
4809
+ var adTagUrl = ((adTagNode === null || adTagNode === void 0 ? void 0 : adTagNode.textContent) || "").trim();
4810
+ if (!adTagUrl) {
4811
+ return;
4812
+ }
4813
+ var breakId = node.getAttribute("breakId") || "vmap-break-".concat(index, "-").concat(timeOffsetRaw || "unknown");
4814
+ parsed.push({
4815
+ id: breakId,
4816
+ startTimeMs: startTimeMs,
4817
+ vastTagUrl: adTagUrl
4818
+ });
4819
+ });
4820
+ parsed.sort(function(a, b) {
4821
+ var aStart = a.startTimeMs < 0 ? Number.MAX_SAFE_INTEGER : a.startTimeMs;
4822
+ var bStart = b.startTimeMs < 0 ? Number.MAX_SAFE_INTEGER : b.startTimeMs;
4823
+ return aStart - bStart;
4824
+ });
4825
+ return parsed;
4826
+ }
4827
+ },
4828
+ {
4829
+ key: "parseVmapTimeOffsetToMs",
4830
+ value: function parseVmapTimeOffsetToMs(timeOffset) {
4831
+ if (!timeOffset) {
4832
+ return void 0;
4833
+ }
4834
+ var normalized = timeOffset.trim().toLowerCase();
4835
+ if (normalized === "start") {
4836
+ return 0;
4837
+ }
4838
+ if (normalized === "end") {
4839
+ return -1;
4840
+ }
4841
+ var hms = timeOffset.match(/^(\d{1,2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?$/);
4842
+ if (hms) {
4843
+ 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;
4844
+ var hours = Number(hh);
4845
+ var minutes = Number(mm);
4846
+ var seconds = Number(ss);
4847
+ var millis = Number(ms.padEnd(3, "0").slice(0, 3));
4848
+ return (hours * 3600 + minutes * 60 + seconds) * 1e3 + millis;
4849
+ }
4850
+ var percent = timeOffset.match(/^(\d+(?:\.\d+)?)%$/);
4851
+ if (percent) {
4852
+ var ratio = Number(percent[1]) / 100;
4853
+ var durationSec = this.video.duration;
4854
+ if (Number.isFinite(durationSec) && durationSec > 0) {
4855
+ return Math.floor(durationSec * 1e3 * ratio);
4856
+ }
4857
+ return void 0;
4858
+ }
4859
+ return void 0;
4860
+ }
4861
+ },
4862
+ {
4863
+ key: "getAdBreakKey",
4864
+ value: function getAdBreakKey(adBreak) {
4865
+ if (adBreak.id) {
4866
+ return adBreak.id;
4867
+ }
4868
+ return "".concat(adBreak.startTimeMs, "-").concat(adBreak.vastTagUrl || "");
4869
+ }
4870
+ },
4871
+ {
4872
+ key: "resolveBreakStartMs",
4873
+ value: function resolveBreakStartMs(adBreak) {
4874
+ if (adBreak.startTimeMs >= 0) {
4875
+ return adBreak.startTimeMs;
4876
+ }
4877
+ if (adBreak.startTimeMs === -1) {
4878
+ var durationSec = this.video.duration;
4879
+ if (Number.isFinite(durationSec) && durationSec > 0) {
4880
+ return Math.floor(durationSec * 1e3);
4881
+ }
4882
+ }
4883
+ return void 0;
4884
+ }
4885
+ },
4468
4886
  {
4469
4887
  key: "getCurrentAdIndex",
4470
4888
  value: function getCurrentAdIndex() {
@@ -4555,10 +4973,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4555
4973
  var scheduled = this.findCurrentOrNextBreak(this.video.currentTime * 1e3);
4556
4974
  var tags = this.selectVastTagsForBreak(scheduled);
4557
4975
  var baseVastUrl;
4558
- if (this.apiVastTagUrl) {
4559
- baseVastUrl = this.apiVastTagUrl;
4560
- } else if (tags && tags.length > 0 && tags[0]) {
4976
+ if (tags && tags.length > 0 && tags[0]) {
4561
4977
  baseVastUrl = tags[0];
4978
+ } else if (this.apiVastTagUrl) {
4979
+ baseVastUrl = this.apiVastTagUrl;
4562
4980
  } else {
4563
4981
  if (this.config.debugAdTiming) {
4564
4982
  console.warn("[StormcloudVideoPlayer] No VAST URL available for prefetch");
@@ -4576,6 +4994,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4576
4994
  isFetching: false,
4577
4995
  fetchStartTime: Date.now()
4578
4996
  });
4997
+ this.adDetectSentForCurrentBreak = true;
4998
+ var detectPayload = {};
4999
+ if (marker.durationSeconds != null) detectPayload.durationSeconds = marker.durationSeconds;
5000
+ if (marker.ptsSeconds != null) detectPayload.ptsSeconds = marker.ptsSeconds;
5001
+ if (fragmentSn !== void 0) detectPayload.detectedAtFragmentSn = fragmentSn;
5002
+ sendAdDetectTracking(this.config.licenseKey, detectPayload).catch(function() {});
4579
5003
  if (this.config.debugAdTiming) {
4580
5004
  console.log("[PREFETCH] \uD83D\uDD04 Starting ad prefetch for upcoming ad break");
4581
5005
  console.log("[PREFETCH] \uD83D\uDCCB Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
@@ -5002,11 +5426,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5002
5426
  switch(_state.label){
5003
5427
  case 0:
5004
5428
  scheduled = this.findCurrentOrNextBreak(this.video.currentTime * 1e3);
5429
+ if (scheduled) {
5430
+ this.consumedVmapBreakIds.add(this.getAdBreakKey(scheduled));
5431
+ }
5005
5432
  tags = this.selectVastTagsForBreak(scheduled);
5006
- if (this.apiVastTagUrl) {
5007
- baseVastUrl = this.apiVastTagUrl;
5008
- } else if (tags && tags.length > 0 && tags[0]) {
5433
+ if (tags && tags.length > 0 && tags[0]) {
5009
5434
  baseVastUrl = tags[0];
5435
+ } else if (this.apiVastTagUrl) {
5436
+ baseVastUrl = this.apiVastTagUrl;
5010
5437
  } else {
5011
5438
  this.clearPendingAdBreak();
5012
5439
  return [
@@ -5051,6 +5478,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5051
5478
  firstAdUrl = preloaded.vastUrl;
5052
5479
  preloadedController = preloaded.imaController;
5053
5480
  usePreloadedAd = true;
5481
+ sendAdLoadedTracking(this.config.licenseKey, {
5482
+ source: "preload",
5483
+ vastUrl: firstAdUrl
5484
+ }, this.config.adPlayerType).catch(function() {});
5054
5485
  if (this.config.debugAdTiming) {
5055
5486
  console.log("[CONTINUOUS-FETCH] \uD83D\uDE80 Using preloaded ad from pool (preloaded in advance, ready immediately!)");
5056
5487
  console.log("[CONTINUOUS-FETCH] Pool still has ".concat(this.preloadPool.length, " preloaded ads ready"));
@@ -5140,6 +5571,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5140
5571
  ];
5141
5572
  case 5:
5142
5573
  _state.sent();
5574
+ sendAdLoadedTracking(this.config.licenseKey, {
5575
+ source: "ssp",
5576
+ vastUrl: firstAdUrl
5577
+ }, this.config.adPlayerType).catch(function() {});
5143
5578
  if (this.config.debugAdTiming) {
5144
5579
  console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
5145
5580
  }
@@ -5536,6 +5971,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5536
5971
  if (this.config.debugAdTiming) {
5537
5972
  console.log("[CONTINUOUS-FETCH] \uD83C\uDFAF Using preloaded ad from pool (".concat(this.preloadPool.length, " remaining in pool)"));
5538
5973
  }
5974
+ sendAdLoadedTracking(this.config.licenseKey, {
5975
+ source: "preload",
5976
+ vastUrl: preloaded.vastUrl
5977
+ }, this.config.adPlayerType).catch(function() {});
5539
5978
  _state.label = 1;
5540
5979
  case 1:
5541
5980
  _state.trys.push([
@@ -5873,15 +6312,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5873
6312
  {
5874
6313
  key: "findCurrentOrNextBreak",
5875
6314
  value: function findCurrentOrNextBreak(nowMs) {
5876
- var schedule = [];
6315
+ var schedule = this.vmapBreaks;
5877
6316
  var candidate;
5878
6317
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
5879
6318
  try {
5880
6319
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
5881
6320
  var b = _step.value;
5882
- var _this_config_driftToleranceMs;
6321
+ var _this_config_driftToleranceMs, _this_resolveBreakStartMs;
6322
+ if (this.consumedVmapBreakIds.has(this.getAdBreakKey(b))) {
6323
+ continue;
6324
+ }
6325
+ var breakStartMs = this.resolveBreakStartMs(b);
6326
+ if (breakStartMs == null) {
6327
+ continue;
6328
+ }
5883
6329
  var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
5884
- if (b.startTimeMs <= nowMs + tol && (candidate == null || b.startTimeMs > (candidate.startTimeMs || 0))) {
6330
+ if (breakStartMs <= nowMs + tol && (candidate == null || breakStartMs > ((_this_resolveBreakStartMs = this.resolveBreakStartMs(candidate)) !== null && _this_resolveBreakStartMs !== void 0 ? _this_resolveBreakStartMs : 0))) {
5885
6331
  candidate = b;
5886
6332
  }
5887
6333
  }
@@ -5905,11 +6351,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5905
6351
  {
5906
6352
  key: "onTimeUpdate",
5907
6353
  value: function onTimeUpdate(currentTimeSec) {
5908
- if (this.ima.isAdPlaying()) return;
6354
+ var _this = this;
6355
+ if (this.ima.isAdPlaying() || this.inAdBreak) return;
5909
6356
  var nowMs = currentTimeSec * 1e3;
5910
6357
  var breakToPlay = this.findBreakForTime(nowMs);
5911
6358
  if (breakToPlay) {
5912
- this.handleMidAdJoin(breakToPlay, nowMs);
6359
+ void this.handleMidAdJoin(breakToPlay, nowMs).catch(function(error) {
6360
+ if (_this.config.debugAdTiming) {
6361
+ console.warn("[StormcloudVideoPlayer] Mid-roll VMAP join failed gracefully:", error);
6362
+ }
6363
+ });
5913
6364
  }
5914
6365
  }
5915
6366
  },
@@ -5917,40 +6368,76 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5917
6368
  key: "handleMidAdJoin",
5918
6369
  value: function handleMidAdJoin(adBreak, nowMs) {
5919
6370
  return _async_to_generator(function() {
5920
- var _adBreak_durationMs, durationMs, endMs, remainingMs, tags, first, rest;
6371
+ var _adBreak_durationMs, _this_config_driftToleranceMs, key, breakStartMs, durationMs, endMs, tol, inWindow, remainingMs, tags, first, rest, error;
5921
6372
  return _ts_generator(this, function(_state) {
5922
6373
  switch(_state.label){
5923
6374
  case 0:
6375
+ key = this.getAdBreakKey(adBreak);
6376
+ if (this.consumedVmapBreakIds.has(key)) {
6377
+ return [
6378
+ 2
6379
+ ];
6380
+ }
6381
+ breakStartMs = this.resolveBreakStartMs(adBreak);
6382
+ if (breakStartMs == null) {
6383
+ return [
6384
+ 2
6385
+ ];
6386
+ }
5924
6387
  durationMs = (_adBreak_durationMs = adBreak.durationMs) !== null && _adBreak_durationMs !== void 0 ? _adBreak_durationMs : 0;
5925
- endMs = adBreak.startTimeMs + durationMs;
5926
- if (!(durationMs > 0 && nowMs > adBreak.startTimeMs && nowMs < endMs)) return [
6388
+ endMs = breakStartMs + durationMs;
6389
+ tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
6390
+ inWindow = durationMs > 0 ? nowMs > breakStartMs && nowMs < endMs : nowMs + tol >= breakStartMs;
6391
+ if (!inWindow) return [
5927
6392
  3,
5928
- 2
6393
+ 4
5929
6394
  ];
5930
- remainingMs = endMs - nowMs;
6395
+ this.consumedVmapBreakIds.add(key);
6396
+ remainingMs = durationMs > 0 ? Math.max(0, endMs - nowMs) : 0;
5931
6397
  tags = this.selectVastTagsForBreak(adBreak) || (this.apiVastTagUrl ? [
5932
6398
  this.apiVastTagUrl
5933
6399
  ] : void 0);
5934
6400
  if (!(tags && tags.length > 0)) return [
5935
6401
  3,
5936
- 2
6402
+ 4
5937
6403
  ];
5938
6404
  first = tags[0];
5939
6405
  rest = tags.slice(1);
5940
6406
  this.adPodQueue = rest;
5941
6407
  this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
6408
+ _state.label = 1;
6409
+ case 1:
6410
+ _state.trys.push([
6411
+ 1,
6412
+ 3,
6413
+ ,
6414
+ 4
6415
+ ]);
5942
6416
  return [
5943
6417
  4,
5944
6418
  this.playSingleAd(first)
5945
6419
  ];
5946
- case 1:
6420
+ case 2:
5947
6421
  _state.sent();
5948
6422
  this.inAdBreak = true;
5949
6423
  this.expectedAdBreakDurationMs = remainingMs;
5950
6424
  this.currentAdBreakStartWallClockMs = Date.now();
5951
6425
  this.scheduleAdStopCountdown(remainingMs);
5952
- _state.label = 2;
5953
- case 2:
6426
+ return [
6427
+ 3,
6428
+ 4
6429
+ ];
6430
+ case 3:
6431
+ error = _state.sent();
6432
+ this.adPodQueue = [];
6433
+ if (this.config.debugAdTiming) {
6434
+ console.warn("[StormcloudVideoPlayer] Mid-roll VMAP ad request failed:", error);
6435
+ }
6436
+ return [
6437
+ 3,
6438
+ 4
6439
+ ];
6440
+ case 4:
5954
6441
  return [
5955
6442
  2
5956
6443
  ];
@@ -6240,6 +6727,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6240
6727
  ];
6241
6728
  case 3:
6242
6729
  _state.sent();
6730
+ sendAdLoadedTracking(this.config.licenseKey, {
6731
+ source: "ssp",
6732
+ vastUrl: vastTagUrl
6733
+ }, this.config.adPlayerType).catch(function() {});
6243
6734
  this.clearAdRequestWatchdog();
6244
6735
  if (this.activeAdRequestToken !== requestToken) {
6245
6736
  return [
@@ -6454,6 +6945,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6454
6945
  }
6455
6946
  this.adRequestQueue = [];
6456
6947
  this.inAdBreak = false;
6948
+ this.adDetectSentForCurrentBreak = false;
6457
6949
  this.expectedAdBreakDurationMs = void 0;
6458
6950
  this.currentAdBreakStartWallClockMs = void 0;
6459
6951
  this.clearAdStartTimer();
@@ -6651,13 +7143,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6651
7143
  {
6652
7144
  key: "findBreakForTime",
6653
7145
  value: function findBreakForTime(nowMs) {
6654
- var schedule = [];
7146
+ var _this_config_driftToleranceMs;
7147
+ var schedule = this.vmapBreaks;
7148
+ var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
6655
7149
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
6656
7150
  try {
6657
7151
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
6658
7152
  var b = _step.value;
6659
- var end = (b.startTimeMs || 0) + (b.durationMs || 0);
6660
- if (nowMs >= (b.startTimeMs || 0) && (b.durationMs ? nowMs < end : true)) {
7153
+ if (this.consumedVmapBreakIds.has(this.getAdBreakKey(b))) {
7154
+ continue;
7155
+ }
7156
+ var breakStartMs = this.resolveBreakStartMs(b);
7157
+ if (breakStartMs == null) {
7158
+ continue;
7159
+ }
7160
+ var end = breakStartMs + (b.durationMs || 0);
7161
+ if (nowMs >= breakStartMs && (b.durationMs ? nowMs < end : nowMs <= breakStartMs + tol)) {
6661
7162
  return b;
6662
7163
  }
6663
7164
  }