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.
@@ -2609,9 +2609,6 @@ function sendInitialTracking(licenseKey) {
2609
2609
  headers = {
2610
2610
  "Content-Type": "application/json"
2611
2611
  };
2612
- if (licenseKey) {
2613
- headers["Authorization"] = "Bearer ".concat(licenseKey);
2614
- }
2615
2612
  return [
2616
2613
  4,
2617
2614
  fetch("https://adstorm.co/api-adstorm-dev/adstorm/player-tracking/track", {
@@ -2717,6 +2714,234 @@ function sendHeartbeat(licenseKey) {
2717
2714
  });
2718
2715
  })();
2719
2716
  }
2717
+ var TRACK_API_URL = "https://adstorm.co/api-adstorm-dev/adstorm/player-tracking/track";
2718
+ function mapToAdTrackingSource(source, adPlayerType) {
2719
+ if (source === "prebid" || source === "ima" || source === "hls") {
2720
+ return source;
2721
+ }
2722
+ if (source === "preload" || source === "ssp") {
2723
+ return source === "ssp" ? "prebid" : "ima";
2724
+ }
2725
+ return adPlayerType === "hls" ? "hls" : "ima";
2726
+ }
2727
+ function postAdTracking(licenseKey, body) {
2728
+ return _async_to_generator(function() {
2729
+ var headers, response;
2730
+ return _ts_generator(this, function(_state) {
2731
+ switch(_state.label){
2732
+ case 0:
2733
+ headers = {
2734
+ "Content-Type": "application/json"
2735
+ };
2736
+ if (licenseKey) {
2737
+ headers["Authorization"] = "Bearer ".concat(licenseKey);
2738
+ }
2739
+ return [
2740
+ 4,
2741
+ fetch(TRACK_API_URL, {
2742
+ method: "POST",
2743
+ headers: headers,
2744
+ body: JSON.stringify(body)
2745
+ })
2746
+ ];
2747
+ case 1:
2748
+ response = _state.sent();
2749
+ if (!response.ok) {
2750
+ throw new Error("HTTP error! status: ".concat(response.status));
2751
+ }
2752
+ return [
2753
+ 4,
2754
+ response.json()
2755
+ ];
2756
+ case 2:
2757
+ _state.sent();
2758
+ return [
2759
+ 2
2760
+ ];
2761
+ }
2762
+ });
2763
+ })();
2764
+ }
2765
+ function sendAdDetectTracking(licenseKey, payload) {
2766
+ return _async_to_generator(function() {
2767
+ var _payload_source, _payload_timestamp, clientInfo, browserId, adDetectInfo, body, error;
2768
+ return _ts_generator(this, function(_state) {
2769
+ switch(_state.label){
2770
+ case 0:
2771
+ _state.trys.push([
2772
+ 0,
2773
+ 3,
2774
+ ,
2775
+ 4
2776
+ ]);
2777
+ clientInfo = getClientInfo();
2778
+ return [
2779
+ 4,
2780
+ getBrowserID(clientInfo)
2781
+ ];
2782
+ case 1:
2783
+ browserId = _state.sent();
2784
+ adDetectInfo = _object_spread({
2785
+ source: (_payload_source = payload.source) !== null && _payload_source !== void 0 ? _payload_source : "scte35",
2786
+ timestamp: (_payload_timestamp = payload.timestamp) !== null && _payload_timestamp !== void 0 ? _payload_timestamp : /* @__PURE__ */ new Date().toISOString()
2787
+ }, payload.durationSeconds != null && {
2788
+ durationSeconds: payload.durationSeconds
2789
+ }, payload.ptsSeconds != null && {
2790
+ ptsSeconds: payload.ptsSeconds
2791
+ }, payload.detectedAtFragmentSn != null && {
2792
+ detectedAtFragmentSn: payload.detectedAtFragmentSn
2793
+ });
2794
+ body = _object_spread_props(_object_spread({
2795
+ browserId: browserId
2796
+ }, clientInfo, licenseKey && {
2797
+ licenseKey: licenseKey
2798
+ }), {
2799
+ adDetectInfo: adDetectInfo
2800
+ });
2801
+ return [
2802
+ 4,
2803
+ postAdTracking(licenseKey, body)
2804
+ ];
2805
+ case 2:
2806
+ _state.sent();
2807
+ return [
2808
+ 3,
2809
+ 4
2810
+ ];
2811
+ case 3:
2812
+ error = _state.sent();
2813
+ console.error("[StormcloudVideoPlayer] Error sending ad-detect tracking:", error);
2814
+ return [
2815
+ 3,
2816
+ 4
2817
+ ];
2818
+ case 4:
2819
+ return [
2820
+ 2
2821
+ ];
2822
+ }
2823
+ });
2824
+ })();
2825
+ }
2826
+ function sendAdLoadedTracking(licenseKey, payload, adPlayerType) {
2827
+ return _async_to_generator(function() {
2828
+ var clientInfo, browserId, source, adLoadedInfo, body, error;
2829
+ return _ts_generator(this, function(_state) {
2830
+ switch(_state.label){
2831
+ case 0:
2832
+ _state.trys.push([
2833
+ 0,
2834
+ 3,
2835
+ ,
2836
+ 4
2837
+ ]);
2838
+ clientInfo = getClientInfo();
2839
+ return [
2840
+ 4,
2841
+ getBrowserID(clientInfo)
2842
+ ];
2843
+ case 1:
2844
+ browserId = _state.sent();
2845
+ source = mapToAdTrackingSource(payload.source, adPlayerType);
2846
+ adLoadedInfo = _object_spread({
2847
+ source: source,
2848
+ timestamp: /* @__PURE__ */ new Date().toISOString()
2849
+ }, payload.vastUrl != null && {
2850
+ vastUrl: payload.vastUrl
2851
+ }, payload.durationSeconds != null && {
2852
+ durationSeconds: payload.durationSeconds
2853
+ });
2854
+ body = _object_spread_props(_object_spread({
2855
+ browserId: browserId
2856
+ }, clientInfo, licenseKey && {
2857
+ licenseKey: licenseKey
2858
+ }), {
2859
+ adLoadedInfo: adLoadedInfo
2860
+ });
2861
+ return [
2862
+ 4,
2863
+ postAdTracking(licenseKey, body)
2864
+ ];
2865
+ case 2:
2866
+ _state.sent();
2867
+ return [
2868
+ 3,
2869
+ 4
2870
+ ];
2871
+ case 3:
2872
+ error = _state.sent();
2873
+ console.error("[StormcloudVideoPlayer] Error sending ad-loaded tracking:", error);
2874
+ return [
2875
+ 3,
2876
+ 4
2877
+ ];
2878
+ case 4:
2879
+ return [
2880
+ 2
2881
+ ];
2882
+ }
2883
+ });
2884
+ })();
2885
+ }
2886
+ function sendAdImpressionTracking(licenseKey, payload, adPlayerType) {
2887
+ return _async_to_generator(function() {
2888
+ var clientInfo, browserId, source, adImpressionInfo, body, error;
2889
+ return _ts_generator(this, function(_state) {
2890
+ switch(_state.label){
2891
+ case 0:
2892
+ _state.trys.push([
2893
+ 0,
2894
+ 3,
2895
+ ,
2896
+ 4
2897
+ ]);
2898
+ clientInfo = getClientInfo();
2899
+ return [
2900
+ 4,
2901
+ getBrowserID(clientInfo)
2902
+ ];
2903
+ case 1:
2904
+ browserId = _state.sent();
2905
+ source = mapToAdTrackingSource(payload.source, adPlayerType);
2906
+ adImpressionInfo = _object_spread({
2907
+ source: source,
2908
+ adIndex: payload.adIndex,
2909
+ timestamp: /* @__PURE__ */ new Date().toISOString()
2910
+ }, payload.durationSeconds != null && {
2911
+ durationSeconds: payload.durationSeconds
2912
+ });
2913
+ body = _object_spread_props(_object_spread({
2914
+ browserId: browserId
2915
+ }, clientInfo, licenseKey && {
2916
+ licenseKey: licenseKey
2917
+ }), {
2918
+ adImpressionInfo: adImpressionInfo
2919
+ });
2920
+ return [
2921
+ 4,
2922
+ postAdTracking(licenseKey, body)
2923
+ ];
2924
+ case 2:
2925
+ _state.sent();
2926
+ return [
2927
+ 3,
2928
+ 4
2929
+ ];
2930
+ case 3:
2931
+ error = _state.sent();
2932
+ console.error("[StormcloudVideoPlayer] Error sending ad-impression tracking:", error);
2933
+ return [
2934
+ 3,
2935
+ 4
2936
+ ];
2937
+ case 4:
2938
+ return [
2939
+ 2
2940
+ ];
2941
+ }
2942
+ });
2943
+ })();
2944
+ }
2720
2945
  // src/utils/polyfills.ts
