stormcloud-video-player 0.3.62 → 0.3.63

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.
package/lib/index.cjs CHANGED
@@ -3666,7 +3666,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3666
3666
  var _level_details, _level_details1;
3667
3667
  return (level === null || level === void 0 ? void 0 : (_level_details = level.details) === null || _level_details === void 0 ? void 0 : _level_details.live) === true || (level === null || level === void 0 ? void 0 : (_level_details1 = level.details) === null || _level_details1 === void 0 ? void 0 : _level_details1.type) === "LIVE";
3668
3668
  })) !== null && _ref !== void 0 ? _ref : false;
3669
- if (!this.isLiveStream && this.vmapBreaks.length === 0 && this.apiVastTagUrl) {
3669
+ if (!this.isVmapEnabled() && !this.isLiveStream && this.vmapBreaks.length === 0 && this.apiVastTagUrl) {
3670
3670
  prerollKey = "synthetic-vod-preroll";
3671
3671
  if (!this.consumedVmapBreakIds.has(prerollKey)) {
3672
3672
  this.vmapBreaks = [
@@ -4249,6 +4249,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4249
4249
  _this.onTimeUpdate(_this.video.currentTime);
4250
4250
  };
4251
4251
  this.video.addEventListener("timeupdate", this.timeUpdateHandler);
4252
+ this.endedHandler = function() {
4253
+ _this.onVideoEnded();
4254
+ };
4255
+ this.video.addEventListener("ended", this.endedHandler);
4252
4256
  this.emptiedHandler = function() {
4253
4257
  if (_this.nativeHlsMode && _this.videoSrcProtection && !_this.ima.isAdPlaying()) {
4254
4258
  if (_this.config.debugAdTiming) {
@@ -5560,6 +5564,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5560
5564
  }
5561
5565
  }
5562
5566
  },
5567
+ {
5568
+ key: "isVmapEnabled",
5569
+ value: function isVmapEnabled() {
5570
+ var _this_config_vmapUrl;
5571
+ return !!(this.config.isVmap && ((_this_config_vmapUrl = this.config.vmapUrl) === null || _this_config_vmapUrl === void 0 ? void 0 : _this_config_vmapUrl.trim()));
5572
+ }
5573
+ },
5563
5574
  {
5564
5575
  key: "fetchAdConfiguration",
5565
5576
  value: function fetchAdConfiguration() {
@@ -5568,7 +5579,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5568
5579
  return _ts_generator(this, function(_state) {
5569
5580
  switch(_state.label){
5570
5581
  case 0:
5571
- if (!this.config.vmapUrl) return [
5582
+ if (!this.isVmapEnabled()) return [
5572
5583
  3,
5573
5584
  2
5574
5585
  ];
@@ -5578,7 +5589,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5578
5589
  ];
5579
5590
  case 1:
5580
5591
  _state.sent();
5581
- _state.label = 2;
5592
+ if (this.config.debugAdTiming) {
5593
+ console.log("[StormcloudVideoPlayer] VMAP mode enabled");
5594
+ }
5595
+ return [
5596
+ 2
5597
+ ];
5582
5598
  case 2:
5583
5599
  vastMode = this.config.vastMode || "default";
5584
5600
  if (this.config.debugAdTiming) {
@@ -5746,7 +5762,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5746
5762
  }
5747
5763
  return [];
5748
5764
  }
5749
- var adBreakNodes = Array.from(doc.querySelectorAll("AdBreak, vmap\\:AdBreak"));
5765
+ var VMAP_NS = "http://www.iab.net/videosuite/vmap";
5766
+ var adBreakNodes = Array.from(doc.getElementsByTagNameNS(VMAP_NS, "AdBreak"));
5767
+ if (adBreakNodes.length === 0) {
5768
+ adBreakNodes = Array.from(doc.querySelectorAll("AdBreak, vmap\\:AdBreak"));
5769
+ }
5770
+ if (adBreakNodes.length === 0) {
5771
+ adBreakNodes = Array.from(doc.getElementsByTagName("*")).filter(function(el) {
5772
+ return el.localName === "AdBreak";
5773
+ });
5774
+ }
5750
5775
  var parsed = [];
5751
5776
  adBreakNodes.forEach(function(node, index) {
5752
5777
  var timeOffsetRaw = (node.getAttribute("timeOffset") || "").trim();
@@ -5754,8 +5779,17 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5754
5779
  if (startTimeMs == null) {
5755
5780
  return;
5756
5781
  }
5757
- var adTagNode = node.querySelector("AdTagURI, vmap\\:AdTagURI");
5758
- var adTagUrl = ((adTagNode === null || adTagNode === void 0 ? void 0 : adTagNode.textContent) || "").trim();
5782
+ var adTagNode = node.getElementsByTagNameNS(VMAP_NS, "AdTagURI")[0];
5783
+ if (!adTagNode) {
5784
+ var _node_querySelector;
5785
+ adTagNode = (_node_querySelector = node.querySelector("AdTagURI, vmap\\:AdTagURI")) !== null && _node_querySelector !== void 0 ? _node_querySelector : void 0;
5786
+ }
5787
+ if (!adTagNode) {
5788
+ adTagNode = Array.from(node.getElementsByTagName("*")).find(function(el) {
5789
+ return el.localName === "AdTagURI";
5790
+ });
5791
+ }
5792
+ var adTagUrl = _this.resolveVmapAdTagUrl(((adTagNode === null || adTagNode === void 0 ? void 0 : adTagNode.textContent) || "").trim());
5759
5793
  if (!adTagUrl) {
5760
5794
  return;
5761
5795
  }
@@ -5774,6 +5808,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5774
5808
  return parsed;
5775
5809
  }
5776
5810
  },
5811
+ {
5812
+ key: "resolveVmapAdTagUrl",
5813
+ value: function resolveVmapAdTagUrl(url) {
5814
+ if (!url) {
5815
+ return "";
5816
+ }
5817
+ return url.replace(/\[timestamp\]/gi, String(Date.now())).replace(/\$\{GDPR\}/gi, "0").trim();
5818
+ }
5819
+ },
5777
5820
  {
5778
5821
  key: "parseVmapTimeOffsetToMs",
5779
5822
  value: function parseVmapTimeOffsetToMs(timeOffset) {
@@ -5796,6 +5839,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5796
5839
  var millis = Number(ms.padEnd(3, "0").slice(0, 3));
5797
5840
  return (hours * 3600 + minutes * 60 + seconds) * 1e3 + millis;
5798
5841
  }
5842
+ var msOnly = timeOffset.match(/^(\d{1,2}):(\d{2})(?:\.(\d{1,3}))?$/);
5843
+ if (msOnly) {
5844
+ var _msOnly = _sliced_to_array(msOnly, 4), mm1 = _msOnly[1], ss1 = _msOnly[2], tmp1 = _msOnly[3], ms1 = tmp1 === void 0 ? "0" : tmp1;
5845
+ var minutes1 = Number(mm1);
5846
+ var seconds1 = Number(ss1);
5847
+ var millis1 = Number(ms1.padEnd(3, "0").slice(0, 3));
5848
+ return (minutes1 * 60 + seconds1) * 1e3 + millis1;
5849
+ }
5799
5850
  var percent = timeOffset.match(/^(\d+(?:\.\d+)?)%$/);
5800
5851
  if (percent) {
5801
5852
  var ratio = Number(percent[1]) / 100;
@@ -7311,23 +7362,49 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7311
7362
  key: "onTimeUpdate",
7312
7363
  value: function onTimeUpdate(currentTimeSec) {
7313
7364
  var _this = this;
7365
+ if (!this.isVmapEnabled() || this.vmapBreaks.length === 0) {
7366
+ return;
7367
+ }
7314
7368
  if (this.ima.isAdPlaying() || this.inAdBreak) return;
7315
7369
  var nowMs = currentTimeSec * 1e3;
7316
7370
  var breakToPlay = this.findBreakForTime(nowMs);
7317
7371
  if (breakToPlay) {
7318
- void this.handleMidAdJoin(breakToPlay, nowMs).catch(function(error) {
7372
+ void this.handleVmapAdBreak(breakToPlay, nowMs).catch(function(error) {
7373
+ if (_this.config.debugAdTiming) {
7374
+ console.warn("[StormcloudVideoPlayer] VMAP ad break failed gracefully:", error);
7375
+ }
7376
+ });
7377
+ }
7378
+ }
7379
+ },
7380
+ {
7381
+ key: "onVideoEnded",
7382
+ value: function onVideoEnded() {
7383
+ var _this = this;
7384
+ if (!this.isVmapEnabled() || this.vmapBreaks.length === 0) {
7385
+ return;
7386
+ }
7387
+ if (this.ima.isAdPlaying() || this.inAdBreak) {
7388
+ return;
7389
+ }
7390
+ var durationMs = Number.isFinite(this.video.duration) ? Math.floor(this.video.duration * 1e3) : 0;
7391
+ var postroll = this.vmapBreaks.find(function(b) {
7392
+ return b.startTimeMs === -1 && !_this.consumedVmapBreakIds.has(_this.getAdBreakKey(b));
7393
+ });
7394
+ if (postroll) {
7395
+ void this.handleVmapAdBreak(postroll, durationMs).catch(function(error) {
7319
7396
  if (_this.config.debugAdTiming) {
7320
- console.warn("[StormcloudVideoPlayer] Mid-roll VMAP join failed gracefully:", error);
7397
+ console.warn("[StormcloudVideoPlayer] VMAP post-roll failed gracefully:", error);
7321
7398
  }
7322
7399
  });
7323
7400
  }
7324
7401
  }
7325
7402
  },
7326
7403
  {
7327
- key: "handleMidAdJoin",
7328
- value: function handleMidAdJoin(adBreak, nowMs) {
7404
+ key: "handleVmapAdBreak",
7405
+ value: function handleVmapAdBreak(adBreak, nowMs) {
7329
7406
  return _async_to_generator(function() {
7330
- var _adBreak_durationMs, _this_config_driftToleranceMs, key, breakStartMs, durationMs, endMs, tol, inWindow, remainingMs, tags, first, rest, error;
7407
+ var _adBreak_durationMs, key, breakStartMs, durationMs, endMs, inWindow, tags, first, rest, error;
7331
7408
  return _ts_generator(this, function(_state) {
7332
7409
  switch(_state.label){
7333
7410
  case 0:
@@ -7345,25 +7422,28 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7345
7422
  }
7346
7423
  durationMs = (_adBreak_durationMs = adBreak.durationMs) !== null && _adBreak_durationMs !== void 0 ? _adBreak_durationMs : 0;
7347
7424
  endMs = breakStartMs + durationMs;
7348
- tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
7349
- inWindow = durationMs > 0 ? nowMs > breakStartMs && nowMs < endMs : nowMs + tol >= breakStartMs;
7425
+ inWindow = durationMs > 0 ? nowMs >= breakStartMs && nowMs < endMs : nowMs >= breakStartMs;
7350
7426
  if (!inWindow) return [
7351
7427
  3,
7352
7428
  4
7353
7429
  ];
7354
7430
  this.consumedVmapBreakIds.add(key);
7355
- remainingMs = durationMs > 0 ? Math.max(0, endMs - nowMs) : 0;
7356
- tags = this.selectVastTagsForBreak(adBreak) || (this.apiVastTagUrl ? [
7357
- this.apiVastTagUrl
7358
- ] : void 0);
7359
- if (!(tags && tags.length > 0)) return [
7360
- 3,
7361
- 4
7362
- ];
7431
+ tags = this.selectVastTagsForBreak(adBreak);
7432
+ if (!tags || tags.length === 0) {
7433
+ return [
7434
+ 2
7435
+ ];
7436
+ }
7363
7437
  first = tags[0];
7364
7438
  rest = tags.slice(1);
7365
7439
  this.adPodQueue = rest;
7366
7440
  this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
7441
+ this.showAds = true;
7442
+ this.inAdBreak = true;
7443
+ this.currentAdBreakStartWallClockMs = Date.now();
7444
+ if (!this.video.paused) {
7445
+ this.video.pause();
7446
+ }
7367
7447
  _state.label = 1;
7368
7448
  case 1:
7369
7449
  _state.trys.push([
@@ -7378,10 +7458,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7378
7458
  ];
7379
7459
  case 2:
7380
7460
  _state.sent();
7381
- this.inAdBreak = true;
7382
- this.expectedAdBreakDurationMs = remainingMs;
7383
- this.currentAdBreakStartWallClockMs = Date.now();
7384
- this.scheduleAdStopCountdown(remainingMs);
7385
7461
  return [
7386
7462
  3,
7387
7463
  4
@@ -7389,8 +7465,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7389
7465
  case 3:
7390
7466
  error = _state.sent();
7391
7467
  this.adPodQueue = [];
7468
+ this.inAdBreak = false;
7469
+ this.showAds = false;
7392
7470
  if (this.config.debugAdTiming) {
7393
- console.warn("[StormcloudVideoPlayer] Mid-roll VMAP ad request failed:", error);
7471
+ console.warn("[StormcloudVideoPlayer] VMAP ad request failed:", error);
7394
7472
  }
7395
7473
  return [
7396
7474
  3,
@@ -8061,16 +8139,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8061
8139
  {
8062
8140
  key: "selectVastTagsForBreak",
8063
8141
  value: function selectVastTagsForBreak(b) {
8142
+ var _this = this;
8064
8143
  if (!b || !b.vastTagUrl) return void 0;
8065
- if (b.vastTagUrl.includes(",")) {
8066
- return b.vastTagUrl.split(",").map(function(s) {
8067
- return s.trim();
8144
+ var resolvedUrl = this.resolveVmapAdTagUrl(b.vastTagUrl);
8145
+ if (resolvedUrl.includes(",")) {
8146
+ return resolvedUrl.split(",").map(function(s) {
8147
+ return _this.resolveVmapAdTagUrl(s.trim());
8068
8148
  }).filter(function(s) {
8069
8149
  return s.length > 0;
8070
8150
  });
8071
8151
  }
8072
8152
  return [
8073
- b.vastTagUrl
8153
+ resolvedUrl
8074
8154
  ];
8075
8155
  }
8076
8156
  },
@@ -8102,9 +8182,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8102
8182
  {
8103
8183
  key: "findBreakForTime",
8104
8184
  value: function findBreakForTime(nowMs) {
8105
- var _this_config_driftToleranceMs;
8106
8185
  var schedule = this.vmapBreaks;
8107
- var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
8108
8186
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
8109
8187
  try {
8110
8188
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
@@ -8116,9 +8194,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8116
8194
  if (breakStartMs == null) {
8117
8195
  continue;
8118
8196
  }
8119
- var end = breakStartMs + (b.durationMs || 0);
8120
- var effectiveTol = breakStartMs === 0 ? Math.max(tol, 3e4) : tol;
8121
- if (nowMs >= breakStartMs && (b.durationMs ? nowMs < end : nowMs <= breakStartMs + effectiveTol)) {
8197
+ if (b.durationMs) {
8198
+ var end = breakStartMs + b.durationMs;
8199
+ if (nowMs >= breakStartMs && nowMs < end) {
8200
+ return b;
8201
+ }
8202
+ continue;
8203
+ }
8204
+ if (nowMs >= breakStartMs) {
8122
8205
  return b;
8123
8206
  }
8124
8207
  }
@@ -8326,6 +8409,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8326
8409
  this.video.removeEventListener("timeupdate", this.timeUpdateHandler);
8327
8410
  delete this.timeUpdateHandler;
8328
8411
  }
8412
+ if (this.endedHandler) {
8413
+ this.video.removeEventListener("ended", this.endedHandler);
8414
+ delete this.endedHandler;
8415
+ }
8329
8416
  if (this.emptiedHandler) {
8330
8417
  this.video.removeEventListener("emptied", this.emptiedHandler);
8331
8418
  delete this.emptiedHandler;
@@ -8351,6 +8438,7 @@ var CRITICAL_PROPS = [
8351
8438
  "allowNativeHls",
8352
8439
  "isLiveStream",
8353
8440
  "licenseKey",
8441
+ "isVmap",
8354
8442
  "vmapUrl",
8355
8443
  "lowLatencyMode",
8356
8444
  "driftToleranceMs",
@@ -8359,7 +8447,7 @@ var CRITICAL_PROPS = [
8359
8447
  var CONTROLS_HIDE_DELAY = 3e3;
8360
8448
  var DEFAULT_PLAYER_ASPECT_RATIO = 16 / 9;
8361
8449
  var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
8362
- var src = props.src, autoplay = props.autoplay, muted = props.muted, lowLatencyMode = props.lowLatencyMode, allowNativeHls = props.allowNativeHls, isLiveStream = props.isLiveStream, driftToleranceMs = props.driftToleranceMs, immediateManifestAds = props.immediateManifestAds, debugAdTiming = props.debugAdTiming, showCustomControls = props.showCustomControls, hideLoadingIndicator = props.hideLoadingIndicator, onVolumeToggle = props.onVolumeToggle, onFullscreenToggle = props.onFullscreenToggle, onControlClick = props.onControlClick, onReady = props.onReady, wrapperClassName = props.wrapperClassName, wrapperStyle = props.wrapperStyle, className = props.className, style = props.style, controls = props.controls, playsInline = props.playsInline, preload = props.preload, poster = props.poster, children = props.children, licenseKey = props.licenseKey, vastMode = props.vastMode, vastTagUrl = props.vastTagUrl, vmapUrl = props.vmapUrl, adPlayerType = props.adPlayerType, minSegmentsBeforePlay = props.minSegmentsBeforePlay, restVideoAttrs = _object_without_properties(props, [
8450
+ var src = props.src, autoplay = props.autoplay, muted = props.muted, lowLatencyMode = props.lowLatencyMode, allowNativeHls = props.allowNativeHls, isLiveStream = props.isLiveStream, driftToleranceMs = props.driftToleranceMs, immediateManifestAds = props.immediateManifestAds, debugAdTiming = props.debugAdTiming, showCustomControls = props.showCustomControls, hideLoadingIndicator = props.hideLoadingIndicator, onVolumeToggle = props.onVolumeToggle, onFullscreenToggle = props.onFullscreenToggle, onControlClick = props.onControlClick, onReady = props.onReady, wrapperClassName = props.wrapperClassName, wrapperStyle = props.wrapperStyle, className = props.className, style = props.style, controls = props.controls, playsInline = props.playsInline, preload = props.preload, poster = props.poster, children = props.children, licenseKey = props.licenseKey, vastMode = props.vastMode, vastTagUrl = props.vastTagUrl, isVmap = props.isVmap, vmapUrl = props.vmapUrl, adPlayerType = props.adPlayerType, minSegmentsBeforePlay = props.minSegmentsBeforePlay, restVideoAttrs = _object_without_properties(props, [
8363
8451
  "src",
8364
8452
  "autoplay",
8365
8453
  "muted",
@@ -8387,6 +8475,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
8387
8475
  "licenseKey",
8388
8476
  "vastMode",
8389
8477
  "vastTagUrl",
8478
+ "isVmap",
8390
8479
  "vmapUrl",
8391
8480
  "adPlayerType",
8392
8481
  "minSegmentsBeforePlay"
@@ -8530,6 +8619,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
8530
8619
  allowNativeHls,
8531
8620
  isLiveStream,
8532
8621
  licenseKey,
8622
+ isVmap,
8533
8623
  vmapUrl,
8534
8624
  lowLatencyMode,
8535
8625
  driftToleranceMs,
@@ -8574,6 +8664,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
8574
8664
  if (licenseKey !== void 0) cfg.licenseKey = licenseKey;
8575
8665
  if (vastMode !== void 0) cfg.vastMode = vastMode;
8576
8666
  if (vastTagUrl !== void 0) cfg.vastTagUrl = vastTagUrl;
8667
+ if (isVmap !== void 0) cfg.isVmap = isVmap;
8577
8668
  if (vmapUrl !== void 0) cfg.vmapUrl = vmapUrl;
8578
8669
  if (adPlayerType !== void 0) cfg.adPlayerType = adPlayerType;
8579
8670
  if (minSegmentsBeforePlay !== void 0) cfg.minSegmentsBeforePlay = minSegmentsBeforePlay;
@@ -9737,6 +9828,7 @@ var defaultProps = {
9737
9828
  showCustomControls: false,
9738
9829
  hideLoadingIndicator: false,
9739
9830
  licenseKey: "",
9831
+ isVmap: false,
9740
9832
  vmapUrl: "",
9741
9833
  adFailsafeTimeoutMs: 1e4,
9742
9834
  minSegmentsBeforePlay: 2,
@@ -10681,6 +10773,7 @@ var SUPPORTED_PROPS = [
10681
10773
  "debugAdTiming",
10682
10774
  "showCustomControls",
10683
10775
  "licenseKey",
10776
+ "isVmap",
10684
10777
  "vmapUrl",
10685
10778
  "adFailsafeTimeoutMs",
10686
10779
  "minSegmentsBeforePlay",