stormcloud-video-player 0.3.61 → 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 { S as StormcloudVideoPlayerConfig } from '../types-XKUJJhlG.cjs';
1
+ import { S as StormcloudVideoPlayerConfig } from '../types-CjI14dPN.cjs';
2
2
 
3
3
  declare class StormcloudVideoPlayer {
4
4
  private readonly video;
@@ -42,6 +42,7 @@ declare class StormcloudVideoPlayer {
42
42
  private maxPlaceholderDurationMs;
43
43
  private isShowingPlaceholder;
44
44
  private timeUpdateHandler?;
45
+ private endedHandler?;
45
46
  private emptiedHandler?;
46
47
  private readonly tsScte35Pids;
47
48
  private readonly pmtPids;
@@ -128,9 +129,11 @@ declare class StormcloudVideoPlayer {
128
129
  private markerTypeFromSegmentationTypeId;
129
130
  private initializeTracking;
130
131
  private sendHeartbeatIfNeeded;
132
+ private isVmapEnabled;
131
133
  private fetchAdConfiguration;
132
134
  private fetchAndParseVmap;
133
135
  private parseVmapToBreaks;
136
+ private resolveVmapAdTagUrl;
134
137
  private parseVmapTimeOffsetToMs;
135
138
  private getAdBreakKey;
136
139
  private resolveBreakStartMs;
@@ -160,7 +163,8 @@ declare class StormcloudVideoPlayer {
160
163
  private showPlaceholderAndWaitForAds;
161
164
  private findCurrentOrNextBreak;
162
165
  private onTimeUpdate;
163
- private handleMidAdJoin;
166
+ private onVideoEnded;
167
+ private handleVmapAdBreak;
164
168
  private scheduleAdStopCountdown;
165
169
  private clearAdStopTimer;
166
170
  private ensureAdStoppedByTimer;
@@ -3389,7 +3389,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3389
3389
  key: "load",
3390
3390
  value: function load() {
3391
3391
  return _async_to_generator(function() {
3392
- var _this, error, _this_config_lowLatencyMode, _this_video_play;
3392
+ var _this, error, _this_config_isLiveStream, _this_config_lowLatencyMode, _this_video_play;
3393
3393
  return _ts_generator(this, function(_state) {
3394
3394
  switch(_state.label){
3395
3395
  case 0:
@@ -3433,7 +3433,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3433
3433
  this.nativeHlsMode = true;
3434
3434
  this.videoSrcProtection = this.config.src;
3435
3435
  this.video.src = this.config.src;
3436
- this.isLiveStream = (_this_config_lowLatencyMode = this.config.lowLatencyMode) !== null && _this_config_lowLatencyMode !== void 0 ? _this_config_lowLatencyMode : false;
3436
+ this.isLiveStream = (_this_config_isLiveStream = this.config.isLiveStream) !== null && _this_config_isLiveStream !== void 0 ? _this_config_isLiveStream : (_this_config_lowLatencyMode = this.config.lowLatencyMode) !== null && _this_config_lowLatencyMode !== void 0 ? _this_config_lowLatencyMode : false;
3437
3437
  if (this.config.debugAdTiming) {
3438
3438
  console.log("[StormcloudVideoPlayer] Using native HLS playback - VOD mode:", {
3439
3439
  isLive: this.isLiveStream,
@@ -3484,19 +3484,28 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3484
3484
  });
3485
3485
  this.hls.on(import_hls2.default.Events.MANIFEST_PARSED, function(_, data) {
3486
3486
  return _async_to_generator(function() {
3487
- var _this_config_minSegmentsBeforePlay, _ref, _this_hls_levels, _this_hls, adBehavior, minSegments, _this_video_play;
3487
+ var _this_config_isLiveStream, _ref, _this_config_minSegmentsBeforePlay, _this_hls_levels, _this_hls, prerollKey, adBehavior, minSegments, _this_video_play;
3488
3488
  return _ts_generator(this, function(_state) {
3489
3489
  switch(_state.label){
3490
3490
  case 0:
3491
- if (this.config.allowNativeHls === false) {
3492
- this.isLiveStream = true;
3493
- } else {
3494
- ;
3495
- ;
3496
- this.isLiveStream = (_ref = (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : (_this_hls_levels = _this_hls.levels) === null || _this_hls_levels === void 0 ? void 0 : _this_hls_levels.some(function(level) {
3497
- var _level_details, _level_details1;
3498
- 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";
3499
- })) !== null && _ref !== void 0 ? _ref : false;
3491
+ this.isLiveStream = (_this_config_isLiveStream = this.config.isLiveStream) !== null && _this_config_isLiveStream !== void 0 ? _this_config_isLiveStream : (_ref = (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : (_this_hls_levels = _this_hls.levels) === null || _this_hls_levels === void 0 ? void 0 : _this_hls_levels.some(function(level) {
3492
+ var _level_details, _level_details1;
3493
+ 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";
3494
+ })) !== null && _ref !== void 0 ? _ref : false;
3495
+ if (!this.isVmapEnabled() && !this.isLiveStream && this.vmapBreaks.length === 0 && this.apiVastTagUrl) {
3496
+ prerollKey = "synthetic-vod-preroll";
3497
+ if (!this.consumedVmapBreakIds.has(prerollKey)) {
3498
+ this.vmapBreaks = [
3499
+ {
3500
+ id: prerollKey,
3501
+ startTimeMs: 0,
3502
+ vastTagUrl: this.apiVastTagUrl
3503
+ }
3504
+ ];
3505
+ if (this.config.debugAdTiming) {
3506
+ console.log("[StormcloudVideoPlayer] Injected synthetic VOD preroll from apiVastTagUrl");
3507
+ }
3508
+ }
3500
3509
  }
3501
3510
  if (this.config.debugAdTiming) {
3502
3511
  adBehavior = this.shouldContinueLiveStreamDuringAds() ? "live (main video continues muted during ads)" : "vod (main video pauses during ads)";
@@ -4066,6 +4075,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4066
4075
  _this.onTimeUpdate(_this.video.currentTime);
4067
4076
  };
4068
4077
  this.video.addEventListener("timeupdate", this.timeUpdateHandler);
4078
+ this.endedHandler = function() {
4079
+ _this.onVideoEnded();
4080
+ };
4081
+ this.video.addEventListener("ended", this.endedHandler);
4069
4082
  this.emptiedHandler = function() {
4070
4083
  if (_this.nativeHlsMode && _this.videoSrcProtection && !_this.ima.isAdPlaying()) {
4071
4084
  if (_this.config.debugAdTiming) {
@@ -5377,6 +5390,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5377
5390
  }
5378
5391
  }
5379
5392
  },
5393
+ {
5394
+ key: "isVmapEnabled",
5395
+ value: function isVmapEnabled() {
5396
+ var _this_config_vmapUrl;
5397
+ return !!(this.config.isVmap && ((_this_config_vmapUrl = this.config.vmapUrl) === null || _this_config_vmapUrl === void 0 ? void 0 : _this_config_vmapUrl.trim()));
5398
+ }
5399
+ },
5380
5400
  {
5381
5401
  key: "fetchAdConfiguration",
5382
5402
  value: function fetchAdConfiguration() {
@@ -5385,7 +5405,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5385
5405
  return _ts_generator(this, function(_state) {
5386
5406
  switch(_state.label){
5387
5407
  case 0:
5388
- if (!this.config.vmapUrl) return [
5408
+ if (!this.isVmapEnabled()) return [
5389
5409
  3,
5390
5410
  2
5391
5411
  ];
@@ -5395,7 +5415,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5395
5415
  ];
5396
5416
  case 1:
5397
5417
  _state.sent();
5398
- _state.label = 2;
5418
+ if (this.config.debugAdTiming) {
5419
+ console.log("[StormcloudVideoPlayer] VMAP mode enabled");
5420
+ }
5421
+ return [
5422
+ 2
5423
+ ];
5399
5424
  case 2:
5400
5425
  vastMode = this.config.vastMode || "default";
5401
5426
  if (this.config.debugAdTiming) {
@@ -5563,7 +5588,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5563
5588
  }
5564
5589
  return [];
5565
5590
  }
5566
- var adBreakNodes = Array.from(doc.querySelectorAll("AdBreak, vmap\\:AdBreak"));
5591
+ var VMAP_NS = "http://www.iab.net/videosuite/vmap";
5592
+ var adBreakNodes = Array.from(doc.getElementsByTagNameNS(VMAP_NS, "AdBreak"));
5593
+ if (adBreakNodes.length === 0) {
5594
+ adBreakNodes = Array.from(doc.querySelectorAll("AdBreak, vmap\\:AdBreak"));
5595
+ }
5596
+ if (adBreakNodes.length === 0) {
5597
+ adBreakNodes = Array.from(doc.getElementsByTagName("*")).filter(function(el) {
5598
+ return el.localName === "AdBreak";
5599
+ });
5600
+ }
5567
5601
  var parsed = [];
5568
5602
  adBreakNodes.forEach(function(node, index) {
5569
5603
  var timeOffsetRaw = (node.getAttribute("timeOffset") || "").trim();
@@ -5571,8 +5605,17 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5571
5605
  if (startTimeMs == null) {
5572
5606
  return;
5573
5607
  }
5574
- var adTagNode = node.querySelector("AdTagURI, vmap\\:AdTagURI");
5575
- var adTagUrl = ((adTagNode === null || adTagNode === void 0 ? void 0 : adTagNode.textContent) || "").trim();
5608
+ var adTagNode = node.getElementsByTagNameNS(VMAP_NS, "AdTagURI")[0];
5609
+ if (!adTagNode) {
5610
+ var _node_querySelector;
5611
+ adTagNode = (_node_querySelector = node.querySelector("AdTagURI, vmap\\:AdTagURI")) !== null && _node_querySelector !== void 0 ? _node_querySelector : void 0;
5612
+ }
5613
+ if (!adTagNode) {
5614
+ adTagNode = Array.from(node.getElementsByTagName("*")).find(function(el) {
5615
+ return el.localName === "AdTagURI";
5616
+ });
5617
+ }
5618
+ var adTagUrl = _this.resolveVmapAdTagUrl(((adTagNode === null || adTagNode === void 0 ? void 0 : adTagNode.textContent) || "").trim());
5576
5619
  if (!adTagUrl) {
5577
5620
  return;
5578
5621
  }
@@ -5591,6 +5634,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5591
5634
  return parsed;
5592
5635
  }
5593
5636
  },
5637
+ {
5638
+ key: "resolveVmapAdTagUrl",
5639
+ value: function resolveVmapAdTagUrl(url) {
5640
+ if (!url) {
5641
+ return "";
5642
+ }
5643
+ return url.replace(/\[timestamp\]/gi, String(Date.now())).replace(/\$\{GDPR\}/gi, "0").trim();
5644
+ }
5645
+ },
5594
5646
  {
5595
5647
  key: "parseVmapTimeOffsetToMs",
5596
5648
  value: function parseVmapTimeOffsetToMs(timeOffset) {
@@ -5613,6 +5665,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5613
5665
  var millis = Number(ms.padEnd(3, "0").slice(0, 3));
5614
5666
  return (hours * 3600 + minutes * 60 + seconds) * 1e3 + millis;
5615
5667
  }
5668
+ var msOnly = timeOffset.match(/^(\d{1,2}):(\d{2})(?:\.(\d{1,3}))?$/);
5669
+ if (msOnly) {
5670
+ var _msOnly = _sliced_to_array(msOnly, 4), mm1 = _msOnly[1], ss1 = _msOnly[2], tmp1 = _msOnly[3], ms1 = tmp1 === void 0 ? "0" : tmp1;
5671
+ var minutes1 = Number(mm1);
5672
+ var seconds1 = Number(ss1);
5673
+ var millis1 = Number(ms1.padEnd(3, "0").slice(0, 3));
5674
+ return (minutes1 * 60 + seconds1) * 1e3 + millis1;
5675
+ }
5616
5676
  var percent = timeOffset.match(/^(\d+(?:\.\d+)?)%$/);
5617
5677
  if (percent) {
5618
5678
  var ratio = Number(percent[1]) / 100;
@@ -7128,23 +7188,49 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7128
7188
  key: "onTimeUpdate",
7129
7189
  value: function onTimeUpdate(currentTimeSec) {
7130
7190
  var _this = this;
7191
+ if (!this.isVmapEnabled() || this.vmapBreaks.length === 0) {
7192
+ return;
7193
+ }
7131
7194
  if (this.ima.isAdPlaying() || this.inAdBreak) return;
7132
7195
  var nowMs = currentTimeSec * 1e3;
7133
7196
  var breakToPlay = this.findBreakForTime(nowMs);
7134
7197
  if (breakToPlay) {
7135
- void this.handleMidAdJoin(breakToPlay, nowMs).catch(function(error) {
7198
+ void this.handleVmapAdBreak(breakToPlay, nowMs).catch(function(error) {
7136
7199
  if (_this.config.debugAdTiming) {
7137
- console.warn("[StormcloudVideoPlayer] Mid-roll VMAP join failed gracefully:", error);
7200
+ console.warn("[StormcloudVideoPlayer] VMAP ad break failed gracefully:", error);
7138
7201
  }
7139
7202
  });
7140
7203
  }
7141
7204
  }
7142
7205
  },
7143
7206
  {
7144
- key: "handleMidAdJoin",
7145
- value: function handleMidAdJoin(adBreak, nowMs) {
7207
+ key: "onVideoEnded",
7208
+ value: function onVideoEnded() {
7209
+ var _this = this;
7210
+ if (!this.isVmapEnabled() || this.vmapBreaks.length === 0) {
7211
+ return;
7212
+ }
7213
+ if (this.ima.isAdPlaying() || this.inAdBreak) {
7214
+ return;
7215
+ }
7216
+ var durationMs = Number.isFinite(this.video.duration) ? Math.floor(this.video.duration * 1e3) : 0;
7217
+ var postroll = this.vmapBreaks.find(function(b) {
7218
+ return b.startTimeMs === -1 && !_this.consumedVmapBreakIds.has(_this.getAdBreakKey(b));
7219
+ });
7220
+ if (postroll) {
7221
+ void this.handleVmapAdBreak(postroll, durationMs).catch(function(error) {
7222
+ if (_this.config.debugAdTiming) {
7223
+ console.warn("[StormcloudVideoPlayer] VMAP post-roll failed gracefully:", error);
7224
+ }
7225
+ });
7226
+ }
7227
+ }
7228
+ },
7229
+ {
7230
+ key: "handleVmapAdBreak",
7231
+ value: function handleVmapAdBreak(adBreak, nowMs) {
7146
7232
  return _async_to_generator(function() {
7147
- var _adBreak_durationMs, _this_config_driftToleranceMs, key, breakStartMs, durationMs, endMs, tol, inWindow, remainingMs, tags, first, rest, error;
7233
+ var _adBreak_durationMs, key, breakStartMs, durationMs, endMs, inWindow, tags, first, rest, error;
7148
7234
  return _ts_generator(this, function(_state) {
7149
7235
  switch(_state.label){
7150
7236
  case 0:
@@ -7162,25 +7248,28 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7162
7248
  }
7163
7249
  durationMs = (_adBreak_durationMs = adBreak.durationMs) !== null && _adBreak_durationMs !== void 0 ? _adBreak_durationMs : 0;
7164
7250
  endMs = breakStartMs + durationMs;
7165
- tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
7166
- inWindow = durationMs > 0 ? nowMs > breakStartMs && nowMs < endMs : nowMs + tol >= breakStartMs;
7251
+ inWindow = durationMs > 0 ? nowMs >= breakStartMs && nowMs < endMs : nowMs >= breakStartMs;
7167
7252
  if (!inWindow) return [
7168
7253
  3,
7169
7254
  4
7170
7255
  ];
7171
7256
  this.consumedVmapBreakIds.add(key);
7172
- remainingMs = durationMs > 0 ? Math.max(0, endMs - nowMs) : 0;
7173
- tags = this.selectVastTagsForBreak(adBreak) || (this.apiVastTagUrl ? [
7174
- this.apiVastTagUrl
7175
- ] : void 0);
7176
- if (!(tags && tags.length > 0)) return [
7177
- 3,
7178
- 4
7179
- ];
7257
+ tags = this.selectVastTagsForBreak(adBreak);
7258
+ if (!tags || tags.length === 0) {
7259
+ return [
7260
+ 2
7261
+ ];
7262
+ }
7180
7263
  first = tags[0];
7181
7264
  rest = tags.slice(1);
7182
7265
  this.adPodQueue = rest;
7183
7266
  this.ima.updateOriginalMutedState(this.video.muted, this.video.volume);
7267
+ this.showAds = true;
7268
+ this.inAdBreak = true;
7269
+ this.currentAdBreakStartWallClockMs = Date.now();
7270
+ if (!this.video.paused) {
7271
+ this.video.pause();
7272
+ }
7184
7273
  _state.label = 1;
7185
7274
  case 1:
7186
7275
  _state.trys.push([
@@ -7195,10 +7284,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7195
7284
  ];
7196
7285
  case 2:
7197
7286
  _state.sent();
7198
- this.inAdBreak = true;
7199
- this.expectedAdBreakDurationMs = remainingMs;
7200
- this.currentAdBreakStartWallClockMs = Date.now();
7201
- this.scheduleAdStopCountdown(remainingMs);
7202
7287
  return [
7203
7288
  3,
7204
7289
  4
@@ -7206,8 +7291,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7206
7291
  case 3:
7207
7292
  error = _state.sent();
7208
7293
  this.adPodQueue = [];
7294
+ this.inAdBreak = false;
7295
+ this.showAds = false;
7209
7296
  if (this.config.debugAdTiming) {
7210
- console.warn("[StormcloudVideoPlayer] Mid-roll VMAP ad request failed:", error);
7297
+ console.warn("[StormcloudVideoPlayer] VMAP ad request failed:", error);
7211
7298
  }
7212
7299
  return [
7213
7300
  3,
@@ -7878,16 +7965,18 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7878
7965
  {
7879
7966
  key: "selectVastTagsForBreak",
7880
7967
  value: function selectVastTagsForBreak(b) {
7968
+ var _this = this;
7881
7969
  if (!b || !b.vastTagUrl) return void 0;
7882
- if (b.vastTagUrl.includes(",")) {
7883
- return b.vastTagUrl.split(",").map(function(s) {
7884
- return s.trim();
7970
+ var resolvedUrl = this.resolveVmapAdTagUrl(b.vastTagUrl);
7971
+ if (resolvedUrl.includes(",")) {
7972
+ return resolvedUrl.split(",").map(function(s) {
7973
+ return _this.resolveVmapAdTagUrl(s.trim());
7885
7974
  }).filter(function(s) {
7886
7975
  return s.length > 0;
7887
7976
  });
7888
7977
  }
7889
7978
  return [
7890
- b.vastTagUrl
7979
+ resolvedUrl
7891
7980
  ];
7892
7981
  }
7893
7982
  },
@@ -7919,9 +8008,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7919
8008
  {
7920
8009
  key: "findBreakForTime",
7921
8010
  value: function findBreakForTime(nowMs) {
7922
- var _this_config_driftToleranceMs;
7923
8011
  var schedule = this.vmapBreaks;
7924
- var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
7925
8012
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
7926
8013
  try {
7927
8014
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
@@ -7933,8 +8020,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7933
8020
  if (breakStartMs == null) {
7934
8021
  continue;
7935
8022
  }
7936
- var end = breakStartMs + (b.durationMs || 0);
7937
- if (nowMs >= breakStartMs && (b.durationMs ? nowMs < end : nowMs <= breakStartMs + tol)) {
8023
+ if (b.durationMs) {
8024
+ var end = breakStartMs + b.durationMs;
8025
+ if (nowMs >= breakStartMs && nowMs < end) {
8026
+ return b;
8027
+ }
8028
+ continue;
8029
+ }
8030
+ if (nowMs >= breakStartMs) {
7938
8031
  return b;
7939
8032
  }
7940
8033
  }
@@ -8142,6 +8235,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8142
8235
  this.video.removeEventListener("timeupdate", this.timeUpdateHandler);
8143
8236
  delete this.timeUpdateHandler;
8144
8237
  }
8238
+ if (this.endedHandler) {
8239
+ this.video.removeEventListener("ended", this.endedHandler);
8240
+ delete this.endedHandler;
8241
+ }
8145
8242
  if (this.emptiedHandler) {
8146
8243
  this.video.removeEventListener("emptied", this.emptiedHandler);
8147
8244
  delete this.emptiedHandler;