2721
2946
  function polyfillURLSearchParams() {
2722
2947
  if (typeof URLSearchParams !== "undefined") {
@@ -2976,6 +3201,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2976
3201
  this.inAdBreak = false;
2977
3202
  this.ptsDriftEmaMs = 0;
2978
3203
  this.adPodQueue = [];
3204
+ this.vmapBreaks = [];
3205
+ this.consumedVmapBreakIds = /* @__PURE__ */ new Set();
2979
3206
  this.lastHeartbeatTime = 0;
2980
3207
  this.currentAdIndex = 0;
2981
3208
  this.totalAdsInBreak = 0;
@@ -3017,6 +3244,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3017
3244
  this.maxPreloadPoolSize = 2;
3018
3245
  this.preloadPoolActive = false;
3019
3246
  this.preloadPoolLoopRunning = false;
3247
+ this.adDetectSentForCurrentBreak = false;
3020
3248
  this.continuousFetchLoopRunning = false;
3021
3249
  initializePolyfills();
3022
3250
  var browserOverrides = getBrowserConfigOverrides();
@@ -3486,6 +3714,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3486
3714
  value: function attachImaEventListeners() {
3487
3715
  var _this = this;
3488
3716
  this.ima.on("all_ads_completed", function() {
3717
+ sendAdImpressionTracking(_this.config.licenseKey, {
3718
+ adIndex: _this.currentAdIndex
3719
+ }, _this.config.adPlayerType).catch(function() {});
3489
3720
  var remaining = _this.getRemainingAdMs();
3490
3721
  _this.consecutiveFailures = 0;
3491
3722
  if (_this.config.debugAdTiming) {
@@ -3548,6 +3779,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3548
3779
  }
3549
3780
  });
3550
3781
  this.ima.on("content_resume", function() {
3782
+ sendAdImpressionTracking(_this.config.licenseKey, {
3783
+ adIndex: _this.currentAdIndex
3784
+ }, _this.config.adPlayerType).catch(function() {});
3551
3785
  if (!_this.video.muted) {
3552
3786
  _this.video.muted = true;
3553
3787
  _this.video.volume = 0;
@@ -3931,6 +4165,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3931
4165
  }
3932
4166
  return;
3933
4167
  }
4168
+ if (!this.adDetectSentForCurrentBreak) {
4169
+ this.adDetectSentForCurrentBreak = true;
4170
+ var detectPayload = {};
4171
+ if (marker.durationSeconds != null) detectPayload.durationSeconds = marker.durationSeconds;
4172
+ if (marker.ptsSeconds != null) detectPayload.ptsSeconds = marker.ptsSeconds;
4173
+ sendAdDetectTracking(this.config.licenseKey, detectPayload).catch(function() {});
4174
+ }
3934
4175
  var hasPrefetchedAds = this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0;
3935
4176
  this.inAdBreak = true;
3936
4177
  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;
@@ -4290,6 +4531,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4290
4531
  return _ts_generator(this, function(_state) {
4291
4532
  switch(_state.label){
4292
4533
  case 0:
4534
+ if (!this.config.vmapUrl) return [
4535
+ 3,
4536
+ 2
4537
+ ];
4538
+ return [
4539
+ 4,
4540
+ this.fetchAndParseVmap(this.config.vmapUrl)
4541
+ ];
4542
+ case 1:
4543
+ _state.sent();
4544
+ _state.label = 2;
4545
+ case 2:
4293
4546
  vastMode = this.config.vastMode || "default";
4294
4547
  if (this.config.debugAdTiming) {
4295
4548
  console.log("[StormcloudVideoPlayer] VAST mode:", vastMode);
@@ -4335,7 +4588,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4335
4588
  headers: headers
4336
4589
  })
4337
4590
  ];
4338
- case 1:
4591
+ case 3:
4339
4592
  response = _state.sent();
4340
4593
  if (!response.ok) {
4341
4594
  if (this.config.debugAdTiming) {
@@ -4349,7 +4602,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4349
4602
  4,
4350
4603
  response.json()
4351
4604
  ];
4352
- case 2:
4605
+ case 4:
4353
4606
  data = _state.sent();
4354
4607
  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;
4355
4608
  if (imaPayload) {
@@ -4377,6 +4630,171 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4377
4630
  }).call(this);
4378
4631
  }
4379
4632
  },
4633
+ {
4634
+ key: "fetchAndParseVmap",
4635
+ value: function fetchAndParseVmap(vmapUrl) {
4636
+ return _async_to_generator(function() {
4637
+ var response, vmapXml, error;
4638
+ return _ts_generator(this, function(_state) {
4639
+ switch(_state.label){
4640
+ case 0:
4641
+ if (!vmapUrl.trim()) {
4642
+ return [
4643
+ 2
4644
+ ];
4645
+ }
4646
+ _state.label = 1;
4647
+ case 1:
4648
+ _state.trys.push([
4649
+ 1,
4650
+ 4,
4651
+ ,
4652
+ 5
4653
+ ]);
4654
+ return [
4655
+ 4,
4656
+ fetch(vmapUrl)
4657
+ ];
4658
+ case 2:
4659
+ response = _state.sent();
4660
+ if (!response.ok) {
4661
+ throw new Error("Failed to fetch VMAP (".concat(response.status, ")"));
4662
+ }
4663
+ return [
4664
+ 4,
4665
+ response.text()
4666
+ ];
4667
+ case 3:
4668
+ vmapXml = _state.sent();
4669
+ this.vmapBreaks = this.parseVmapToBreaks(vmapXml);
4670
+ this.consumedVmapBreakIds.clear();
4671
+ if (this.config.debugAdTiming) {
4672
+ console.log("[StormcloudVideoPlayer] Loaded ".concat(this.vmapBreaks.length, " VMAP ad break(s) from:"), vmapUrl);
4673
+ }
4674
+ return [
4675
+ 3,
4676
+ 5
4677
+ ];
4678
+ case 4:
4679
+ error = _state.sent();
4680
+ this.vmapBreaks = [];
4681
+ this.consumedVmapBreakIds.clear();
4682
+ if (this.config.debugAdTiming) {
4683
+ console.warn("[StormcloudVideoPlayer] Failed to load VMAP:", error);
4684
+ }
4685
+ return [
4686
+ 3,
4687
+ 5
4688
+ ];
4689
+ case 5:
4690
+ return [
4691
+ 2
4692
+ ];
4693
+ }
4694
+ });
4695
+ }).call(this);
4696
+ }
4697
+ },
4698
+ {
4699
+ key: "parseVmapToBreaks",
4700
+ value: function parseVmapToBreaks(vmapXml) {
4701
+ var _this = this;
4702
+ if (typeof DOMParser === "undefined") {
4703
+ return [];
4704
+ }
4705
+ var doc = new DOMParser().parseFromString(vmapXml, "application/xml");
4706
+ if (doc.querySelector("parsererror")) {
4707
+ if (this.config.debugAdTiming) {
4708
+ console.warn("[StormcloudVideoPlayer] Invalid VMAP XML received");
4709
+ }
4710
+ return [];
4711
+ }
4712
+ var adBreakNodes = Array.from(doc.querySelectorAll("AdBreak, vmap\\:AdBreak"));
4713
+ var parsed = [];
4714
+ adBreakNodes.forEach(function(node, index) {
4715
+ var timeOffsetRaw = (node.getAttribute("timeOffset") || "").trim();
4716
+ var startTimeMs = _this.parseVmapTimeOffsetToMs(timeOffsetRaw);
4717
+ if (startTimeMs == null) {
4718
+ return;
4719
+ }
4720
+ var adTagNode = node.querySelector("AdTagURI, vmap\\:AdTagURI");
4721
+ var adTagUrl = ((adTagNode === null || adTagNode === void 0 ? void 0 : adTagNode.textContent) || "").trim();
4722
+ if (!adTagUrl) {
4723
+ return;
4724
+ }
4725
+ var breakId = node.getAttribute("breakId") || "vmap-break-".concat(index, "-").concat(timeOffsetRaw || "unknown");
4726
+ parsed.push({
4727
+ id: breakId,
4728
+ startTimeMs: startTimeMs,
4729
+ vastTagUrl: adTagUrl
4730
+ });
4731
+ });
4732
+ parsed.sort(function(a, b) {
4733
+ var aStart = a.startTimeMs < 0 ? Number.MAX_SAFE_INTEGER : a.startTimeMs;
4734
+ var bStart = b.startTimeMs < 0 ? Number.MAX_SAFE_INTEGER : b.startTimeMs;
4735
+ return aStart - bStart;
4736
+ });
4737
+ return parsed;
4738
+ }
4739
+ },
4740
+ {
4741
+ key: "parseVmapTimeOffsetToMs",
4742
+ value: function parseVmapTimeOffsetToMs(timeOffset) {
4743
+ if (!timeOffset) {
4744
+ return void 0;
4745
+ }
4746
+ var normalized = timeOffset.trim().toLowerCase();
4747
+ if (normalized === "start") {
4748
+ return 0;
4749
+ }
4750
+ if (normalized === "end") {
4751
+ return -1;
4752
+ }
4753
+ var hms = timeOffset.match(/^(\d{1,2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?$/);
4754
+ if (hms) {
4755
+ 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;
4756
+ var hours = Number(hh);
4757
+ var minutes = Number(mm);
4758
+ var seconds = Number(ss);
4759
+ var millis = Number(ms.padEnd(3, "0").slice(0, 3));
4760
+ return (hours * 3600 + minutes * 60 + seconds) * 1e3 + millis;
4761
+ }
4762
+ var percent = timeOffset.match(/^(\d+(?:\.\d+)?)%$/);
4763
+ if (percent) {
4764
+ var ratio = Number(percent[1]) / 100;
4765
+ var durationSec = this.video.duration;
4766
+ if (Number.isFinite(durationSec) && durationSec > 0) {
4767
+ return Math.floor(durationSec * 1e3 * ratio);
4768
+ }
4769
+ return void 0;
4770
+ }
4771
+ return void 0;
4772
+ }
4773
+ },
4774
+ {
4775
+ key: "getAdBreakKey",
4776
+ value: function getAdBreakKey(adBreak) {
4777
+ if (adBreak.id) {
4778
+ return adBreak.id;
4779
+ }
4780
+ return "".concat(adBreak.startTimeMs, "-").concat(adBreak.vastTagUrl || "");
4781
+ }
4782
+ },
4783
+ {
4784
+ key: "resolveBreakStartMs",
4785
+ value: function resolveBreakStartMs(adBreak) {
4786
+ if (adBreak.startTimeMs >= 0) {
4787
+ return adBreak.startTimeMs;
4788
+ }
4789
+ if (adBreak.startTimeMs === -1) {
4790
+ var durationSec = this.video.duration;
4791
+ if (Number.isFinite(durationSec) && durationSec > 0) {
4792
+ return Math.floor(durationSec * 1e3);
4793
+ }
4794
+ }
4795
+ return void 0;
4796
+ }
4797
+ },
4380
4798
  {
4381
4799
  key: "getCurrentAdIndex",
4382
4800
  value: function getCurrentAdIndex() {
@@ -4467,10 +4885,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4467
4885
  var scheduled = this.findCurrentOrNextBreak(this.video.currentTime * 1e3);
4468
4886
  var tags = this.selectVastTagsForBreak(scheduled);
4469
4887
  var baseVastUrl;
4470
- if (this.apiVastTagUrl) {
4471
- baseVastUrl = this.apiVastTagUrl;
4472
- } else if (tags && tags.length > 0 && tags[0]) {
4888
+ if (tags && tags.length > 0 && tags[0]) {
4473
4889
  baseVastUrl = tags[0];
4890
+ } else if (this.apiVastTagUrl) {
4891
+ baseVastUrl = this.apiVastTagUrl;
4474
4892
  } else {
4475
4893
  if (this.config.debugAdTiming) {
4476
4894
  console.warn("[StormcloudVideoPlayer] No VAST URL available for prefetch");
@@ -4488,6 +4906,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4488
4906
  isFetching: false,
4489
4907
  fetchStartTime: Date.now()
4490
4908
  });
4909
+ this.adDetectSentForCurrentBreak = true;
4910
+ var detectPayload = {};
4911
+ if (marker.durationSeconds != null) detectPayload.durationSeconds = marker.durationSeconds;
4912
+ if (marker.ptsSeconds != null) detectPayload.ptsSeconds = marker.ptsSeconds;
4913
+ if (fragmentSn !== void 0) detectPayload.detectedAtFragmentSn = fragmentSn;
4914
+ sendAdDetectTracking(this.config.licenseKey, detectPayload).catch(function() {});
4491
4915
  if (this.config.debugAdTiming) {
4492
4916
  console.log("[PREFETCH] \uD83D\uDD04 Starting ad prefetch for upcoming ad break");
4493
4917
  console.log("[PREFETCH] \uD83D\uDCCB Pre-generated ".concat(generatedUrls.length, " VAST URLs"));
@@ -4914,11 +5338,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4914
5338
  switch(_state.label){
4915
5339
  case 0:
4916
5340
  scheduled = this.findCurrentOrNextBreak(this.video.currentTime * 1e3);
5341
+ if (scheduled) {
5342
+ this.consumedVmapBreakIds.add(this.getAdBreakKey(scheduled));
5343
+ }
4917
5344
  tags = this.selectVastTagsForBreak(scheduled);
4918
- if (this.apiVastTagUrl) {
4919
- baseVastUrl = this.apiVastTagUrl;
4920
- } else if (tags && tags.length > 0 && tags[0]) {
5345
+ if (tags && tags.length > 0 && tags[0]) {
4921
5346
  baseVastUrl = tags[0];
5347
+ } else if (this.apiVastTagUrl) {
5348
+ baseVastUrl = this.apiVastTagUrl;
4922
5349
  } else {
4923
5350
  this.clearPendingAdBreak();
4924
5351
  return [
@@ -4963,6 +5390,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4963
5390
  firstAdUrl = preloaded.vastUrl;
4964
5391
  preloadedController = preloaded.imaController;
4965
5392
  usePreloadedAd = true;
5393
+ sendAdLoadedTracking(this.config.licenseKey, {
5394
+ source: "preload",
5395
+ vastUrl: firstAdUrl
5396
+ }, this.config.adPlayerType).catch(function() {});
4966
5397
  if (this.config.debugAdTiming) {
4967
5398
  console.log("[CONTINUOUS-FETCH] \uD83D\uDE80 Using preloaded ad from pool (preloaded in advance, ready immediately!)");
4968
5399
  console.log("[CONTINUOUS-FETCH] Pool still has ".concat(this.preloadPool.length, " preloaded ads ready"));
@@ -5052,6 +5483,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5052
5483
  ];
5053
5484
  case 5:
5054
5485
  _state.sent();
5486
+ sendAdLoadedTracking(this.config.licenseKey, {
5487
+ source: "ssp",
5488
+ vastUrl: firstAdUrl
5489
+ }, this.config.adPlayerType).catch(function() {});
5055
5490
  if (this.config.debugAdTiming) {
5056
5491
  console.log("[CONTINUOUS-FETCH] \u2705 First ad request successful, starting playback");
5057
5492
  }
@@ -5448,6 +5883,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5448
5883
  if (this.config.debugAdTiming) {
5449
5884
  console.log("[CONTINUOUS-FETCH] \uD83C\uDFAF Using preloaded ad from pool (".concat(this.preloadPool.length, " remaining in pool)"));
5450
5885
  }
5886
+ sendAdLoadedTracking(this.config.licenseKey, {
5887
+ source: "preload",
5888
+ vastUrl: preloaded.vastUrl
5889
+ }, this.config.adPlayerType).catch(function() {});
5451
5890
  _state.label = 1;
5452
5891
  case 1:
5453
5892
  _state.trys.push([
@@ -5785,15 +6224,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5785
6224
  {
5786
6225
  key: "findCurrentOrNextBreak",
5787
6226
  value: function findCurrentOrNextBreak(nowMs) {
5788
- var schedule = [];
6227
+ var schedule = this.vmapBreaks;
5789
6228
  var candidate;
5790
6229
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
5791
6230
  try {
5792
6231
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
5793
6232
  var b = _step.value;
5794
- var _this_config_driftToleranceMs;
6233
+ var _this_config_driftToleranceMs, _this_resolveBreakStartMs;
6234
+ if (this.consumedVmapBreakIds.has(this.getAdBreakKey(b))) {
6235
+ continue;
6236
+ }
6237
+ var breakStartMs = this.resolveBreakStartMs(b);
6238
+ if (breakStartMs == null) {
6239
+ continue;
6240
+ }
5795
6241
  var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
5796
- if (b.startTimeMs <= nowMs + tol && (candidate == null || b.startTimeMs > (candidate.startTimeMs || 0))) {
6242
+ if (breakStartMs <= nowMs + tol && (candidate == null || breakStartMs > ((_this_resolveBreakStartMs = this.resolveBreakStartMs(candidate)) !== null && _this_resolveBreakStartMs !== void 0 ? _this_resolveBreakStartMs : 0))) {
5797
6243
  candidate = b;
5798
6244
  }
5799
6245
  }
@@ -5817,11 +6263,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5817
6263
  {
5818
6264
  key: "onTimeUpdate",
5819
6265
  value: function onTimeUpdate(currentTimeSec) {
5820
- if (this.ima.isAdPlaying()) return;
6266
+ var _this = this;
6267
+ if (this.ima.isAdPlaying() || this.inAdBreak) return;
5821
6268
  var nowMs = currentTimeSec * 1e3;
5822
6269
  var breakToPlay = this.findBreakForTime(nowMs);
5823
6270
  if (breakToPlay) {
5824
- this.handleMidAdJoin(breakToPlay, nowMs);
6271
+ void this.handleMidAdJoin(breakToPlay, nowMs).catch(function(error) {
6272
+ if (_this.config.debugAdTiming) {
6273
+ console.warn("[StormcloudVideoPlayer] Mid-roll VMAP join failed gracefully:", error);
6274
+ }
6275
+ });
5825
6276
  }
5826
6277
  }
5827
6278
  },
@@ -5829,40 +6280,76 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5829
6280
  key: "handleMidAdJoin",
5830
6281
  value: function handleMidAdJoin(adBreak, nowMs) {
5831
6282
  return _async_to_generator(function() {
5832
- var _adBreak_durationMs, durationMs, endMs, remainingMs, tags, first, rest;
6283
+ var _adBreak_durationMs, _this_config_driftToleranceMs, key, breakStartMs, durationMs, endMs, tol, inWindow, remainingMs, tags, first, rest, error;
5833
6284
  return _ts_generator(this, function(_state) {
5834
6285
  switch(_state.label){
5835
6286
  case 0:
6287
+ key = this.getAdBreakKey(adBreak);
6288
+ if (this.consumedVmapBreakIds.has(key)) {
6289
+ return [
6290
+ 2
6291
+ ];
6292
+ }
6293
+ breakStartMs = this.resolveBreakStartMs(adBreak);
6294
+ if (breakStartMs == null) {
6295
+ return [
6296
+ 2
6297
+ ];
6298
+ }
5836
6299
  durationMs = (_adBreak_durationMs = adBreak.durationMs) !== null && _adBreak_durationMs !== void 0 ? _adBreak_durationMs : 0;
5837
- endMs = adBreak.startTimeMs + durationMs;
5838
- if (!(durationMs > 0 && nowMs > adBreak.startTimeMs && nowMs < endMs)) return [
6300
+ endMs = breakStartMs + durationMs;
6301
+ tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
6302
+ inWindow = durationMs > 0 ? nowMs > breakStartMs && nowMs < endMs : nowMs + tol >= breakStartMs;
6303
+ if (!inWindow) return [
5839
6304
  3,
5840
- 2
6305
+ 4
5841
6306
  ];
5842
- remainingMs = endMs - nowMs;
6307
+ this.consumedVmapBreakIds.add(key);
6308
+ remainingMs = durationMs > 0 ? Math.max(0, endMs - nowMs) : 0;
5843
6309
  tags = this.selectVastTagsForBreak(adBreak) || (this.apiVastTagUrl ? [
5844
6310
  this.apiVastTagUrl
5845
6311
  ] : void 0);
5846
6312
  if (!(tags && tags.length > 0)) return [
5847
6313
  3,
5848
- 2
6314
+ 4
5849
6315
  ];
5850
6316
  first = tags[0];
5851
6317
  rest = tags.slice(1);
5852
6318
  this.adPodQueue = rest;
5853
6319
  this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
6320
+ _state.label = 1;
6321
+ case 1:
6322
+ _state.trys.push([
6323
+ 1,
6324
+ 3,
6325
+ ,
6326
+ 4
6327
+ ]);
5854
6328
  return [
5855
6329
  4,
5856
6330
  this.playSingleAd(first)
5857
6331
  ];
5858
- case 1:
6332
+ case 2:
5859
6333
  _state.sent();
5860
6334
  this.inAdBreak = true;
5861
6335
  this.expectedAdBreakDurationMs = remainingMs;
5862
6336
  this.currentAdBreakStartWallClockMs = Date.now();
5863
6337
  this.scheduleAdStopCountdown(remainingMs);
5864
- _state.label = 2;
5865
- case 2:
6338
+ return [
6339
+ 3,
6340
+ 4
6341
+ ];
6342
+ case 3:
6343
+ error = _state.sent();
6344
+ this.adPodQueue = [];
6345
+ if (this.config.debugAdTiming) {
6346
+ console.warn("[StormcloudVideoPlayer] Mid-roll VMAP ad request failed:", error);
6347
+ }
6348
+ return [
6349
+ 3,
6350
+ 4
6351
+ ];
6352
+ case 4:
5866
6353
  return [
5867
6354
  2
5868
6355
  ];
@@ -6152,6 +6639,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6152
6639
  ];
6153
6640
  case 3:
6154
6641
  _state.sent();
6642
+ sendAdLoadedTracking(this.config.licenseKey, {
6643
+ source: "ssp",
6644
+ vastUrl: vastTagUrl
6645
+ }, this.config.adPlayerType).catch(function() {});
6155
6646
  this.clearAdRequestWatchdog();
6156
6647
  if (this.activeAdRequestToken !== requestToken) {
6157
6648
  return [
@@ -6366,6 +6857,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6366
6857
  }
6367
6858
  this.adRequestQueue = [];
6368
6859
  this.inAdBreak = false;
6860
+ this.adDetectSentForCurrentBreak = false;
6369
6861
  this.expectedAdBreakDurationMs = void 0;
6370
6862
  this.currentAdBreakStartWallClockMs = void 0;
6371
6863
  this.clearAdStartTimer();
@@ -6563,13 +7055,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6563
7055
  {
6564
7056
  key: "findBreakForTime",
6565
7057
  value: function findBreakForTime(nowMs) {
6566
- var schedule = [];
7058
+ var _this_config_driftToleranceMs;
7059
+ var schedule = this.vmapBreaks;
7060
+ var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
6567
7061
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
6568
7062
  try {
6569
7063
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
6570
7064
  var b = _step.value;
6571
- var end = (b.startTimeMs || 0) + (b.durationMs || 0);
6572
- if (nowMs >= (b.startTimeMs || 0) && (b.durationMs ? nowMs < end : true)) {
7065
+ if (this.consumedVmapBreakIds.has(this.getAdBreakKey(b))) {
7066
+ continue;
7067
+ }
7068
+ var breakStartMs = this.resolveBreakStartMs(b);
7069
+ if (breakStartMs == null) {
7070
+ continue;
7071
+ }
7072
+ var end = breakStartMs + (b.durationMs || 0);
7073
+ if (nowMs >= breakStartMs && (b.durationMs ? nowMs < end : nowMs <= breakStartMs + tol)) {
6573
7074
  return b;
6574
7075
  }
6575
7076
  }