stormcloud-video-player 0.8.45 → 0.8.47

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.
@@ -351,15 +351,19 @@ module.exports = __toCommonJS(StormcloudVideoPlayer_exports);
351
351
  var import_hls = __toESM(require("hls.js"), 1);
352
352
  var MAX_VAST_WRAPPER_DEPTH = 5;
353
353
  function createHlsAdPlayer(contentVideo, options) {
354
+ var _contentVideo_AD_VIDEO_PROP, _contentVideo_AD_CONTAINER_PROP;
354
355
  var adPlaying = false;
355
356
  var originalMutedState = false;
356
357
  var originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));
357
358
  var listeners = /* @__PURE__ */ new Map();
358
359
  var licenseKey = options === null || options === void 0 ? void 0 : options.licenseKey;
359
360
  var mainHlsInstance = options === null || options === void 0 ? void 0 : options.mainHlsInstance;
360
- var adVideoElement;
361
+ var AD_CONTAINER_PROP = "__stormcloudAdContainer";
362
+ var AD_VIDEO_PROP = "__stormcloudAdVideo";
363
+ var adVideoElement = (_contentVideo_AD_VIDEO_PROP = contentVideo[AD_VIDEO_PROP]) !== null && _contentVideo_AD_VIDEO_PROP !== void 0 ? _contentVideo_AD_VIDEO_PROP : void 0;
361
364
  var adHls;
362
- var adContainerEl;
365
+ var adContainerEl = (_contentVideo_AD_CONTAINER_PROP = contentVideo[AD_CONTAINER_PROP]) !== null && _contentVideo_AD_CONTAINER_PROP !== void 0 ? _contentVideo_AD_CONTAINER_PROP : void 0;
366
+ var adEventHandlers = [];
363
367
  var currentAd;
364
368
  var podAds = [];
365
369
  var podIndex = 0;
@@ -1003,7 +1007,8 @@ function createHlsAdPlayer(contentVideo, options) {
1003
1007
  }
