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.
@@ -3201,6 +3201,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3201
3201
  this.inAdBreak = false;
3202
3202
  this.ptsDriftEmaMs = 0;
3203
3203
  this.adPodQueue = [];
3204
+ this.vmapBreaks = [];
3205
+ this.consumedVmapBreakIds = /* @__PURE__ */ new Set();
3204
3206
  this.lastHeartbeatTime = 0;
3205
3207
  this.currentAdIndex = 0;
3206
3208
  this.totalAdsInBreak = 0;
@@ -4529,6 +4531,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4529
4531
  return _ts_generator(this, function(_state) {
4530
4532
  switch(_state.label){
4531
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:
4532
4546
  vastMode = this.config.vastMode || "default";
4533
4547
  if (this.config.debugAdTiming) {
4534
4548
  console.log("[StormcloudVideoPlayer] VAST mode:", vastMode);
@@ -4574,7 +4588,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4574
4588
  headers: headers
4575
4589
  })
4576
4590
  ];
4577
- case 1:
4591
+ case 3:
4578
4592
  response = _state.sent();
4579
4593
  if (!response.ok) {
4580
4594
  if (this.config.debugAdTiming) {
@@ -4588,7 +4602,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4588
4602
  4,
4589
4603
  response.json()
4590
4604
  ];
4591
- case 2:
4605
+ case 4:
4592
4606
  data = _state.sent();
4593
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;
4594
4608
  if (imaPayload) {
@@ -4616,6 +4630,171 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4616
4630
  }).call(this);
4617
4631
  }
4618
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
+ },
4619
4798
  {
4620
4799
  key: "getCurrentAdIndex",
4621
4800
  value: function getCurrentAdIndex() {
@@ -4706,10 +4885,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4706
4885
  var scheduled = this.findCurrentOrNextBreak(this.video.currentTime * 1e3);
4707
4886
  var tags = this.selectVastTagsForBreak(scheduled);
4708
4887
  var baseVastUrl;
4709
- if (this.apiVastTagUrl) {
4710
- baseVastUrl = this.apiVastTagUrl;
4711
- } else if (tags && tags.length > 0 && tags[0]) {
4888
+ if (tags && tags.length > 0 && tags[0]) {
4712
4889
  baseVastUrl = tags[0];
4890
+ } else if (this.apiVastTagUrl) {
4891
+ baseVastUrl = this.apiVastTagUrl;
4713
4892
  } else {
4714
4893
  if (this.config.debugAdTiming) {
4715
4894
  console.warn("[StormcloudVideoPlayer] No VAST URL available for prefetch");
@@ -5159,11 +5338,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5159
5338
  switch(_state.label){
5160
5339
  case 0:
5161
5340
  scheduled = this.findCurrentOrNextBreak(this.video.currentTime * 1e3);
5341
+ if (scheduled) {
5342
+ this.consumedVmapBreakIds.add(this.getAdBreakKey(scheduled));
5343
+ }
5162
5344
  tags = this.selectVastTagsForBreak(scheduled);
5163
- if (this.apiVastTagUrl) {
5164
- baseVastUrl = this.apiVastTagUrl;
5165
- } else if (tags && tags.length > 0 && tags[0]) {
5345
+ if (tags && tags.length > 0 && tags[0]) {
5166
5346
  baseVastUrl = tags[0];
5347
+ } else if (this.apiVastTagUrl) {
5348
+ baseVastUrl = this.apiVastTagUrl;
5167
5349
  } else {
5168
5350
  this.clearPendingAdBreak();
5169
5351
  return [
@@ -6042,15 +6224,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6042
6224
  {
6043
6225
  key: "findCurrentOrNextBreak",
6044
6226
  value: function findCurrentOrNextBreak(nowMs) {
6045
- var schedule = [];
6227
+ var schedule = this.vmapBreaks;
6046
6228
  var candidate;
6047
6229
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
6048
6230
  try {
6049
6231
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
6050
6232
  var b = _step.value;
6051
- 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
+ }
6052
6241
  var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
6053
- 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))) {
6054
6243
  candidate = b;
6055
6244
  }
6056
6245
  }
@@ -6074,11 +6263,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6074
6263
  {
6075
6264
  key: "onTimeUpdate",
6076
6265
  value: function onTimeUpdate(currentTimeSec) {
6077
- if (this.ima.isAdPlaying()) return;
6266
+ var _this = this;
6267
+ if (this.ima.isAdPlaying() || this.inAdBreak) return;
6078
6268
  var nowMs = currentTimeSec * 1e3;
6079
6269
  var breakToPlay = this.findBreakForTime(nowMs);
6080
6270
  if (breakToPlay) {
6081
- 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
+ });
6082
6276
  }
6083
6277
  }
6084
6278
  },
@@ -6086,40 +6280,76 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6086
6280
  key: "handleMidAdJoin",
6087
6281
  value: function handleMidAdJoin(adBreak, nowMs) {
6088
6282
  return _async_to_generator(function() {
6089
- 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;
6090
6284
  return _ts_generator(this, function(_state) {
6091
6285
  switch(_state.label){
6092
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
+ }
6093
6299
  durationMs = (_adBreak_durationMs = adBreak.durationMs) !== null && _adBreak_durationMs !== void 0 ? _adBreak_durationMs : 0;
6094
- endMs = adBreak.startTimeMs + durationMs;
6095
- 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 [
6096
6304
  3,
6097
- 2
6305
+ 4
6098
6306
  ];
6099
- remainingMs = endMs - nowMs;
6307
+ this.consumedVmapBreakIds.add(key);
6308
+ remainingMs = durationMs > 0 ? Math.max(0, endMs - nowMs) : 0;
6100
6309
  tags = this.selectVastTagsForBreak(adBreak) || (this.apiVastTagUrl ? [
6101
6310
  this.apiVastTagUrl
6102
6311
  ] : void 0);
6103
6312
  if (!(tags && tags.length > 0)) return [
6104
6313
  3,
6105
- 2
6314
+ 4
6106
6315
  ];
6107
6316
  first = tags[0];
6108
6317
  rest = tags.slice(1);
6109
6318
  this.adPodQueue = rest;
6110
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
+ ]);
6111
6328
  return [
6112
6329
  4,
6113
6330
  this.playSingleAd(first)
6114
6331
  ];
6115
- case 1:
6332
+ case 2:
6116
6333
  _state.sent();
6117
6334
  this.inAdBreak = true;
6118
6335
  this.expectedAdBreakDurationMs = remainingMs;
6119
6336
  this.currentAdBreakStartWallClockMs = Date.now();
6120
6337
  this.scheduleAdStopCountdown(remainingMs);
6121
- _state.label = 2;
6122
- 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:
6123
6353
  return [
6124
6354
  2
6125
6355
  ];
@@ -6825,13 +7055,22 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6825
7055
  {
6826
7056
  key: "findBreakForTime",
6827
7057
  value: function findBreakForTime(nowMs) {
6828
- 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;
6829
7061
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
6830
7062
  try {
6831
7063
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
6832
7064
  var b = _step.value;
6833
- var end = (b.startTimeMs || 0) + (b.durationMs || 0);
6834
- 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)) {
6835
7074
  return b;
6836
7075
  }
6837
7076
  }