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.
@@ -388,15 +388,19 @@ var import_react = __toESM(require("react"), 1);
388
388
  var import_hls = __toESM(require("hls.js"), 1);
389
389
  var MAX_VAST_WRAPPER_DEPTH = 5;
390
390
  function createHlsAdPlayer(contentVideo, options) {
391
+ var _contentVideo_AD_VIDEO_PROP, _contentVideo_AD_CONTAINER_PROP;
391
392
  var adPlaying = false;
392
393
  var originalMutedState = false;
393
394
  var originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));
394
395
  var listeners = /* @__PURE__ */ new Map();
395
396
  var licenseKey = options === null || options === void 0 ? void 0 : options.licenseKey;
396
397
  var mainHlsInstance = options === null || options === void 0 ? void 0 : options.mainHlsInstance;
397
- var adVideoElement;
398
+ var AD_CONTAINER_PROP = "__stormcloudAdContainer";
399
+ var AD_VIDEO_PROP = "__stormcloudAdVideo";
400
+ var adVideoElement = (_contentVideo_AD_VIDEO_PROP = contentVideo[AD_VIDEO_PROP]) !== null && _contentVideo_AD_VIDEO_PROP !== void 0 ? _contentVideo_AD_VIDEO_PROP : void 0;
398
401
  var adHls;
399
- var adContainerEl;
402
+ var adContainerEl = (_contentVideo_AD_CONTAINER_PROP = contentVideo[AD_CONTAINER_PROP]) !== null && _contentVideo_AD_CONTAINER_PROP !== void 0 ? _contentVideo_AD_CONTAINER_PROP : void 0;
403
+ var adEventHandlers = [];
400
404
  var currentAd;
401
405
  var podAds = [];
402
406
  var podIndex = 0;
@@ -1040,7 +1044,8 @@ function createHlsAdPlayer(contentVideo, options) {
1040
1044
  }