1004
1008
  function setupAdEventListeners() {
1005
1009
  if (!adVideoElement || !currentAd) return;
1006
- adVideoElement.addEventListener("timeupdate", function() {
1010
+ if (adEventHandlers.length > 0) return;
1011
+ var onTimeUpdate = function onTimeUpdate() {
1007
1012
  if (!currentAd || !adVideoElement) return;
1008
1013
  noteAdProgress();
1009
1014
  var progress = adVideoElement.currentTime / currentAd.duration;
@@ -1019,15 +1024,15 @@ function createHlsAdPlayer(contentVideo, options) {
1019
1024
  trackingFired.thirdQuartile = true;
1020
1025
  fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
1021
1026
  }
1022
- });
1023
- adVideoElement.addEventListener("playing", function() {
1027
+ };
1028
+ var onPlaying = function onPlaying() {
1024
1029
  startStallWatchdog();
1025
1030
  if (!currentAd || trackingFired.start) return;
1026
1031
  trackingFired.start = true;
1027
1032
  fireTrackingPixels(currentAd.trackingUrls.start);
1028
1033
  console.log("[HlsAdPlayer] Ad started playing");
1029
- });
1030
- adVideoElement.addEventListener("ended", function() {
1034
+ };
1035
+ var onEnded = function onEnded() {
1031
1036
  if (!currentAd || trackingFired.complete) return;
1032
1037
  trackingFired.complete = true;
1033
1038
  fireTrackingPixels(currentAd.trackingUrls.complete);
@@ -1036,8 +1041,8 @@ function createHlsAdPlayer(contentVideo, options) {
1036
1041
  return;
1037
1042
  }
1038
1043
  handleAdComplete();
1039
- });
1040
- adVideoElement.addEventListener("error", function(e) {
1044
+ };
1045
+ var onError = function onError(e) {
1041
1046
  if (!adPlaying) return;
1042
1047
  console.error("[HlsAdPlayer] Ad video error:", e);
1043
1048
  if (currentAd) {
@@ -1047,25 +1052,100 @@ function createHlsAdPlayer(contentVideo, options) {
1047
1052
  return;
1048
1053
  }
1049
1054
  handleAdError();
1050
- });
1051
- adVideoElement.addEventListener("volumechange", function() {
1052
- if (!currentAd) return;
1055
+ };
1056
+ var onVolumeChange = function onVolumeChange() {
1057
+ if (!currentAd || !adVideoElement) return;
1053
1058
  if (adVideoElement.muted) {
1054
1059
  fireTrackingPixels(currentAd.trackingUrls.mute);
1055
1060
  } else {
1056
1061
  fireTrackingPixels(currentAd.trackingUrls.unmute);
1057
1062
  }
1058
- });
1059
- adVideoElement.addEventListener("pause", function() {
1060
- if (currentAd && !adVideoElement.ended) {
1063
+ };
1064
+ var onPause = function onPause() {
1065
+ if (currentAd && adVideoElement && !adVideoElement.ended) {
1061
1066
  fireTrackingPixels(currentAd.trackingUrls.pause);
1062
1067
  }
1063
- });
1064
- adVideoElement.addEventListener("play", function() {
1065
- if (currentAd && adVideoElement.currentTime > 0) {
1068
+ };
1069
+ var onPlay = function onPlay() {
1070
+ if (currentAd && adVideoElement && adVideoElement.currentTime > 0) {
1066
1071
  fireTrackingPixels(currentAd.trackingUrls.resume);
1067
1072
  }
1068
- });
1073
+ };
1074
+ adEventHandlers = [
1075
+ {
1076
+ type: "timeupdate",
1077
+ handler: onTimeUpdate
1078
+ },
1079
+ {
1080
+ type: "playing",
1081
+ handler: onPlaying
1082
+ },
1083
+ {
1084
+ type: "ended",
1085
+ handler: onEnded
1086
+ },
1087
+ {
1088
+ type: "error",
1089
+ handler: onError
1090
+ },
1091
+ {
1092
+ type: "volumechange",
1093
+ handler: onVolumeChange
1094
+ },
1095
+ {
1096
+ type: "pause",
1097
+ handler: onPause
1098
+ },
1099
+ {
1100
+ type: "play",
1101
+ handler: onPlay
1102
+ }
1103
+ ];
1104
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1105
+ try {
1106
+ for(var _iterator = adEventHandlers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1107
+ var _step_value = _step.value, type = _step_value.type, handler = _step_value.handler;
1108
+ adVideoElement.addEventListener(type, handler);
1109
+ }
1110
+ } catch (err) {
1111
+ _didIteratorError = true;
1112
+ _iteratorError = err;
1113
+ } finally{
1114
+ try {
1115
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1116
+ _iterator.return();
1117
+ }
1118
+ } finally{
1119
+ if (_didIteratorError) {
1120
+ throw _iteratorError;
1121
+ }
1122
+ }
1123
+ }
1124
+ }
1125
+ function teardownAdEventListeners() {
1126
+ if (adVideoElement) {
1127
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1128
+ try {
1129
+ for(var _iterator = adEventHandlers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1130
+ var _step_value = _step.value, type = _step_value.type, handler = _step_value.handler;
1131
+ adVideoElement.removeEventListener(type, handler);
1132
+ }
1133
+ } catch (err) {
1134
+ _didIteratorError = true;
1135
+ _iteratorError = err;
1136
+ } finally{
1137
+ try {
1138
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1139
+ _iterator.return();
1140
+ }
1141
+ } finally{
1142
+ if (_didIteratorError) {
1143
+ throw _iteratorError;
1144
+ }
1145
+ }
1146
+ }
1147
+ }
1148
+ adEventHandlers = [];
1069
1149
  }
1070
1150
  function setAdPlayingFlag(isPlaying) {
1071
1151
  if (isPlaying) {
@@ -1205,16 +1285,32 @@ function createHlsAdPlayer(contentVideo, options) {
1205
1285
  }
1206
1286
  }, STALL_CHECK_INTERVAL_MS);
1207
1287
  }
