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.
@@ -402,15 +402,19 @@ var import_react = require("react");
402
402
  var import_hls = __toESM(require("hls.js"), 1);
403
403
  var MAX_VAST_WRAPPER_DEPTH = 5;
404
404
  function createHlsAdPlayer(contentVideo, options) {
405
+ var _contentVideo_AD_VIDEO_PROP, _contentVideo_AD_CONTAINER_PROP;
405
406
  var adPlaying = false;
406
407
  var originalMutedState = false;
407
408
  var originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));
408
409
  var listeners = /* @__PURE__ */ new Map();
409
410
  var licenseKey = options === null || options === void 0 ? void 0 : options.licenseKey;
410
411
  var mainHlsInstance = options === null || options === void 0 ? void 0 : options.mainHlsInstance;
411
- var adVideoElement;
412
+ var AD_CONTAINER_PROP = "__stormcloudAdContainer";
413
+ var AD_VIDEO_PROP = "__stormcloudAdVideo";
414
+ var adVideoElement = (_contentVideo_AD_VIDEO_PROP = contentVideo[AD_VIDEO_PROP]) !== null && _contentVideo_AD_VIDEO_PROP !== void 0 ? _contentVideo_AD_VIDEO_PROP : void 0;
412
415
  var adHls;
413
- var adContainerEl;
416
+ var adContainerEl = (_contentVideo_AD_CONTAINER_PROP = contentVideo[AD_CONTAINER_PROP]) !== null && _contentVideo_AD_CONTAINER_PROP !== void 0 ? _contentVideo_AD_CONTAINER_PROP : void 0;
417
+ var adEventHandlers = [];
414
418
  var currentAd;
415
419
  var podAds = [];
416
420
  var podIndex = 0;
@@ -1054,7 +1058,8 @@ function createHlsAdPlayer(contentVideo, options) {
1054
1058
  }