1041
1045
  function setupAdEventListeners() {
1042
1046
  if (!adVideoElement || !currentAd) return;
1043
- adVideoElement.addEventListener("timeupdate", function() {
1047
+ if (adEventHandlers.length > 0) return;
1048
+ var onTimeUpdate = function onTimeUpdate() {
1044
1049
  if (!currentAd || !adVideoElement) return;
1045
1050
  noteAdProgress();
1046
1051
  var progress = adVideoElement.currentTime / currentAd.duration;
@@ -1056,15 +1061,15 @@ function createHlsAdPlayer(contentVideo, options) {
1056
1061
  trackingFired.thirdQuartile = true;
1057
1062
  fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
1058
1063
  }
1059
- });
1060
- adVideoElement.addEventListener("playing", function() {
1064
+ };
1065
+ var onPlaying = function onPlaying() {
1061
1066
  startStallWatchdog();
1062
1067
  if (!currentAd || trackingFired.start) return;
1063
1068
  trackingFired.start = true;
1064
1069
  fireTrackingPixels(currentAd.trackingUrls.start);
1065
1070
  console.log("[HlsAdPlayer] Ad started playing");
1066
- });
1067
- adVideoElement.addEventListener("ended", function() {
1071
+ };
1072
+ var onEnded = function onEnded() {
1068
1073
  if (!currentAd || trackingFired.complete) return;
1069
1074
  trackingFired.complete = true;
1070
1075
  fireTrackingPixels(currentAd.trackingUrls.complete);
@@ -1073,8 +1078,8 @@ function createHlsAdPlayer(contentVideo, options) {
1073
1078
  return;
1074
1079
  }
1075
1080
  handleAdComplete();
1076
- });
1077
- adVideoElement.addEventListener("error", function(e) {
1081
+ };
1082
+ var onError = function onError(e) {
1078
1083
  if (!adPlaying) return;
1079
1084
  console.error("[HlsAdPlayer] Ad video error:", e);
1080
1085
  if (currentAd) {
@@ -1084,25 +1089,100 @@ function createHlsAdPlayer(contentVideo, options) {
1084
1089
  return;
1085
1090
  }
1086
1091
  handleAdError();
1087
- });
1088
- adVideoElement.addEventListener("volumechange", function() {
1089
- if (!currentAd) return;
1092
+ };
1093
+ var onVolumeChange = function onVolumeChange() {
1094
+ if (!currentAd || !adVideoElement) return;
1090
1095
  if (adVideoElement.muted) {
1091
1096
  fireTrackingPixels(currentAd.trackingUrls.mute);
1092
1097
  } else {
1093
1098
  fireTrackingPixels(currentAd.trackingUrls.unmute);
1094
1099
  }
1095
- });
1096
- adVideoElement.addEventListener("pause", function() {
1097
- if (currentAd && !adVideoElement.ended) {
1100
+ };
1101
+ var onPause = function onPause() {
1102
+ if (currentAd && adVideoElement && !adVideoElement.ended) {
1098
1103
  fireTrackingPixels(currentAd.trackingUrls.pause);
1099
1104
  }
1100
- });
1101
- adVideoElement.addEventListener("play", function() {
1102
- if (currentAd && adVideoElement.currentTime > 0) {
1105
+ };
1106
+ var onPlay = function onPlay() {
1107
+ if (currentAd && adVideoElement && adVideoElement.currentTime > 0) {
1103
1108
  fireTrackingPixels(currentAd.trackingUrls.resume);
1104
1109
  }
1105
- });
1110
+ };
1111
+ adEventHandlers = [
1112
+ {
1113
+ type: "timeupdate",
1114
+ handler: onTimeUpdate
1115
+ },
1116
+ {
1117
+ type: "playing",
1118
+ handler: onPlaying
1119
+ },
1120
+ {
1121
+ type: "ended",
1122
+ handler: onEnded
1123
+ },
1124
+ {
1125
+ type: "error",
1126
+ handler: onError
1127
+ },
1128
+ {
1129
+ type: "volumechange",
1130
+ handler: onVolumeChange
1131
+ },
1132
+ {
1133
+ type: "pause",
1134
+ handler: onPause
1135
+ },
1136
+ {
1137
+ type: "play",
1138
+ handler: onPlay
1139
+ }
1140
+ ];
1141
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1142
+ try {
1143
+ for(var _iterator = adEventHandlers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1144
+ var _step_value = _step.value, type = _step_value.type, handler = _step_value.handler;
1145
+ adVideoElement.addEventListener(type, handler);
1146
+ }
1147
+ } catch (err) {
1148
+ _didIteratorError = true;
1149
+ _iteratorError = err;
1150
+ } finally{
1151
+ try {
1152
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1153
+ _iterator.return();
1154
+ }
1155
+ } finally{
1156
+ if (_didIteratorError) {
1157
+ throw _iteratorError;
1158
+ }
1159
+ }
1160
+ }
1161
+ }
1162
+ function teardownAdEventListeners() {
1163
+ if (adVideoElement) {
1164
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1165
+ try {
1166
+ for(var _iterator = adEventHandlers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1167
+ var _step_value = _step.value, type = _step_value.type, handler = _step_value.handler;
1168
+ adVideoElement.removeEventListener(type, handler);
1169
+ }
1170
+ } catch (err) {
1171
+ _didIteratorError = true;
1172
+ _iteratorError = err;
1173
+ } finally{
1174
+ try {
1175
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1176
+ _iterator.return();
1177
+ }
1178
+ } finally{
1179
+ if (_didIteratorError) {
1180
+ throw _iteratorError;
1181
+ }
1182
+ }
1183
+ }
1184
+ }
1185
+ adEventHandlers = [];
1106
1186
  }
1107
1187
  function setAdPlayingFlag(isPlaying) {
1108
1188
  if (isPlaying) {
@@ -1242,16 +1322,32 @@ function createHlsAdPlayer(contentVideo, options) {
1242
1322
  }
1243
1323
  }, STALL_CHECK_INTERVAL_MS);
1244
1324
  }
