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.
@@ -1,4 +1,4 @@
1
- import { I as ImaController } from '../types-BJPNhfLV.cjs';
1
+ import { I as ImaController } from '../types-CjI14dPN.cjs';
2
2
  import Hls from 'hls.js';
3
3
 
4
4
  declare function createHlsAdPlayer(contentVideo: HTMLVideoElement, options?: {
package/lib/sdk/ima.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { I as ImaController } from '../types-BJPNhfLV.cjs';
1
+ import { I as ImaController } from '../types-CjI14dPN.cjs';
2
2
 
3
3
  declare global {
4
4
  interface Window {
@@ -20,6 +20,7 @@ interface StormcloudVideoPlayerConfig {
20
20
  licenseKey?: string;
21
21
  adPlayerType?: 'ima' | 'hls';
22
22
  vastTagUrl?: string;
23
+ isVmap?: boolean;
23
24
  vmapUrl?: string;
24
25
  vastMode?: 'adstorm' | 'default';
25
26
  minSegmentsBeforePlay?: number;
@@ -3478,7 +3478,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3478
3478
  var _level_details, _level_details1;
3479
3479
  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";
3480
3480
  })) !== null && _ref !== void 0 ? _ref : false;
3481
- if (!this.isLiveStream && this.vmapBreaks.length === 0 && this.apiVastTagUrl) {
3481
+ if (!this.isVmapEnabled() && !this.isLiveStream && this.vmapBreaks.length === 0 && this.apiVastTagUrl) {
3482
3482
  prerollKey = "synthetic-vod-preroll";
3483
3483
  if (!this.consumedVmapBreakIds.has(prerollKey)) {
3484
3484
  this.vmapBreaks = [
@@ -4061,6 +4061,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4061
4061
  _this.onTimeUpdate(_this.video.currentTime);
4062
4062
  };
4063
4063
  this.video.addEventListener("timeupdate", this.timeUpdateHandler);
4064
+ this.endedHandler = function() {
4065
+ _this.onVideoEnded();
4066
+ };
4067
+ this.video.addEventListener("ended", this.endedHandler);
4064
4068
  this.emptiedHandler = function() {
4065
4069
  if (_this.nativeHlsMode && _this.videoSrcProtection && !_this.ima.isAdPlaying()) {
4066
4070
  if (_this.config.debugAdTiming) {
@@ -5372,6 +5376,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5372
5376
  }
5373
5377
  }
5374
5378
  },
5379
+ {
5380
+ key: "isVmapEnabled",
5381
+ value: function isVmapEnabled() {
5382
+ var _this_config_vmapUrl;
5383
+ return !!(this.config.isVmap && ((_this_config_vmapUrl = this.config.vmapUrl) === null || _this_config_vmapUrl === void 0 ? void 0 : _this_config_vmapUrl.trim()));
5384
+ }
5385
+ },
5375
5386
  {
5376
5387
  key: "fetchAdConfiguration",
5377
5388
  value: function fetchAdConfiguration() {
@@ -5380,7 +5391,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5380
5391
  return _ts_generator(this, function(_state) {
5381
5392
  switch(_state.label){
5382
5393
  case 0:
5383
- if (!this.config.vmapUrl) return [
5394
+ if (!this.isVmapEnabled()) return [
5384
5395
  3,
5385
5396
  2
5386
5397
  ];
@@ -5390,7 +5401,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5390
5401
  ];
5391
5402
  case 1:
5392
5403
  _state.sent();
5393
- _state.label = 2;
5404
+ if (this.config.debugAdTiming) {
5405
+ console.log("[StormcloudVideoPlayer] VMAP mode enabled");
5406
+ }
5407
+ return [
5408
+ 2
5409
+ ];
5394
5410
  case 2:
5395
5411
  vastMode = this.config.vastMode || "default";
5396
5412
  if (this.config.debugAdTiming) {
@@ -5558,7 +5574,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5558
5574
  }
5559
5575
  return [];
5560
5576
  }
5561
- var adBreakNodes = Array.from(doc.querySelectorAll("AdBreak, vmap\\:AdBreak"));
5577
+ var VMAP_NS = "http://www.iab.net/videosuite/vmap";
5578
+ var adBreakNodes = Array.from(doc.getElementsByTagNameNS(VMAP_NS, "AdBreak"));
5579
+ if (adBreakNodes.length === 0) {
5580
+ adBreakNodes = Array.from(doc.querySelectorAll("AdBreak, vmap\\:AdBreak"));
5581
+ }
5582
+ if (adBreakNodes.length === 0) {
5583
+ adBreakNodes = Array.from(doc.getElementsByTagName("*")).filter(function(el) {
5584
+ return el.localName === "AdBreak";
5585
+ });
5586
+ }
5562
5587
  var parsed = [];
5563
5588
  adBreakNodes.forEach(function(node, index) {
5564
5589
  var timeOffsetRaw = (node.getAttribute("timeOffset") || "").trim();
@@ -5566,8 +5591,17 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5566
5591
  if (startTimeMs == null) {
5567
5592
  return;
5568
5593
  }
5569
- var adTagNode = node.querySelector("AdTagURI, vmap\\:AdTagURI");
5570
- var adTagUrl = ((adTagNode === null || adTagNode === void 0 ? void 0 : adTagNode.textContent) || "").trim();
5594
+ var adTagNode = node.getElementsByTagNameNS(VMAP_NS, "AdTagURI")[0];
5595
+ if (!adTagNode) {
5596
+ var _node_querySelector;
5597
+ adTagNode = (_node_querySelector = node.querySelector("AdTagURI, vmap\\:AdTagURI")) !== null && _node_querySelector !== void 0 ? _node_querySelector : void 0;
5598
+ }
5599
+ if (!adTagNode) {
5600
+ adTagNode = Array.from(node.getElementsByTagName("*")).find(function(el) {
5601
+ return el.localName === "AdTagURI";
5602
+ });
5603
+ }
5604
+ var adTagUrl = _this.resolveVmapAdTagUrl(((adTagNode === null || adTagNode === void 0 ? void 0 : adTagNode.textContent) || "").trim());
5571
5605
  if (!adTagUrl) {
5572
5606
  return;
5573
5607
  }
@@ -5586,6 +5620,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5586
5620
  return parsed;
5587
5621
  }
5588
5622
  },
5623
+ {
5624
+ key: "resolveVmapAdTagUrl",
5625
+ value: function resolveVmapAdTagUrl(url) {
5626
+ if (!url) {
5627
+ return "";
5628
+ }
5629
+ return url.replace(/\[timestamp\]/gi, String(Date.now())).replace(/\$\{GDPR\}/gi, "0").trim();
5630
+ }
5631
+ },
5589
5632
  {
5590
5633
  key: "parseVmapTimeOffsetToMs",
5591
5634
  value: function parseVmapTimeOffsetToMs(timeOffset) {
@@ -5608,6 +5651,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5608
5651
  var millis = Number(ms.padEnd(3, "0").slice(0, 3));
5609
5652
  return (hours * 3600 + minutes * 60 + seconds) * 1e3 + millis;
5610
5653
  }
5654
+ var msOnly = timeOffset.match(/^(\d{1,2}):(\d{2})(?:\.(\d{1,3}))?$/);
5655
+ if (msOnly) {
5656
+ var _msOnly = _sliced_to_array(msOnly, 4), mm1 = _msOnly[1], ss1 = _msOnly[2], tmp1 = _msOnly[3], ms1 = tmp1 === void 0 ? "0" : tmp1;
5657
+ var minutes1 = Number(mm1);
5658
+ var seconds1 = Number(ss1);
5659
+ var millis1 = Number(ms1.padEnd(3, "0").slice(0, 3));
5660
+ return (minutes1 * 60 + seconds1) * 1e3 + millis1;
5661
+ }
5611
5662
  var percent = timeOffset.match(/^(\d+(?:\.\d+)?)%$/);
5612
5663
  if (percent) {
5613
5664
  var ratio = Number(percent[1]) / 100;
@@ -7123,23 +7174,49 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7123
7174
  key: "onTimeUpdate",
7124
7175
  value: function onTimeUpdate(currentTimeSec) {
7125
7176
  var _this = this;
7177
+ if (!this.isVmapEnabled() || this.vmapBreaks.length === 0) {
7178
+ return;
7179
+ }
7126
7180
  if (this.ima.isAdPlaying() || this.inAdBreak) return;
7127
7181
  var nowMs = currentTimeSec * 1e3;
7128
7182
  var breakToPlay = this.findBreakForTime(nowMs);
7129
7183
  if (breakToPlay) {
7130
- void this.handleMidAdJoin(breakToPlay, nowMs).catch(function(error) {
7184
+ void this.handleVmapAdBreak(breakToPlay, nowMs).catch(function(error) {
7185
+ if (_this.config.debugAdTiming) {
7186
+ console.warn("[StormcloudVideoPlayer] VMAP ad break failed gracefully:", error);
7187
+ }
7188
+ });
7189
+ }
7190
+ }
7191
+ },
7192
+ {
7193
+ key: "onVideoEnded",
7194
+ value: function onVideoEnded() {
7195
+ var _this = this;
7196
+ if (!this.isVmapEnabled() || this.vmapBreaks.length === 0) {
7197
+ return;
7198
+ }
7199
+ if (this.ima.isAdPlaying() || this.inAdBreak) {
7200
+ return;
7201
+ }
7202
+ var durationMs = Number.isFinite(this.video.duration) ? Math.floor(this.video.duration * 1e3) : 0;
7203
+ var postroll = this.vmapBreaks.find(function(b) {
7204
+ return b.startTimeMs === -1 && !_this.consumedVmapBreakIds.has(_this.getAdBreakKey(b));
7205
+ });
7206
+ if (postroll) {
7207
+ void this.handleVmapAdBreak(postroll, durationMs).catch(function(error) {
7131
7208
  if (_this.config.debugAdTiming) {
7132
- console.warn("[StormcloudVideoPlayer] Mid-roll VMAP join failed gracefully:", error);
7209
+ console.warn("[StormcloudVideoPlayer] VMAP post-roll failed gracefully:", error);
7133
7210
  }
7134
7211
  });
7135
7212
  }
7136
7213
  }
7137
7214
  },
7138
7215
  {
7139
- key: "handleMidAdJoin",
7140
- value: function handleMidAdJoin(adBreak, nowMs) {
7216
+ key: "handleVmapAdBreak",
7217
+ value: function handleVmapAdBreak(adBreak, nowMs) {
7141
7218
  return _async_to_generator(function() {
7142
- var _adBreak_durationMs, _this_config_driftToleranceMs, key, breakStartMs, durationMs, endMs, tol, inWindow, remainingMs, tags, first, rest, error;
7219
+ var _adBreak_durationMs, key, breakStartMs, durationMs, endMs, inWindow, tags, first, rest, error;
7143
7220
  return _ts_generator(this, function(_state) {
7144
7221
  switch(_state.label){
7145
7222
  case 0:
@@ -7157,25 +7234,28 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7157
7234
  }
7158
7235
  durationMs = (_adBreak_durationMs = adBreak.durationMs) !== null && _adBreak_durationMs !== void 0 ? _adBreak_durationMs : 0;
7159
7236
  endMs = breakStartMs + durationMs;
7160
- tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
7161
- inWindow = durationMs > 0 ? nowMs > breakStartMs && nowMs < endMs : nowMs + tol >= breakStartMs;
7237
+ inWindow = durationMs > 0 ? nowMs >= breakStartMs && nowMs < endMs : nowMs >= breakStartMs;
7162
7238
  if (!inWindow) return [
7163
7239
  3,
7164
7240
  4
7165
7241
  ];
7166
7242
  this.consumedVmapBreakIds.add(key);
7167
- remainingMs = durationMs > 0 ? Math.max(0, endMs - nowMs) : 0;
7168
- tags = this.selectVastTagsForBreak(adBreak) || (this.apiVastTagUrl ? [
7169
- this.apiVastTagUrl
7170
- ] : void 0);
7171
- if (!(tags && tags.length > 0)) return [
7172
- 3,
7173
- 4
7174
- ];
7243
+ tags = this.selectVastTagsForBreak(adBreak);
7244
+ if (!tags || tags.length === 0) {
7245
+ return [
7246
+ 2
7247
+ ];
7248
+ }
7175
7249
  first = tags[0];
7176
7250
  rest = tags.slice(1);
7177
7251
  this.adPodQueue = rest;
7178
7252
  this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
7253
+ this.showAds = true;
7254
+ this.inAdBreak = true;
7255
+ this.currentAdBreakStartWallClockMs = Date.now();
7256
+ if (!this.video.paused) {
7257
+ this.video.pause();
7258
+ }
7179
7259
  _state.label = 1;
7180
7260
  case 1:
7181
7261
  _state.trys.push([
@@ -7190,10 +7270,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7190
7270
  ];
7191
7271
  case 2:
7192
7272
  _state.sent();
7193
- this.inAdBreak = true;
7194
- this.expectedAdBreakDurationMs = remainingMs;
7195
- this.currentAdBreakStartWallClockMs = Date.now();
7196
- this.scheduleAdStopCountdown(remainingMs);
7197
7273
  return [
7198
7274
  3,
7199
7275
  4
@@ -7201,8 +7277,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7201
7277
  case 3:
7202
7278
  error = _state.sent();
7203
7279
  this.adPodQueue = [];
7280
+ this.inAdBreak = false;
7281
+ this.showAds = false;
7204
7282
  if (this.config.debugAdTiming) {
7205
- console.warn("[StormcloudVideoPlayer] Mid-roll VMAP ad request failed:", error);
7283
+ console.warn("[StormcloudVideoPlayer] VMAP ad request failed:", error);
7206
7284
  }
7207
7285
  return [
7208
7286
  3,
@@ -7873,16 +7951,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7873
7951
  {
7874
7952
  key: "selectVastTagsForBreak",
7875
7953
  value: function selectVastTagsForBreak(b) {
7954
+ var _this = this;
7876
7955
  if (!b || !b.vastTagUrl) return void 0;
7877
- if (b.vastTagUrl.includes(",")) {
7878
- return b.vastTagUrl.split(",").map(function(s) {
7879
- return s.trim();
7956
+ var resolvedUrl = this.resolveVmapAdTagUrl(b.vastTagUrl);
7957
+ if (resolvedUrl.includes(",")) {
7958
+ return resolvedUrl.split(",").map(function(s) {
7959
+ return _this.resolveVmapAdTagUrl(s.trim());
7880
7960
  }).filter(function(s) {
7881
7961
  return s.length > 0;
7882
7962
  });
7883
7963
  }
7884
7964
  return [
7885
- b.vastTagUrl
7965
+ resolvedUrl
7886
7966
  ];
7887
7967
  }
7888
7968
  },
@@ -7914,9 +7994,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7914
7994
  {
7915
7995
  key: "findBreakForTime",
7916
7996
  value: function findBreakForTime(nowMs) {
7917
- var _this_config_driftToleranceMs;
7918
7997
  var schedule = this.vmapBreaks;
7919
- var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
7920
7998
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
7921
7999
  try {
7922
8000
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
@@ -7928,9 +8006,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7928
8006
  if (breakStartMs == null) {
7929
8007
  continue;
7930
8008
  }
7931
- var end = breakStartMs + (b.durationMs || 0);
7932
- var effectiveTol = breakStartMs === 0 ? Math.max(tol, 3e4) : tol;
7933
- if (nowMs >= breakStartMs && (b.durationMs ? nowMs < end : nowMs <= breakStartMs + effectiveTol)) {
8009
+ if (b.durationMs) {
8010
+ var end = breakStartMs + b.durationMs;
8011
+ if (nowMs >= breakStartMs && nowMs < end) {
8012
+ return b;
8013
+ }
8014
+ continue;
8015
+ }
8016
+ if (nowMs >= breakStartMs) {
7934
8017
  return b;
7935
8018
  }
7936
8019
  }
@@ -8138,6 +8221,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8138
8221
  this.video.removeEventListener("timeupdate", this.timeUpdateHandler);
8139
8222
  delete this.timeUpdateHandler;
8140
8223
  }
8224
+ if (this.endedHandler) {
8225
+ this.video.removeEventListener("ended", this.endedHandler);
8226
+ delete this.endedHandler;
8227
+ }
8141
8228
  if (this.emptiedHandler) {
8142
8229
  this.video.removeEventListener("emptied", this.emptiedHandler);
8143
8230
  delete this.emptiedHandler;
@@ -8163,6 +8250,7 @@ var CRITICAL_PROPS = [
8163
8250
  "allowNativeHls",
8164
8251
  "isLiveStream",
8165
8252
  "licenseKey",
8253
+ "isVmap",
8166
8254
  "vmapUrl",
8167
8255
  "lowLatencyMode",
8168
8256
  "driftToleranceMs",
@@ -8171,7 +8259,7 @@ var CRITICAL_PROPS = [
8171
8259
  var CONTROLS_HIDE_DELAY = 3e3;
8172
8260
  var DEFAULT_PLAYER_ASPECT_RATIO = 16 / 9;
8173
8261
  var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
8174
- 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, [
8262
+ 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, [
8175
8263
  "src",
8176
8264
  "autoplay",
8177
8265
  "muted",
@@ -8199,6 +8287,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
8199
8287
  "licenseKey",
8200
8288
  "vastMode",
8201
8289
  "vastTagUrl",
8290
+ "isVmap",
8202
8291
  "vmapUrl",
8203
8292
  "adPlayerType",
8204
8293
  "minSegmentsBeforePlay"
@@ -8342,6 +8431,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
8342
8431
  allowNativeHls,
8343
8432
  isLiveStream,
8344
8433
  licenseKey,
8434
+ isVmap,
8345
8435
  vmapUrl,
8346
8436
  lowLatencyMode,
8347
8437
  driftToleranceMs,
@@ -8386,6 +8476,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
8386
8476
  if (licenseKey !== void 0) cfg.licenseKey = licenseKey;
8387
8477
  if (vastMode !== void 0) cfg.vastMode = vastMode;
8388
8478
  if (vastTagUrl !== void 0) cfg.vastTagUrl = vastTagUrl;
8479
+ if (isVmap !== void 0) cfg.isVmap = isVmap;
8389
8480
  if (vmapUrl !== void 0) cfg.vmapUrl = vmapUrl;
8390
8481
  if (adPlayerType !== void 0) cfg.adPlayerType = adPlayerType;
8391
8482
  if (minSegmentsBeforePlay !== void 0) cfg.minSegmentsBeforePlay = minSegmentsBeforePlay;