stormcloud-video-player 0.3.55 → 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.
@@ -1,5 +1,5 @@
1
1
  import { Component } from 'react';
2
- import { S as StormcloudVideoPlayerConfig } from '../types-C23mJ_hh.cjs';
2
+ import { S as StormcloudVideoPlayerConfig } from '../types-Vj55FghO.cjs';
3
3
 
4
4
  interface HlsPlayerProps extends StormcloudVideoPlayerConfig {
5
5
  onMount?: (player: any) => void;
@@ -3289,6 +3289,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3289
3289
  this.inAdBreak = false;
3290
3290
  this.ptsDriftEmaMs = 0;
3291
3291
  this.adPodQueue = [];
3292
+ this.vmapBreaks = [];
3293
+ this.consumedVmapBreakIds = /* @__PURE__ */ new Set();
3292
3294
  this.lastHeartbeatTime = 0;
3293
3295
  this.currentAdIndex = 0;
3294
3296
  this.totalAdsInBreak = 0;
@@ -4617,6 +4619,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4617
4619
  return _ts_generator(this, function(_state) {
4618
4620
  switch(_state.label){
4619
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:
4620
4634
  vastMode = this.config.vastMode || "default";
4621
4635
  if (this.config.debugAdTiming) {
4622
4636
  console.log("[StormcloudVideoPlayer] VAST mode:", vastMode);
@@ -4662,7 +4676,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4662
4676
  headers: headers
4663
4677
  })
4664
4678
  ];
4665
- case 1:
4679
+ case 3:
4666
4680
  response = _state.sent();
4667
4681
  if (!response.ok) {
4668
4682
  if (this.config.debugAdTiming) {
@@ -4676,7 +4690,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4676
4690
  4,
4677
4691
  response.json()
4678
4692
  ];
4679
- case 2:
4693
+ case 4:
4680
4694
  data = _state.sent();
4681
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;
4682
4696
  if (imaPayload) {
@@ -4704,6 +4718,171 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4704
4718
  }).call(this);
4705
4719
  }
4706
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
+ },
4707
4886
  {
4708
4887
  key: "getCurrentAdIndex",
4709
4888
  value: function getCurrentAdIndex() {
@@ -4794,10 +4973,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4794
4973
  var scheduled = this.findCurrentOrNextBreak(this.video.currentTime * 1e3);
4795
4974
  var tags = this.selectVastTagsForBreak(scheduled);
4796
4975
  var baseVastUrl;
4797
- if (this.apiVastTagUrl) {
4798
- baseVastUrl = this.apiVastTagUrl;
4799
- } else if (tags && tags.length > 0 && tags[0]) {
4976
+ if (tags && tags.length > 0 && tags[0]) {
4800
4977
  baseVastUrl = tags[0];
4978
+ } else if (this.apiVastTagUrl) {
4979
+ baseVastUrl = this.apiVastTagUrl;
4801
4980
  } else {
4802
4981
  if (this.config.debugAdTiming) {
4803
4982
  console.warn("[StormcloudVideoPlayer] No VAST URL available for prefetch");
@@ -5247,11 +5426,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5247
5426
  switch(_state.label){
5248
5427
  case 0:
5249
5428
  scheduled = this.findCurrentOrNextBreak(this.video.currentTime * 1e3);
5429
+ if (scheduled) {
5430
+ this.consumedVmapBreakIds.add(this.getAdBreakKey(scheduled));
5431
+ }
5250
5432
  tags = this.selectVastTagsForBreak(scheduled);
5251
- if (this.apiVastTagUrl) {
5252
- baseVastUrl = this.apiVastTagUrl;
5253
- } else if (tags && tags.length > 0 && tags[0]) {
5433
+ if (tags && tags.length > 0 && tags[0]) {
5254
5434
  baseVastUrl = tags[0];
5435
+ } else if (this.apiVastTagUrl) {
5436
+ baseVastUrl = this.apiVastTagUrl;
5255
5437
  } else {
5256
5438
  this.clearPendingAdBreak();
5257
5439
  return [
@@ -6130,15 +6312,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6130
6312
  {
6131
6313
  key: "findCurrentOrNextBreak",
6132
6314
  value: function findCurrentOrNextBreak(nowMs) {
6133
- var schedule = [];
6315
+ var schedule = this.vmapBreaks;
6134
6316
  var candidate;
6135
6317
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
6136
6318
  try {
6137
6319
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
6138
6320
  var b = _step.value;
6139
- 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
+ }
6140
6329
  var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
6141
- 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))) {
6142
6331
  candidate = b;
6143
6332
  }
6144
6333
  }
@@ -6162,11 +6351,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6162
6351
  {
6163
6352
  key: "onTimeUpdate",
6164
6353
  value: function onTimeUpdate(currentTimeSec) {
6165
- if (this.ima.isAdPlaying()) return;
6354
+ var _this = this;
6355
+ if (this.ima.isAdPlaying() || this.inAdBreak) return;
6166
6356
  var nowMs = currentTimeSec * 1e3;
6167
6357
  var breakToPlay = this.findBreakForTime(nowMs);
6168
6358
  if (breakToPlay) {
6169
- 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
+ });
6170
6364
  }
6171
6365
  }
6172
6366
  },
@@ -6174,40 +6368,76 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6174
6368
  key: "handleMidAdJoin",
6175
6369
  value: function handleMidAdJoin(adBreak, nowMs) {
6176
6370
  return _async_to_generator(function() {
6177
- 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;
6178
6372
  return _ts_generator(this, function(_state) {
6179
6373
  switch(_state.label){
6180
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
+ }
6181
6387
  durationMs = (_adBreak_durationMs = adBreak.durationMs) !== null && _adBreak_durationMs !== void 0 ? _adBreak_durationMs : 0;
6182
- endMs = adBreak.startTimeMs + durationMs;
6183
- 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 [
6184
6392
  3,
6185
- 2
6393
+ 4
6186
6394
  ];
6187
- remainingMs = endMs - nowMs;
6395
+ this.consumedVmapBreakIds.add(key);
6396
+ remainingMs = durationMs > 0 ? Math.max(0, endMs - nowMs) : 0;
6188
6397
  tags = this.selectVastTagsForBreak(adBreak) || (this.apiVastTagUrl ? [
6189
6398
  this.apiVastTagUrl
6190
6399
  ] : void 0);
6191
6400
  if (!(tags && tags.length > 0)) return [
6192
6401
  3,
6193
- 2
6402
+ 4
6194
6403
  ];
6195
6404
  first = tags[0];
6196
6405
  rest = tags.slice(1);
6197
6406
  this.adPodQueue = rest;
6198
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
+ ]);
6199
6416
  return [
6200
6417
  4,
6201
6418
  this.playSingleAd(first)
6202
6419
  ];
6203
- case 1:
6420
+ case 2:
6204
6421
  _state.sent();
6205
6422
  this.inAdBreak = true;
6206
6423
  this.expectedAdBreakDurationMs = remainingMs;
6207
6424
  this.currentAdBreakStartWallClockMs = Date.now();
6208
6425
  this.scheduleAdStopCountdown(remainingMs);
6209
- _state.label = 2;
6210
- 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:
6211
6441
  return [
6212
6442
  2
6213
6443
  ];
@@ -6913,13 +7143,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6913
7143
  {
6914
7144
  key: "findBreakForTime",
6915
7145
  value: function findBreakForTime(nowMs) {
6916
- 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;
6917
7149
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
6918
7150
  try {
6919
7151
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
6920
7152
  var b = _step.value;
6921
- var end = (b.startTimeMs || 0) + (b.durationMs || 0);
6922
- 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)) {
6923
7162
  return b;
6924
7163
  }
6925
7164
  }