1325
+ function releaseAdDecoder() {
1326
+ if (adHls) {
1327
+ adHls.destroy();
1328
+ adHls = void 0;
1329
+ }
1330
+ if (adVideoElement) {
1331
+ try {
1332
+ adVideoElement.pause();
1333
+ adVideoElement.removeAttribute("src");
1334
+ adVideoElement.load();
1335
+ } catch (error) {
1336
+ console.warn("[HlsAdPlayer] Error releasing ad decoder:", error);
1337
+ }
1338
+ }
1339
+ if (adContainerEl) {
1340
+ adContainerEl.style.display = "none";
1341
+ adContainerEl.style.pointerEvents = "none";
1342
+ }
1343
+ }
1245
1344
  function handleAdComplete() {
1246
1345
  console.log("[HlsAdPlayer] Handling ad completion");
1247
1346
  clearStallWatchdog();
1248
1347
  adPlaying = false;
1249
1348
  setAdPlayingFlag(false);
1250
1349
  contentVideo.muted = true;
1251
- if (adContainerEl) {
1252
- adContainerEl.style.display = "none";
1253
- adContainerEl.style.pointerEvents = "none";
1254
- }
1350
+ releaseAdDecoder();
1255
1351
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1256
1352
  contentVideo.style.visibility = "visible";
1257
1353
  contentVideo.style.opacity = "1";
@@ -1274,10 +1370,7 @@ function createHlsAdPlayer(contentVideo, options) {
1274
1370
  adPlaying = false;
1275
1371
  setAdPlayingFlag(false);
1276
1372
  contentVideo.muted = true;
1277
- if (adContainerEl) {
1278
- adContainerEl.style.display = "none";
1279
- adContainerEl.style.pointerEvents = "none";
1280
- }
1373
+ releaseAdDecoder();
1281
1374
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1282
1375
  contentVideo.style.visibility = "visible";
1283
1376
  contentVideo.style.opacity = "1";
@@ -1304,6 +1397,7 @@ function createHlsAdPlayer(contentVideo, options) {
1304
1397
  (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1305
1398
  adContainerEl = container;
1306
1399
  }
1400
+ contentVideo[AD_CONTAINER_PROP] = adContainerEl;
1307
1401
  },
1308
1402
  requestAds: function requestAds(vastTagUrl) {
1309
1403
  return _async_to_generator(function() {
@@ -1406,8 +1500,9 @@ function createHlsAdPlayer(contentVideo, options) {
1406
1500
  if (!adVideoElement) {
1407
1501
  adVideoElement = createAdVideoElement();
1408
1502
  adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.appendChild(adVideoElement);
1409
- setupAdEventListeners();
1503
+ contentVideo[AD_VIDEO_PROP] = adVideoElement;
1410
1504
  }
1505
+ setupAdEventListeners();
1411
1506
  resetQuartileTracking(true);
1412
1507
  contentVolume = contentVideo.volume;
1413
1508
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
@@ -1486,10 +1581,7 @@ function createHlsAdPlayer(contentVideo, options) {
1486
1581
  contentVideo.muted = originalMutedState;
1487
1582
  contentVideo.volume = Math.max(0, Math.min(1, originalVolume));
1488
1583
  console.log("[HlsAdPlayer] Restored mute state on stop: ".concat(previousMutedState, " -> ").concat(originalMutedState, ", volume: ").concat(contentVideo.volume));
1489
- if (adContainerEl) {
1490
- adContainerEl.style.display = "none";
1491
- adContainerEl.style.pointerEvents = "none";
1492
- }
1584
+ releaseAdDecoder();
1493
1585
  contentVideo.style.visibility = "visible";
1494
1586
  contentVideo.style.opacity = "1";
1495
1587
  if (options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) {
@@ -1500,17 +1592,6 @@ function createHlsAdPlayer(contentVideo, options) {
1500
1592
  });
1501
1593
  }
1502
1594
  }
1503
- if (adHls) {
1504
- adHls.destroy();
1505
- adHls = void 0;
1506
- }
1507
- if (adVideoElement) {
1508
- adVideoElement.pause();
1509
- adVideoElement.removeAttribute("src");
1510
- try {
1511
- adVideoElement.load();
1512
- } catch (unused) {}
1513
- }
1514
1595
  currentAd = void 0;
1515
1596
  podAds = [];
1516
1597
  podIndex = 0;
@@ -1549,23 +1630,8 @@ function createHlsAdPlayer(contentVideo, options) {
1549
1630
  setAdPlayingFlag(false);
1550
1631
  contentVideo.muted = originalMutedState;
1551
1632
  contentVideo.volume = Math.max(0, Math.min(1, originalVolume));
1552
- if (adHls) {
1553
- adHls.destroy();
1554
- adHls = void 0;
1555
- }
1556
- if (adVideoElement) {
1557
- adVideoElement.pause();
1558
- adVideoElement.removeAttribute("src");
1559
- try {
1560
- adVideoElement.load();
1561
- } catch (unused) {}
1562
- adVideoElement.remove();
1563
- adVideoElement = void 0;
1564
- }
1565
- if (adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.parentElement) {
1566
- adContainerEl.parentElement.removeChild(adContainerEl);
1567
- }
1568
- adContainerEl = void 0;
1633
+ teardownAdEventListeners();
1634
+ releaseAdDecoder();
1569
1635
  currentAd = void 0;
1570
1636
  podAds = [];
1571
1637
  podIndex = 0;
@@ -3978,6 +4044,11 @@ function detectBrowser() {
3978
4044
  chromeVersion: chromeVersionNum
3979
4045
  };
3980
4046
  }
4047
+ function requiresMediaPipelineResetAfterAds() {
4048
+ var _browser_tizenVersion;
4049
+ var browser = detectBrowser();
4050
+ return browser.name === "Samsung Tizen" && ((_browser_tizenVersion = browser.tizenVersion) !== null && _browser_tizenVersion !== void 0 ? _browser_tizenVersion : 0) >= 5;
4051
+ }
3981
4052
  function getBrowserConfigOverrides() {
3982
4053
  var browser = detectBrowser();
3983
4054
  var overrides = {};
@@ -6721,31 +6792,67 @@ var HlsEngine = /*#__PURE__*/ function() {
6721
6792
  }
6722
6793
  }
6723
6794
  },
6795
+ {
6796
+ key: "recoverMediaError",
6797
+ value: function recoverMediaError() {
6798
+ if (!this.hls) {
6799
+ return;
6800
+ }
6801
+ try {
6802
+ this.hls.recoverMediaError();
6803
+ } catch (error) {
6804
+ if (this.debug) {
6805
+ console.warn("[StormcloudVideoPlayer] recoverMediaError() failed:", error);
6806
+ }
6807
+ }
6808
+ }
6809
+ },
6724
6810
  {
6725
6811
  key: "reloadLiveStream",
6726
6812
  value: function reloadLiveStream() {
6813
+ var _this = this;
6727
6814
  var hls = this.hls;
6728
6815
  if (!hls) {
6729
6816
  return;
6730
6817
  }
6731
6818
  if (this.debug) {
6732
- console.log("[StormcloudVideoPlayer] Hard-reloading live stream (detach \u2192 reset video \u2192 re-attach)");
6819
+ console.log("[StormcloudVideoPlayer] Hard-reloading live stream (destroy hls \u2192 reset video \u2192 fresh setupHls)");
6733
6820
  }
6734
6821
  try {
6735
6822
  this.reloadingLiveStream = true;
6736
6823
  hls.stopLoad();
6737
6824
  hls.detachMedia();
6825
+ hls.destroy();
6826
+ this.hls = void 0;
6738
6827
  try {
6739
6828
  this.video.removeAttribute("src");
6740
6829
  this.video.load();
6741
- } catch (unused) {}
6742
- hls.attachMedia(this.video);
6830
+ } catch (error) {
6831
+ console.warn("[StormcloudVideoPlayer] video.load() threw after src removal:", error);
6832
+ }
6743
6833
  } catch (error) {
6744
6834
  this.reloadingLiveStream = false;
6835
+ this.hls = void 0;
6745
6836
  if (this.debug) {
6746
- console.warn("[StormcloudVideoPlayer] Failed to hard-reload live stream:", error);
6837
+ console.warn("[StormcloudVideoPlayer] Failed to tear down during hard-reload:", error);
6747
6838
  }
6839
+ return;
6748
6840
  }
6841
+ var recreateDelayMs = 600;
6842
+ window.setTimeout(function() {
6843
+ if (_this.hls) {
6844
+ _this.reloadingLiveStream = false;
6845
+ return;
6846
+ }
6847
+ try {
6848
+ _this.setupHls();
6849
+ } catch (error) {
6850
+ _this.reloadingLiveStream = false;
6851
+ if (_this.debug) {
6852
+ console.warn("[StormcloudVideoPlayer] Failed to rebuild pipeline during hard-reload:", error);
6853
+ }
6854
+ }
6855
+ }, recreateDelayMs);
6749
6856
  }
6750
6857
  },
6751
6858
  {
@@ -8058,6 +8165,14 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8058
8165
  this.host.video.style.visibility = "visible";
8059
8166
  this.host.video.style.opacity = "1";
8060
8167
  if (this.host.isLiveStream()) {
8168
+ if (this.host.requiresMediaPipelineResetAfterAds()) {
8169
+ if (this.debug) {
8170
+ console.log("[StormcloudVideoPlayer] Live break end \u2014 hard-reloading stream to reacquire decoder (single-decoder platform)");
8171
+ }
8172
+ this.host.reloadLiveStream();
8173
+ this.monitorLiveResumeStall(this.host.video.currentTime);
8174
+ return;
8175
+ }
8061
8176
  var liveSyncPos = this.host.getLiveSyncPosition();
8062
8177
  if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {
8063
8178
  if (this.debug) {
@@ -8119,11 +8234,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8119
8234
  key: "monitorLiveResumeStall",
8120
8235
  value: function monitorLiveResumeStall(resumeBaseline) {
8121
8236
  var _this = this;
8237
+ var needsReset = this.host.requiresMediaPipelineResetAfterAds();
8122
8238
  var pollIntervalMs = 1e3;
8123
- var graceMs = 5e3;
8124
- var recoveryCooldownMs = 5e3;
8125
- var maxRecoveries = 2;
8126
- var maxMonitorMs = 3e4;
8239
+ var graceMs = needsReset ? 8e3 : 5e3;
8240
+ var recoveryCooldownMs = needsReset ? 7e3 : 5e3;
8241
+ var maxRecoveries = 3;
8242
+ var maxMonitorMs = 45e3;
8127
8243
  var startTime = Date.now();
8128
8244
  var baseline = resumeBaseline;
8129
8245
  var recoveries = 0;
@@ -8235,8 +8351,17 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8235
8351
  if (canRecover) {
8236
8352
  recoveries++;
8237
8353
  lastRecoveryAt = Date.now();
8238
- var isFinalAttempt = recoveries >= maxRecoveries;
8239
- if (!isFinalAttempt) {
8354
+ if (needsReset) {
8355
+ if (_this.debug) {
8356
+ console.warn("[StormcloudVideoPlayer] Content still wedged after ad break — hard-reloading live stream (recovery ".concat(recoveries, "/").concat(maxRecoveries, ") — ").concat(_this.describeVideoState()));
8357
+ }
8358
+ _this.host.reloadLiveStream();
8359
+ } else if (recoveries === 1) {
8360
+ if (_this.debug) {
8361
+ console.warn("[StormcloudVideoPlayer] Content wedged after ad break — calling recoverMediaError() (recovery ".concat(recoveries, "/").concat(maxRecoveries, ") — ").concat(_this.describeVideoState()));
8362
+ }
8363
+ _this.host.recoverMediaError();
8364
+ } else if (recoveries === 2) {
8240
8365
  var liveSyncPos = _this.host.getLiveSyncPosition();
8241
8366
  var reloadPos = liveSyncPos != null && liveSyncPos > currentTime ? liveSyncPos : currentTime;
8242
8367
  if (_this.debug) {
@@ -8268,17 +8393,41 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8268
8393
  value: function resumeContentPlayback() {
8269
8394
  var _this = this;
8270
8395
  var attempt = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 0;
8271
- var maxAttempts = 3;
8272
- if (!this.host.video.paused) {
8396
+ var maxAttempts = 5;
8397
+ var video = this.host.video;
8398
+ if (!video.paused) {
8273
8399
  if (this.debug && attempt === 0) {
8274
8400
  console.log("[StormcloudVideoPlayer] Content video already playing after ads");
8275
8401
  }
8276
8402
  return;
8277
8403
  }
8278
8404
  if (this.debug) {
8279
- console.log("[StormcloudVideoPlayer] Resuming content playback after ads (attempt ".concat(attempt + 1, "/").concat(maxAttempts, ")"));
8405
+ console.log("[StormcloudVideoPlayer] Resuming content playback after ads (attempt ".concat(attempt + 1, "/").concat(maxAttempts, ", readyState=").concat(video.readyState, ")"));
8280
8406
  }
8281
- var playResult = this.host.video.play();
8407
+ if (video.readyState < 3) {
8408
+ if (attempt + 1 >= maxAttempts) {
8409
+ var _video_play;
8410
+ if (this.debug) {
8411
+ console.warn("[StormcloudVideoPlayer] readyState never reached HAVE_FUTURE_DATA, forcing play()");
8412
+ }
8413
+ (_video_play = video.play()) === null || _video_play === void 0 ? void 0 : _video_play.catch(function() {});
8414
+ return;
8415
+ }
8416
+ var onReady = function onReady1() {
8417
+ video.removeEventListener("canplay", onReady);
8418
+ _this.resumeContentPlayback(attempt + 1);
8419
+ };
8420
+ video.addEventListener("canplay", onReady);
8421
+ var backoffMs = 500 * (attempt + 1);
8422
+ window.setTimeout(function() {
8423
+ video.removeEventListener("canplay", onReady);
8424
+ if (video.paused) {
8425
+ _this.resumeContentPlayback(attempt + 1);
8426
+ }
8427
+ }, backoffMs);
8428
+ return;
8429
+ }
8430
+ var playResult = video.play();
8282
8431
  if (playResult && typeof playResult.catch === "function") {
8283
8432
  playResult.catch(function(error) {
8284
8433
  if (attempt + 1 >= maxAttempts) {
@@ -8417,6 +8566,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8417
8566
  reloadLiveStream: function reloadLiveStream() {
8418
8567
  return _this.hlsEngine.reloadLiveStream();
8419
8568
  },
8569
+ recoverMediaError: function recoverMediaError() {
8570
+ return _this.hlsEngine.recoverMediaError();
8571
+ },
8572
+ requiresMediaPipelineResetAfterAds: function requiresMediaPipelineResetAfterAds1() {
8573
+ return requiresMediaPipelineResetAfterAds();
8574
+ },
8420
8575
  generatePodVastUrl: function generatePodVastUrl(base, breakDurationMs) {
8421
8576
  return _this.generatePodVastUrl(base, breakDurationMs);
8422
8577
  }