1055
1059
  function setupAdEventListeners() {
1056
1060
  if (!adVideoElement || !currentAd) return;
1057
- adVideoElement.addEventListener("timeupdate", function() {
1061
+ if (adEventHandlers.length > 0) return;
1062
+ var onTimeUpdate = function onTimeUpdate() {
1058
1063
  if (!currentAd || !adVideoElement) return;
1059
1064
  noteAdProgress();
1060
1065
  var progress = adVideoElement.currentTime / currentAd.duration;
@@ -1070,15 +1075,15 @@ function createHlsAdPlayer(contentVideo, options) {
1070
1075
  trackingFired.thirdQuartile = true;
1071
1076
  fireTrackingPixels(currentAd.trackingUrls.thirdQuartile);
1072
1077
  }
1073
- });
1074
- adVideoElement.addEventListener("playing", function() {
1078
+ };
1079
+ var onPlaying = function onPlaying() {
1075
1080
  startStallWatchdog();
1076
1081
  if (!currentAd || trackingFired.start) return;
1077
1082
  trackingFired.start = true;
1078
1083
  fireTrackingPixels(currentAd.trackingUrls.start);
1079
1084
  console.log("[HlsAdPlayer] Ad started playing");
1080
- });
1081
- adVideoElement.addEventListener("ended", function() {
1085
+ };
1086
+ var onEnded = function onEnded() {
1082
1087
  if (!currentAd || trackingFired.complete) return;
1083
1088
  trackingFired.complete = true;
1084
1089
  fireTrackingPixels(currentAd.trackingUrls.complete);
@@ -1087,8 +1092,8 @@ function createHlsAdPlayer(contentVideo, options) {
1087
1092
  return;
1088
1093
  }
1089
1094
  handleAdComplete();
1090
- });
1091
- adVideoElement.addEventListener("error", function(e) {
1095
+ };
1096
+ var onError = function onError(e) {
1092
1097
  if (!adPlaying) return;
1093
1098
  console.error("[HlsAdPlayer] Ad video error:", e);
1094
1099
  if (currentAd) {
@@ -1098,25 +1103,100 @@ function createHlsAdPlayer(contentVideo, options) {
1098
1103
  return;
1099
1104
  }
1100
1105
  handleAdError();
1101
- });
1102
- adVideoElement.addEventListener("volumechange", function() {
1103
- if (!currentAd) return;
1106
+ };
1107
+ var onVolumeChange = function onVolumeChange() {
1108
+ if (!currentAd || !adVideoElement) return;
1104
1109
  if (adVideoElement.muted) {
1105
1110
  fireTrackingPixels(currentAd.trackingUrls.mute);
1106
1111
  } else {
1107
1112
  fireTrackingPixels(currentAd.trackingUrls.unmute);
1108
1113
  }
1109
- });
1110
- adVideoElement.addEventListener("pause", function() {
1111
- if (currentAd && !adVideoElement.ended) {
1114
+ };
1115
+ var onPause = function onPause() {
1116
+ if (currentAd && adVideoElement && !adVideoElement.ended) {
1112
1117
  fireTrackingPixels(currentAd.trackingUrls.pause);
1113
1118
  }
1114
- });
1115
- adVideoElement.addEventListener("play", function() {
1116
- if (currentAd && adVideoElement.currentTime > 0) {
1119
+ };
1120
+ var onPlay = function onPlay() {
1121
+ if (currentAd && adVideoElement && adVideoElement.currentTime > 0) {
1117
1122
  fireTrackingPixels(currentAd.trackingUrls.resume);
1118
1123
  }
1119
- });
1124
+ };
1125
+ adEventHandlers = [
1126
+ {
1127
+ type: "timeupdate",
1128
+ handler: onTimeUpdate
1129
+ },
1130
+ {
1131
+ type: "playing",
1132
+ handler: onPlaying
1133
+ },
1134
+ {
1135
+ type: "ended",
1136
+ handler: onEnded
1137
+ },
1138
+ {
1139
+ type: "error",
1140
+ handler: onError
1141
+ },
1142
+ {
1143
+ type: "volumechange",
1144
+ handler: onVolumeChange
1145
+ },
1146
+ {
1147
+ type: "pause",
1148
+ handler: onPause
1149
+ },
1150
+ {
1151
+ type: "play",
1152
+ handler: onPlay
1153
+ }
1154
+ ];
1155
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1156
+ try {
1157
+ for(var _iterator = adEventHandlers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1158
+ var _step_value = _step.value, type = _step_value.type, handler = _step_value.handler;
1159
+ adVideoElement.addEventListener(type, handler);
1160
+ }
1161
+ } catch (err) {
1162
+ _didIteratorError = true;
1163
+ _iteratorError = err;
1164
+ } finally{
1165
+ try {
1166
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1167
+ _iterator.return();
1168
+ }
1169
+ } finally{
1170
+ if (_didIteratorError) {
1171
+ throw _iteratorError;
1172
+ }
1173
+ }
1174
+ }
1175
+ }
1176
+ function teardownAdEventListeners() {
1177
+ if (adVideoElement) {
1178
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1179
+ try {
1180
+ for(var _iterator = adEventHandlers[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1181
+ var _step_value = _step.value, type = _step_value.type, handler = _step_value.handler;
1182
+ adVideoElement.removeEventListener(type, handler);
1183
+ }
1184
+ } catch (err) {
1185
+ _didIteratorError = true;
1186
+ _iteratorError = err;
1187
+ } finally{
1188
+ try {
1189
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1190
+ _iterator.return();
1191
+ }
1192
+ } finally{
1193
+ if (_didIteratorError) {
1194
+ throw _iteratorError;
1195
+ }
1196
+ }
1197
+ }
1198
+ }
1199
+ adEventHandlers = [];
1120
1200
  }
1121
1201
  function setAdPlayingFlag(isPlaying) {
1122
1202
  if (isPlaying) {
@@ -1256,16 +1336,32 @@ function createHlsAdPlayer(contentVideo, options) {
1256
1336
  }
1257
1337
  }, STALL_CHECK_INTERVAL_MS);
1258
1338
  }
1339
+ function releaseAdDecoder() {
1340
+ if (adHls) {
1341
+ adHls.destroy();
1342
+ adHls = void 0;
1343
+ }
1344
+ if (adVideoElement) {
1345
+ try {
1346
+ adVideoElement.pause();
1347
+ adVideoElement.removeAttribute("src");
1348
+ adVideoElement.load();
1349
+ } catch (error) {
1350
+ console.warn("[HlsAdPlayer] Error releasing ad decoder:", error);
1351
+ }
1352
+ }
1353
+ if (adContainerEl) {
1354
+ adContainerEl.style.display = "none";
1355
+ adContainerEl.style.pointerEvents = "none";
1356
+ }
1357
+ }
1259
1358
  function handleAdComplete() {
1260
1359
  console.log("[HlsAdPlayer] Handling ad completion");
1261
1360
  clearStallWatchdog();
1262
1361
  adPlaying = false;
1263
1362
  setAdPlayingFlag(false);
1264
1363
  contentVideo.muted = true;
1265
- if (adContainerEl) {
1266
- adContainerEl.style.display = "none";
1267
- adContainerEl.style.pointerEvents = "none";
1268
- }
1364
+ releaseAdDecoder();
1269
1365
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1270
1366
  contentVideo.style.visibility = "visible";
1271
1367
  contentVideo.style.opacity = "1";
@@ -1288,10 +1384,7 @@ function createHlsAdPlayer(contentVideo, options) {
1288
1384
  adPlaying = false;
1289
1385
  setAdPlayingFlag(false);
1290
1386
  contentVideo.muted = true;
1291
- if (adContainerEl) {
1292
- adContainerEl.style.display = "none";
1293
- adContainerEl.style.pointerEvents = "none";
1294
- }
1387
+ releaseAdDecoder();
1295
1388
  if (!(options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds)) {
1296
1389
  contentVideo.style.visibility = "visible";
1297
1390
  contentVideo.style.opacity = "1";
@@ -1318,6 +1411,7 @@ function createHlsAdPlayer(contentVideo, options) {
1318
1411
  (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1319
1412
  adContainerEl = container;
1320
1413
  }
1414
+ contentVideo[AD_CONTAINER_PROP] = adContainerEl;
1321
1415
  },
1322
1416
  requestAds: function requestAds(vastTagUrl) {
1323
1417
  return _async_to_generator(function() {
@@ -1420,8 +1514,9 @@ function createHlsAdPlayer(contentVideo, options) {
1420
1514
  if (!adVideoElement) {
1421
1515
  adVideoElement = createAdVideoElement();
1422
1516
  adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.appendChild(adVideoElement);
1423
- setupAdEventListeners();
1517
+ contentVideo[AD_VIDEO_PROP] = adVideoElement;
1424
1518
  }
1519
+ setupAdEventListeners();
1425
1520
  resetQuartileTracking(true);
1426
1521
  contentVolume = contentVideo.volume;
1427
1522
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
@@ -1500,10 +1595,7 @@ function createHlsAdPlayer(contentVideo, options) {
1500
1595
  contentVideo.muted = originalMutedState;
1501
1596
  contentVideo.volume = Math.max(0, Math.min(1, originalVolume));
1502
1597
  console.log("[HlsAdPlayer] Restored mute state on stop: ".concat(previousMutedState, " -> ").concat(originalMutedState, ", volume: ").concat(contentVideo.volume));
1503
- if (adContainerEl) {
1504
- adContainerEl.style.display = "none";
1505
- adContainerEl.style.pointerEvents = "none";
1506
- }
1598
+ releaseAdDecoder();
1507
1599
  contentVideo.style.visibility = "visible";
1508
1600
  contentVideo.style.opacity = "1";
1509
1601
  if (options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) {
@@ -1514,17 +1606,6 @@ function createHlsAdPlayer(contentVideo, options) {
1514
1606
  });
1515
1607
  }
1516
1608
  }
1517
- if (adHls) {
1518
- adHls.destroy();
1519
- adHls = void 0;
1520
- }
1521
- if (adVideoElement) {
1522
- adVideoElement.pause();
1523
- adVideoElement.removeAttribute("src");
1524
- try {
1525
- adVideoElement.load();
1526
- } catch (unused) {}
1527
- }
1528
1609
  currentAd = void 0;
1529
1610
  podAds = [];
1530
1611
  podIndex = 0;
@@ -1563,23 +1644,8 @@ function createHlsAdPlayer(contentVideo, options) {
1563
1644
  setAdPlayingFlag(false);
1564
1645
  contentVideo.muted = originalMutedState;
1565
1646
  contentVideo.volume = Math.max(0, Math.min(1, originalVolume));
1566
- if (adHls) {
1567
- adHls.destroy();
1568
- adHls = void 0;
1569
- }
1570
- if (adVideoElement) {
1571
- adVideoElement.pause();
1572
- adVideoElement.removeAttribute("src");
1573
- try {
1574
- adVideoElement.load();
1575
- } catch (unused) {}
1576
- adVideoElement.remove();
1577
- adVideoElement = void 0;
1578
- }
1579
- if (adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.parentElement) {
1580
- adContainerEl.parentElement.removeChild(adContainerEl);
1581
- }
1582
- adContainerEl = void 0;
1647
+ teardownAdEventListeners();
1648
+ releaseAdDecoder();
1583
1649
  currentAd = void 0;
1584
1650
  podAds = [];
1585
1651
  podIndex = 0;
@@ -3992,6 +4058,11 @@ function detectBrowser() {
3992
4058
  chromeVersion: chromeVersionNum
3993
4059
  };
3994
4060
  }
4061
+ function requiresMediaPipelineResetAfterAds() {
4062
+ var _browser_tizenVersion;
4063
+ var browser = detectBrowser();
4064
+ return browser.name === "Samsung Tizen" && ((_browser_tizenVersion = browser.tizenVersion) !== null && _browser_tizenVersion !== void 0 ? _browser_tizenVersion : 0) >= 5;
4065
+ }
3995
4066
  function getBrowserConfigOverrides() {
3996
4067
  var browser = detectBrowser();
3997
4068
  var overrides = {};
@@ -6735,31 +6806,67 @@ var HlsEngine = /*#__PURE__*/ function() {
6735
6806
  }
6736
6807
  }
6737
6808
  },
6809
+ {
6810
+ key: "recoverMediaError",
6811
+ value: function recoverMediaError() {
6812
+ if (!this.hls) {
6813
+ return;
6814
+ }
6815
+ try {
6816
+ this.hls.recoverMediaError();
6817
+ } catch (error) {
6818
+ if (this.debug) {
6819
+ console.warn("[StormcloudVideoPlayer] recoverMediaError() failed:", error);
6820
+ }
6821
+ }
6822
+ }
6823
+ },
6738
6824
  {
6739
6825
  key: "reloadLiveStream",
6740
6826
  value: function reloadLiveStream() {
6827
+ var _this = this;
6741
6828
  var hls = this.hls;
6742
6829
  if (!hls) {
6743
6830
  return;
6744
6831
  }
6745
6832
  if (this.debug) {
6746
- console.log("[StormcloudVideoPlayer] Hard-reloading live stream (detach \u2192 reset video \u2192 re-attach)");
6833
+ console.log("[StormcloudVideoPlayer] Hard-reloading live stream (destroy hls \u2192 reset video \u2192 fresh setupHls)");
6747
6834
  }
6748
6835
  try {
6749
6836
  this.reloadingLiveStream = true;
6750
6837
  hls.stopLoad();
6751
6838
  hls.detachMedia();
6839
+ hls.destroy();
6840
+ this.hls = void 0;
6752
6841
  try {
6753
6842
  this.video.removeAttribute("src");
6754
6843
  this.video.load();
6755
- } catch (unused) {}
6756
- hls.attachMedia(this.video);
6844
+ } catch (error) {
6845
+ console.warn("[StormcloudVideoPlayer] video.load() threw after src removal:", error);
6846
+ }
6757
6847
  } catch (error) {
6758
6848
  this.reloadingLiveStream = false;
6849
+ this.hls = void 0;
6759
6850
  if (this.debug) {
6760
- console.warn("[StormcloudVideoPlayer] Failed to hard-reload live stream:", error);
6851
+ console.warn("[StormcloudVideoPlayer] Failed to tear down during hard-reload:", error);
6761
6852
  }
6853
+ return;
6762
6854
  }
6855
+ var recreateDelayMs = 600;
6856
+ window.setTimeout(function() {
6857
+ if (_this.hls) {
6858
+ _this.reloadingLiveStream = false;
6859
+ return;
6860
+ }
6861
+ try {
6862
+ _this.setupHls();
6863
+ } catch (error) {
6864
+ _this.reloadingLiveStream = false;
6865
+ if (_this.debug) {
6866
+ console.warn("[StormcloudVideoPlayer] Failed to rebuild pipeline during hard-reload:", error);
6867
+ }
6868
+ }
6869
+ }, recreateDelayMs);
6763
6870
  }
6764
6871
  },
6765
6872
  {
@@ -8072,6 +8179,14 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8072
8179
  this.host.video.style.visibility = "visible";
8073
8180
  this.host.video.style.opacity = "1";
8074
8181
  if (this.host.isLiveStream()) {
8182
+ if (this.host.requiresMediaPipelineResetAfterAds()) {
8183
+ if (this.debug) {
8184
+ console.log("[StormcloudVideoPlayer] Live break end \u2014 hard-reloading stream to reacquire decoder (single-decoder platform)");
8185
+ }
8186
+ this.host.reloadLiveStream();
8187
+ this.monitorLiveResumeStall(this.host.video.currentTime);
8188
+ return;
8189
+ }
8075
8190
  var liveSyncPos = this.host.getLiveSyncPosition();
8076
8191
  if (liveSyncPos != null && liveSyncPos > this.host.video.currentTime) {
8077
8192
  if (this.debug) {
@@ -8133,11 +8248,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8133
8248
  key: "monitorLiveResumeStall",
8134
8249
  value: function monitorLiveResumeStall(resumeBaseline) {
8135
8250
  var _this = this;
8251
+ var needsReset = this.host.requiresMediaPipelineResetAfterAds();
8136
8252
  var pollIntervalMs = 1e3;
8137
- var graceMs = 5e3;
8138
- var recoveryCooldownMs = 5e3;
8139
- var maxRecoveries = 2;
8140
- var maxMonitorMs = 3e4;
8253
+ var graceMs = needsReset ? 8e3 : 5e3;
8254
+ var recoveryCooldownMs = needsReset ? 7e3 : 5e3;
8255
+ var maxRecoveries = 3;
8256
+ var maxMonitorMs = 45e3;
8141
8257
  var startTime = Date.now();
8142
8258
  var baseline = resumeBaseline;
8143
8259
  var recoveries = 0;
@@ -8249,8 +8365,17 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8249
8365
  if (canRecover) {
8250
8366
  recoveries++;
8251
8367
  lastRecoveryAt = Date.now();
8252
- var isFinalAttempt = recoveries >= maxRecoveries;
8253
- if (!isFinalAttempt) {
8368
+ if (needsReset) {
8369
+ if (_this.debug) {
8370
+ console.warn("[StormcloudVideoPlayer] Content still wedged after ad break — hard-reloading live stream (recovery ".concat(recoveries, "/").concat(maxRecoveries, ") — ").concat(_this.describeVideoState()));
8371
+ }
8372
+ _this.host.reloadLiveStream();
8373
+ } else if (recoveries === 1) {
8374
+ if (_this.debug) {
8375
+ console.warn("[StormcloudVideoPlayer] Content wedged after ad break — calling recoverMediaError() (recovery ".concat(recoveries, "/").concat(maxRecoveries, ") — ").concat(_this.describeVideoState()));
8376
+ }
8377
+ _this.host.recoverMediaError();
8378
+ } else if (recoveries === 2) {
8254
8379
  var liveSyncPos = _this.host.getLiveSyncPosition();
8255
8380
  var reloadPos = liveSyncPos != null && liveSyncPos > currentTime ? liveSyncPos : currentTime;
8256
8381
  if (_this.debug) {
@@ -8282,17 +8407,41 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
8282
8407
  value: function resumeContentPlayback() {
8283
8408
  var _this = this;
8284
8409
  var attempt = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 0;
8285
- var maxAttempts = 3;
8286
- if (!this.host.video.paused) {
8410
+ var maxAttempts = 5;
8411
+ var video = this.host.video;
8412
+ if (!video.paused) {
8287
8413
  if (this.debug && attempt === 0) {
8288
8414
  console.log("[StormcloudVideoPlayer] Content video already playing after ads");
8289
8415
  }
8290
8416
  return;
8291
8417
  }
8292
8418
  if (this.debug) {
8293
- console.log("[StormcloudVideoPlayer] Resuming content playback after ads (attempt ".concat(attempt + 1, "/").concat(maxAttempts, ")"));
8419
+ console.log("[StormcloudVideoPlayer] Resuming content playback after ads (attempt ".concat(attempt + 1, "/").concat(maxAttempts, ", readyState=").concat(video.readyState, ")"));
8294
8420
  }
8295
- var playResult = this.host.video.play();
8421
+ if (video.readyState < 3) {
8422
+ if (attempt + 1 >= maxAttempts) {
8423
+ var _video_play;
8424
+ if (this.debug) {
8425
+ console.warn("[StormcloudVideoPlayer] readyState never reached HAVE_FUTURE_DATA, forcing play()");
8426
+ }
8427
+ (_video_play = video.play()) === null || _video_play === void 0 ? void 0 : _video_play.catch(function() {});
8428
+ return;
8429
+ }
8430
+ var onReady = function onReady1() {
8431
+ video.removeEventListener("canplay", onReady);
8432
+ _this.resumeContentPlayback(attempt + 1);
8433
+ };
8434
+ video.addEventListener("canplay", onReady);
8435
+ var backoffMs = 500 * (attempt + 1);
8436
+ window.setTimeout(function() {
8437
+ video.removeEventListener("canplay", onReady);
8438
+ if (video.paused) {
8439
+ _this.resumeContentPlayback(attempt + 1);
8440
+ }
8441
+ }, backoffMs);
8442
+ return;
8443
+ }
8444
+ var playResult = video.play();
8296
8445
  if (playResult && typeof playResult.catch === "function") {
8297
8446
  playResult.catch(function(error) {
8298
8447
  if (attempt + 1 >= maxAttempts) {
@@ -8431,6 +8580,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
8431
8580
  reloadLiveStream: function reloadLiveStream() {
8432
8581
  return _this.hlsEngine.reloadLiveStream();
8433
8582
  },
8583
+ recoverMediaError: function recoverMediaError() {
8584
+ return _this.hlsEngine.recoverMediaError();
8585
+ },
8586
+ requiresMediaPipelineResetAfterAds: function requiresMediaPipelineResetAfterAds1() {
8587
+ return requiresMediaPipelineResetAfterAds();
8588
+ },
8434
8589
  generatePodVastUrl: function generatePodVastUrl(base, breakDurationMs) {
8435
8590
  return _this.generatePodVastUrl(base, breakDurationMs);
8436
8591
  }