1288
+ function releaseAdDecoder() {
1289
+ if (adHls) {
1290
+ adHls.destroy();
1291
+ adHls = void 0;
1292
+ }
1293
+ if (adVideoElement) {
1294
+ try {
1295
+ adVideoElement.pause();
1296
+ adVideoElement.removeAttribute("src");
1297
+ adVideoElement.load();
1298
+ } catch (error) {
1299
+ console.warn("[HlsAdPlayer] Error releasing ad decoder:", error);
1300
+ }
1301
+ }
1302
+ if (adContainerEl) {
1303
+ adContainerEl.style.display = "none";
1304
+ adContainerEl.style.pointerEvents = "none";
1305
+ }
1306
+ }
1208
1307
  function handleAdComplete() {
1209
1308
  console.log("[HlsAdPlayer] Handling ad completion");
1210
1309
  clearStallWatchdog();
1211
1310
  adPlaying = false;
1212
1311
  setAdPlayingFlag(false);
1213
1312
  contentVideo.muted = true;
1214
- if (adContainerEl) {
1215
- adContainerEl.style.display = "none";
1216
- adContainerEl.style.pointerEvents = "none";
1217
- }
1313
+ releaseAdDecoder();
1218
1314
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1219
1315
  contentVideo.style.visibility = "visible";
1220
1316
  contentVideo.style.opacity = "1";
@@ -1237,10 +1333,7 @@ function createHlsAdPlayer(contentVideo, options) {
1237
1333
  adPlaying = false;
1238
1334
  setAdPlayingFlag(false);
1239
1335
  contentVideo.muted = true;
1240
- if (adContainerEl) {
1241
- adContainerEl.style.display = "none";
1242
- adContainerEl.style.pointerEvents = "none";
1243
- }
1336
+ releaseAdDecoder();
1244
1337
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1245
1338
  contentVideo.style.visibility = "visible";
1246
1339
  contentVideo.style.opacity = "1";
@@ -1267,6 +1360,7 @@ function createHlsAdPlayer(contentVideo, options) {
1267
1360
  (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1268
1361
  adContainerEl = container;
1269
1362
  }
1363
+ contentVideo[AD_CONTAINER_PROP] = adContainerEl;
1270
1364
  },
1271
1365
  requestAds: function requestAds(vastTagUrl) {
1272
1366
  return _async_to_generator(function() {
@@ -1369,8 +1463,9 @@ function createHlsAdPlayer(contentVideo, options) {
1369
1463
  if (!adVideoElement) {
1370
1464
  adVideoElement = createAdVideoElement();
1371
1465
  adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.appendChild(adVideoElement);
1372
- setupAdEventListeners();
1466
+ contentVideo[AD_VIDEO_PROP] = adVideoElement;
1373
1467
  }
1468
+ setupAdEventListeners();
1374
1469
  resetQuartileTracking(true);
1375
1470
  contentVolume = contentVideo.volume;
1376
1471
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
@@ -1449,10 +1544,7 @@ function createHlsAdPlayer(contentVideo, options) {
1449
1544
  contentVideo.muted = originalMutedState;
1450
1545
  contentVideo.volume = Math.max(0, Math.min(1, originalVolume));
1451
1546
  console.log("[HlsAdPlayer] Restored mute state on stop: ".concat(previousMutedState, " -> ").concat(originalMutedState, ", volume: ").concat(contentVideo.volume));
1452
- if (adContainerEl) {
1453
- adContainerEl.style.display = "none";
1454
- adContainerEl.style.pointerEvents = "none";
1455
- }
1547
+ releaseAdDecoder();
1456
1548
  contentVideo.style.visibility = "visible";
1457
1549
  contentVideo.style.opacity = "1";
1458
1550
  if (options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) {
@@ -1463,17 +1555,6 @@ function createHlsAdPlayer(contentVideo, options) {
1463
1555
  });
1464
1556
  }
1465
1557
  }
1466
- if (adHls) {
1467
- adHls.destroy();
1468
- adHls = void 0;
1469
- }
1470
- if (adVideoElement) {
1471
- adVideoElement.pause();
1472
- adVideoElement.removeAttribute("src");
1473
- try {
1474
- adVideoElement.load();
1475
- } catch (unused) {}
1476
- }
1477
1558
  currentAd = void 0;
1478
1559
  podAds = [];
1479
1560
  podIndex = 0;
@@ -1512,23 +1593,8 @@ function createHlsAdPlayer(contentVideo, options) {
1512
1593
  setAdPlayingFlag(false);
1513
1594
  contentVideo.muted = originalMutedState;
1514
1595
  contentVideo.volume = Math.max(0, Math.min(1, originalVolume));
1515
- if (adHls) {
1516
- adHls.destroy();
1517
- adHls = void 0;
1518
- }
1519
- if (adVideoElement) {
1520
- adVideoElement.pause();
1521
- adVideoElement.removeAttribute("src");
1522
- try {
1523
- adVideoElement.load();
1524
- } catch (unused) {}
1525
- adVideoElement.remove();
1526
- adVideoElement = void 0;
1527
- }
1528
- if (adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.parentElement) {
1529
- adContainerEl.parentElement.removeChild(adContainerEl);
1530
- }
1531
- adContainerEl = void 0;
1596
+ teardownAdEventListeners();
1597
+ releaseAdDecoder();
1532
1598
  currentAd = void 0;
1533
1599
  podAds = [];
1534
1600
  podIndex = 0;
@@ -3941,6 +4007,11 @@ function detectBrowser() {
3941
4007
  chromeVersion: chromeVersionNum
3942
4008
  };
3943
4009
  }
4010
+ function requiresMediaPipelineResetAfterAds() {
4011
+ var _browser_tizenVersion;
4012
+ var browser = detectBrowser();
4013
+ return browser.name === "Samsung Tizen" && ((_browser_tizenVersion = browser.tizenVersion) !== null && _browser_tizenVersion !== void 0 ? _browser_tizenVersion : 0) >= 5;
4014
+ }
3944
4015
  function getBrowserConfigOverrides() {
3945
4016
  var browser = detectBrowser();
3946
4017
  var overrides = {};
@@ -6684,31 +6755,67 @@ var HlsEngine = /*#__PURE__*/ function() {
6684
6755
  }
6685
6756
  }
6686
6757
  },
6758
+ {
6759
+ key: "recoverMediaError",
6760
+ value: function recoverMediaError() {
6761
+ if (!this.hls) {
6762
+ return;
6763
+ }
6764
+ try {
6765
+ this.hls.recoverMediaError();
6766
+ } catch (error) {
6767
+ if (this.debug) {
6768
+ console.warn("[StormcloudVideoPlayer] recoverMediaError() failed:", error);
6769
+ }
6770
+ }
6771
+ }
6772
+ },
6687
6773
  {
6688
6774
  key: "reloadLiveStream",
6689
6775
  value: function reloadLiveStream() {
6776
+ var _this = this;
6690
6777
  var hls = this.hls;
6691
6778
  if (!hls) {
6692
6779
  return;
6693
6780
  }
6694
6781
  if (this.debug) {
6695
- console.log("[StormcloudVideoPlayer] Hard-reloading live stream (detach \u2192 reset video \u2192 re-attach)");
6782
+ console.log("[StormcloudVideoPlayer] Hard-reloading live stream (destroy hls \u2192 reset video \u2192 fresh setupHls)");
6696
6783
  }
6697
6784
  try {
6698
6785
  this.reloadingLiveStream = true;
6699
6786
  hls.stopLoad();
6700
6787
  hls.detachMedia();
6788
+ hls.destroy();
6789
+ this.hls = void 0;
6701
6790
  try {
6702
6791
  this.video.removeAttribute("src");
6703
6792
  this.video.load();
6704
- } catch (unused) {}
6705
- hls.attachMedia(this.video);
6793
+ } catch (error) {
6794
+ console.warn("[StormcloudVideoPlayer] video.load() threw after src removal:", error);
6795
+ }
6706
6796
  } catch (error) {
6707
6797
  this.reloadingLiveStream = false;
6798
+ this.hls = void 0;
6708
6799
  if (this.debug) {
6709
- console.warn("[StormcloudVideoPlayer] Failed to hard-reload live stream:", error);
6800
+ console.warn("[StormcloudVideoPlayer] Failed to tear down during hard-reload:", error);
6710
6801
  }
6802
+ return;
6711
6803
  }
6804
+ var recreateDelayMs = 600;
6805
+ window.setTimeout(function() {
6806
+ if (_this.hls) {
6807
+ _this.reloadingLiveStream = false;
6808
+ return;
6809
+ }
6810
+ try {
6811
+ _this.setupHls();
6812
+ } catch (error) {
6813
+ _this.reloadingLiveStream = false;
6814
+ if (_this.debug) {
6815
+ console.warn("[StormcloudVideoPlayer] Failed to rebuild pipeline during hard-reload:", error);
6816
+ }
6817
+ }
6818
+ }, recreateDelayMs);
6712
6819
  }
6713
6820
  },
6714
6821
  {
@@ -8021,6 +8128,14 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8021
8128
  this.host.video.style.visibility = "visible";
8022
8129
  this.host.video.style.opacity = "1";
8023
8130
  if (this.host.isLiveStream()) {
8131
+ if (this.host.requiresMediaPipelineResetAfterAds()) {
8132
+ if (this.debug) {
8133
+ console.log("[StormcloudVideoPlayer] Live break end \u2014 hard-reloading stream to reacquire decoder (single-decoder platform)");
8134
+ }
8135
+ this.host.reloadLiveStream();
8136
+ this.monitorLiveResumeStall(this.host.video.currentTime);
8137
+ return;
8138
+ }
8024
8139
  var liveSyncPos = this.host.getLiveSyncPosition();
8025
8140
  if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {
8026
8141
  if (this.debug) {
@@ -8082,11 +8197,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8082
8197
  key: "monitorLiveResumeStall",
8083
8198
  value: function monitorLiveResumeStall(resumeBaseline) {
8084
8199
  var _this = this;
8200
+ var needsReset = this.host.requiresMediaPipelineResetAfterAds();
8085
8201
  var pollIntervalMs = 1e3;
8086
- var graceMs = 5e3;
8087
- var recoveryCooldownMs = 5e3;
8088
- var maxRecoveries = 2;
8089
- var maxMonitorMs = 3e4;
8202
+ var graceMs = needsReset ? 8e3 : 5e3;
8203
+ var recoveryCooldownMs = needsReset ? 7e3 : 5e3;
8204
+ var maxRecoveries = 3;
8205
+ var maxMonitorMs = 45e3;
8090
8206
  var startTime = Date.now();
8091
8207
  var baseline = resumeBaseline;
8092
8208
  var recoveries = 0;
@@ -8198,8 +8314,17 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8198
8314
  if (canRecover) {
8199
8315
  recoveries++;
8200
8316
  lastRecoveryAt = Date.now();
8201
- var isFinalAttempt = recoveries >= maxRecoveries;
8202
- if (!isFinalAttempt) {
8317
+ if (needsReset) {
8318
+ if (_this.debug) {
8319
+ console.warn("[StormcloudVideoPlayer] Content still wedged after ad break — hard-reloading live stream (recovery ".concat(recoveries, "/").concat(maxRecoveries, ") — ").concat(_this.describeVideoState()));
8320
+ }
8321
+ _this.host.reloadLiveStream();
8322
+ } else if (recoveries === 1) {
8323
+ if (_this.debug) {
8324
+ console.warn("[StormcloudVideoPlayer] Content wedged after ad break — calling recoverMediaError() (recovery ".concat(recoveries, "/").concat(maxRecoveries, ") — ").concat(_this.describeVideoState()));
8325
+ }
8326
+ _this.host.recoverMediaError();
8327
+ } else if (recoveries === 2) {
8203
8328
  var liveSyncPos = _this.host.getLiveSyncPosition();
8204
8329
  var reloadPos = liveSyncPos != null && liveSyncPos > currentTime ? liveSyncPos : currentTime;
8205
8330
  if (_this.debug) {
@@ -8231,17 +8356,41 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8231
8356
  value: function resumeContentPlayback() {
8232
8357
  var _this = this;
8233
8358
  var attempt = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 0;
8234
- var maxAttempts = 3;
8235
- if (!this.host.video.paused) {
8359
+ var maxAttempts = 5;
8360
+ var video = this.host.video;
8361
+ if (!video.paused) {
8236
8362
  if (this.debug && attempt === 0) {
8237
8363
  console.log("[StormcloudVideoPlayer] Content video already playing after ads");
8238
8364
  }
8239
8365
  return;
8240
8366
  }
8241
8367
  if (this.debug) {
8242
- console.log("[StormcloudVideoPlayer] Resuming content playback after ads (attempt ".concat(attempt + 1, "/").concat(maxAttempts, ")"));
8368
+ console.log("[StormcloudVideoPlayer] Resuming content playback after ads (attempt ".concat(attempt + 1, "/").concat(maxAttempts, ", readyState=").concat(video.readyState, ")"));
8243
8369
  }
8244
- var playResult = this.host.video.play();
8370
+ if (video.readyState < 3) {
8371
+ if (attempt + 1 >= maxAttempts) {
8372
+ var _video_play;
8373
+ if (this.debug) {
8374
+ console.warn("[StormcloudVideoPlayer] readyState never reached HAVE_FUTURE_DATA, forcing play()");
8375
+ }
8376
+ (_video_play = video.play()) === null || _video_play === void 0 ? void 0 : _video_play.catch(function() {});
8377
+ return;
8378
+ }
8379
+ var onReady = function onReady1() {
8380
+ video.removeEventListener("canplay", onReady);
8381
+ _this.resumeContentPlayback(attempt + 1);
8382
+ };
8383
+ video.addEventListener("canplay", onReady);
8384
+ var backoffMs = 500 * (attempt + 1);
8385
+ window.setTimeout(function() {
8386
+ video.removeEventListener("canplay", onReady);
8387
+ if (video.paused) {
8388
+ _this.resumeContentPlayback(attempt + 1);
8389
+ }
8390
+ }, backoffMs);
8391
+ return;
8392
+ }
8393
+ var playResult = video.play();
8245
8394
  if (playResult && typeof playResult.catch === "function") {
8246
8395
  playResult.catch(function(error) {
8247
8396
  if (attempt + 1 >= maxAttempts) {
@@ -8380,6 +8529,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8380
8529
  reloadLiveStream: function reloadLiveStream() {
8381
8530
  return _this.hlsEngine.reloadLiveStream();
8382
8531
  },
8532
+ recoverMediaError: function recoverMediaError() {
8533
+ return _this.hlsEngine.recoverMediaError();
8534
+ },
8535
+ requiresMediaPipelineResetAfterAds: function requiresMediaPipelineResetAfterAds1() {
8536
+ return requiresMediaPipelineResetAfterAds();
8537
+ },
8383
8538
  generatePodVastUrl: function generatePodVastUrl(base, breakDurationMs) {
8384
8539
  return _this.generatePodVastUrl(base, breakDurationMs);
8385
8540
  }