stormcloud-video-player 0.6.1 → 0.6.3

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.
package/lib/index.cjs CHANGED
@@ -1104,7 +1104,7 @@ function resolveBidToVastAd(winner, logPrefix) {
1104
1104
  return Promise.resolve(null);
1105
1105
  }
1106
1106
  function createVastAdLayer(contentVideo, options) {
1107
- var _ref, _ref1, _ref2, _ref3;
1107
+ var _ref, _ref1, _ref2, _ref3, _ref4;
1108
1108
  var adPlaying = false;
1109
1109
  var originalMutedState = false;
1110
1110
  var originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));
@@ -1113,7 +1113,8 @@ function createVastAdLayer(contentVideo, options) {
1113
1113
  var continueLiveStreamDuringAds = (_ref = options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) !== null && _ref !== void 0 ? _ref : false;
1114
1114
  var smartTVMode = (_ref1 = options === null || options === void 0 ? void 0 : options.smartTVMode) !== null && _ref1 !== void 0 ? _ref1 : false;
1115
1115
  var singleElementMode = (_ref2 = options === null || options === void 0 ? void 0 : options.singleElementMode) !== null && _ref2 !== void 0 ? _ref2 : false;
1116
- var debug = (_ref3 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref3 !== void 0 ? _ref3 : false;
1116
+ var forceMP4Ads = (_ref3 = options === null || options === void 0 ? void 0 : options.forceMP4Ads) !== null && _ref3 !== void 0 ? _ref3 : smartTVMode || singleElementMode;
1117
+ var debug = (_ref4 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref4 !== void 0 ? _ref4 : false;
1117
1118
  var adVideoElement;
1118
1119
  var adHls;
1119
1120
  var adContainerEl;
@@ -1184,14 +1185,26 @@ function createVastAdLayer(contentVideo, options) {
1184
1185
  var _ref;
1185
1186
  var _scoredFiles_;
1186
1187
  if (mediaFiles.length === 0) throw new Error("No media files available");
1187
- var firstFile = mediaFiles[0];
1188
- if (mediaFiles.length === 1) return firstFile;
1188
+ var candidates = mediaFiles;
1189
+ if (forceMP4Ads) {
1190
+ var mp4Only = candidates.filter(function(f) {
1191
+ return !isHlsMediaFile(f);
1192
+ });
1193
+ if (mp4Only.length > 0) {
1194
+ candidates = mp4Only;
1195
+ if (debug) console.log("".concat(LOG, " forceMP4Ads: filtered to ").concat(mp4Only.length, " MP4-only file(s)"));
1196
+ } else if (debug) {
1197
+ console.warn("".concat(LOG, " forceMP4Ads: no MP4 files available, falling back to all media files"));
1198
+ }
1199
+ }
1200
+ var firstFile = candidates[0];
1201
+ if (candidates.length === 1) return firstFile;
1189
1202
  var mainQuality = getMainStreamQuality();
1190
1203
  if (!mainQuality) {
1191
1204
  if (debug) console.log("".concat(LOG, " No main stream quality info, using first media file"));
1192
1205
  return firstFile;
1193
1206
  }
1194
- var scoredFiles = mediaFiles.map(function(file) {
1207
+ var scoredFiles = candidates.map(function(file) {
1195
1208
  var widthDiff = Math.abs(file.width - mainQuality.width);
1196
1209
  var heightDiff = Math.abs(file.height - mainQuality.height);
1197
1210
  var resolutionDiff = widthDiff + heightDiff;
@@ -1408,6 +1421,16 @@ function createVastAdLayer(contentVideo, options) {
1408
1421
  }
1409
1422
  function startPlayback(mediaFile) {
1410
1423
  if (!adVideoElement) return;
1424
+ if (singleElementMode && isHlsMediaFile(mediaFile)) {
1425
+ var mp4Fallback = currentAd === null || currentAd === void 0 ? void 0 : currentAd.mediaFiles.find(function(f) {
1426
+ return !isHlsMediaFile(f);
1427
+ });
1428
+ if (mp4Fallback) {
1429
+ if (debug) console.log("".concat(LOG, " singleElementMode: HLS ad blocked, using MP4 fallback"));
1430
+ startNativePlayback(mp4Fallback);
1431
+ return;
1432
+ }
1433
+ }
1411
1434
  if (isHlsMediaFile(mediaFile)) {
1412
1435
  startHlsPlayback(mediaFile);
1413
1436
  } else {
@@ -1416,7 +1439,7 @@ function createVastAdLayer(contentVideo, options) {
1416
1439
  }
1417
1440
  function playAd(bids) {
1418
1441
  return _async_to_generator(function() {
1419
- var winner, ad, contentVolume, _contentVideo_parentElement, container, adVolume, mediaFile;
1442
+ var winner, ad, contentVolume, adVolume2, mediaFile2, _contentVideo_parentElement, container, adVolume, mediaFile;
1420
1443
  return _ts_generator(this, function(_state) {
1421
1444
  switch(_state.label){
1422
1445
  case 0:
@@ -1460,37 +1483,71 @@ function createVastAdLayer(contentVideo, options) {
1460
1483
  trackingFired.impression = true;
1461
1484
  contentVolume = contentVideo.volume;
1462
1485
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1463
- if (singleElementMode) {
1464
- mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1465
- teardownCurrentPlayback();
1466
- adVideoElement = contentVideo;
1467
- adHls = void 0;
1486
+ if (!singleElementMode) return [
1487
+ 3,
1488
+ 3
1489
+ ];
1490
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1491
+ teardownCurrentPlayback();
1492
+ adVideoElement = contentVideo;
1493
+ adHls = void 0;
1494
+ adPlaying = true;
1495
+ setAdPlayingFlag(true);
1496
+ contentVideo.removeAttribute("src");
1497
+ contentVideo.load();
1498
+ if (!continueLiveStreamDuringAds) {
1499
+ contentVideo.pause();
1500
+ }
1501
+ contentVideo.muted = true;
1502
+ contentVideo.volume = 0;
1503
+ return [
1504
+ 4,
1505
+ new Promise(function(resolve) {
1506
+ return setTimeout(resolve, 200);
1507
+ })
1508
+ ];
1509
+ case 2:
1510
+ _state.sent();
1511
+ if (destroyed || tornDown) return [
1512
+ 2
1513
+ ];
1514
+ contentVideo.style.visibility = "visible";
1515
+ contentVideo.style.opacity = "1";
1516
+ emit("content_pause");
1517
+ setupAdEventListeners();
1518
+ adVolume2 = originalMutedState ? 1 : originalVolume;
1519
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume2));
1520
+ adVideoElement.muted = false;
1521
+ mediaFile2 = selectBestMediaFile(ad.mediaFiles);
1522
+ if (debug) console.log("".concat(LOG, " Loading ad from: ").concat(mediaFile2.url));
1523
+ startPlayback(mediaFile2);
1524
+ return [
1525
+ 2
1526
+ ];
1527
+ case 3:
1528
+ if (!adContainerEl) {
1529
+ ;
1530
+ container = document.createElement("div");
1531
+ container.style.position = "absolute";
1532
+ container.style.left = "0";
1533
+ container.style.top = "0";
1534
+ container.style.right = "0";
1535
+ container.style.bottom = "0";
1536
+ container.style.display = "none";
1537
+ container.style.alignItems = "center";
1538
+ container.style.justifyContent = "center";
1539
+ container.style.pointerEvents = "none";
1540
+ container.style.zIndex = "10";
1541
+ container.style.backgroundColor = "#000";
1542
+ (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1543
+ adContainerEl = container;
1544
+ }
1545
+ if (!adVideoElement) {
1546
+ adVideoElement = createAdVideoElement();
1547
+ adContainerEl.appendChild(adVideoElement);
1468
1548
  setupAdEventListeners();
1469
1549
  } else {
1470
- if (!adContainerEl) {
1471
- ;
1472
- container = document.createElement("div");
1473
- container.style.position = "absolute";
1474
- container.style.left = "0";
1475
- container.style.top = "0";
1476
- container.style.right = "0";
1477
- container.style.bottom = "0";
1478
- container.style.display = "none";
1479
- container.style.alignItems = "center";
1480
- container.style.justifyContent = "center";
1481
- container.style.pointerEvents = "none";
1482
- container.style.zIndex = "10";
1483
- container.style.backgroundColor = "#000";
1484
- (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1485
- adContainerEl = container;
1486
- }
1487
- if (!adVideoElement) {
1488
- adVideoElement = createAdVideoElement();
1489
- adContainerEl.appendChild(adVideoElement);
1490
- setupAdEventListeners();
1491
- } else {
1492
- teardownCurrentPlayback();
1493
- }
1550
+ teardownCurrentPlayback();
1494
1551
  }
1495
1552
  if (!continueLiveStreamDuringAds) {
1496
1553
  contentVideo.pause();
@@ -1502,7 +1559,7 @@ function createVastAdLayer(contentVideo, options) {
1502
1559
  adVolume = originalMutedState ? 1 : originalVolume;
1503
1560
  adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1504
1561
  adVideoElement.muted = false;
1505
- if (!singleElementMode && adContainerEl) {
1562
+ if (adContainerEl) {
1506
1563
  adContainerEl.style.display = "flex";
1507
1564
  adContainerEl.style.pointerEvents = "auto";
1508
1565
  }
@@ -1606,6 +1663,7 @@ function createVastAdLayer(contentVideo, options) {
1606
1663
  if (debug) console.log("".concat(LOG, " [preload] HLS manifest parsed, token=").concat(token));
1607
1664
  });
1608
1665
  hls.on(import_hls.default.Events.ERROR, function(_evt, data) {
1666
+ if (!preloadSlots.has(token)) return;
1609
1667
  if (data.fatal) {
1610
1668
  if (debug) console.warn("".concat(LOG, " [preload] HLS error for token=").concat(token));
1611
1669
  preloadSlots.delete(token);
@@ -1642,113 +1700,146 @@ function createVastAdLayer(contentVideo, options) {
1642
1700
  }
1643
1701
  function playPreloaded(token) {
1644
1702
  return _async_to_generator(function() {
1645
- var slot, contentVolume, adVolume2, videoEl, container2, adVolume21, adVolume, container;
1703
+ var slot, contentVolume, adVolume2, videoEl, container2, adVolume21, nonFatalNetworkErrors, adVolume, container;
1646
1704
  return _ts_generator(this, function(_state) {
1647
- if (destroyed) return [
1648
- 2,
1649
- Promise.reject(new Error("Layer has been destroyed"))
1650
- ];
1651
- slot = preloadSlots.get(token);
1652
- if (!slot) {
1653
- if (debug) console.warn("".concat(LOG, " [preload] No slot found for token=").concat(token, ", nothing to play"));
1654
- return [
1655
- 2
1656
- ];
1657
- }
1658
- preloadSlots.delete(token);
1659
- if (debug) console.log("".concat(LOG, " [preload] Playing preloaded ad, token=").concat(token, ", ready=").concat(slot.ready));
1660
- contentVolume = contentVideo.volume;
1661
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1662
- sessionId = generateSessionId();
1663
- currentAd = slot.ad;
1664
- trackingFired = _object_spread({}, createEmptyTrackingState());
1665
- fireTrackingPixels2(slot.ad.trackingUrls.impression);
1666
- trackingFired.impression = true;
1667
- if (singleElementMode) {
1668
- mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1669
- teardownCurrentPlayback();
1670
- adVideoElement = contentVideo;
1671
- adHls = void 0;
1672
- setupAdEventListeners();
1673
- if (!continueLiveStreamDuringAds) {
1674
- contentVideo.pause();
1675
- }
1676
- contentVideo.muted = true;
1677
- contentVideo.volume = 0;
1678
- adPlaying = true;
1679
- setAdPlayingFlag(true);
1680
- adVolume2 = originalMutedState ? 1 : originalVolume;
1681
- contentVideo.volume = Math.max(0, Math.min(1, adVolume2));
1682
- contentVideo.muted = false;
1683
- emit("content_pause");
1684
- if (debug) console.log("".concat(LOG, " [preload] singleElementMode: attaching ad to contentVideo, url=").concat(slot.mediaFile.url));
1685
- startPlayback(slot.mediaFile);
1686
- return [
1687
- 2
1688
- ];
1689
- }
1690
- if (smartTVMode && !slot.videoEl) {
1691
- teardownCurrentPlayback();
1692
- if (adVideoElement) {
1693
- adVideoElement.remove();
1694
- adVideoElement = void 0;
1695
- }
1696
- videoEl = createAdVideoElement();
1697
- videoEl.style.visibility = "visible";
1698
- videoEl.style.pointerEvents = "none";
1699
- container2 = ensureAdContainer();
1700
- container2.appendChild(videoEl);
1701
- adVideoElement = videoEl;
1702
- setupAdEventListeners();
1703
- if (!continueLiveStreamDuringAds) {
1704
- contentVideo.pause();
1705
- }
1706
- contentVideo.muted = true;
1707
- contentVideo.volume = 0;
1708
- adPlaying = true;
1709
- setAdPlayingFlag(true);
1710
- adVolume21 = originalMutedState ? 1 : originalVolume;
1711
- adVideoElement.volume = Math.max(0, Math.min(1, adVolume21));
1712
- adVideoElement.muted = false;
1713
- container2.style.display = "flex";
1714
- container2.style.pointerEvents = "auto";
1715
- emit("content_pause");
1716
- if (debug) console.log("".concat(LOG, " [preload] smartTVMode deferred: creating video element and loading, url=").concat(slot.mediaFile.url));
1717
- startPlayback(slot.mediaFile);
1718
- return [
1719
- 2
1720
- ];
1721
- }
1722
- teardownCurrentPlayback();
1723
- if (adVideoElement && adVideoElement !== slot.videoEl) {
1724
- adVideoElement.remove();
1705
+ switch(_state.label){
1706
+ case 0:
1707
+ if (destroyed) return [
1708
+ 2,
1709
+ Promise.reject(new Error("Layer has been destroyed"))
1710
+ ];
1711
+ slot = preloadSlots.get(token);
1712
+ if (!slot) {
1713
+ if (debug) console.warn("".concat(LOG, " [preload] No slot found for token=").concat(token, ", nothing to play"));
1714
+ return [
1715
+ 2
1716
+ ];
1717
+ }
1718
+ preloadSlots.delete(token);
1719
+ if (debug) console.log("".concat(LOG, " [preload] Playing preloaded ad, token=").concat(token, ", ready=").concat(slot.ready));
1720
+ contentVolume = contentVideo.volume;
1721
+ originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1722
+ sessionId = generateSessionId();
1723
+ currentAd = slot.ad;
1724
+ trackingFired = _object_spread({}, createEmptyTrackingState());
1725
+ fireTrackingPixels2(slot.ad.trackingUrls.impression);
1726
+ trackingFired.impression = true;
1727
+ if (!singleElementMode) return [
1728
+ 3,
1729
+ 2
1730
+ ];
1731
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1732
+ teardownCurrentPlayback();
1733
+ adVideoElement = contentVideo;
1734
+ adHls = void 0;
1735
+ adPlaying = true;
1736
+ setAdPlayingFlag(true);
1737
+ contentVideo.removeAttribute("src");
1738
+ contentVideo.load();
1739
+ contentVideo.muted = true;
1740
+ contentVideo.volume = 0;
1741
+ return [
1742
+ 4,
1743
+ new Promise(function(resolve) {
1744
+ return setTimeout(resolve, 200);
1745
+ })
1746
+ ];
1747
+ case 1:
1748
+ _state.sent();
1749
+ if (destroyed || tornDown) return [
1750
+ 2
1751
+ ];
1752
+ contentVideo.style.visibility = "visible";
1753
+ contentVideo.style.opacity = "1";
1754
+ emit("content_pause");
1755
+ setupAdEventListeners();
1756
+ adVolume2 = originalMutedState ? 1 : originalVolume;
1757
+ contentVideo.volume = Math.max(0, Math.min(1, adVolume2));
1758
+ contentVideo.muted = false;
1759
+ if (debug) console.log("".concat(LOG, " [preload] singleElementMode: attaching ad to contentVideo, url=").concat(slot.mediaFile.url));
1760
+ startPlayback(slot.mediaFile);
1761
+ return [
1762
+ 2
1763
+ ];
1764
+ case 2:
1765
+ if (smartTVMode && !slot.videoEl) {
1766
+ teardownCurrentPlayback();
1767
+ if (adVideoElement) {
1768
+ adVideoElement.remove();
1769
+ adVideoElement = void 0;
1770
+ }
1771
+ videoEl = createAdVideoElement();
1772
+ videoEl.style.visibility = "visible";
1773
+ videoEl.style.pointerEvents = "none";
1774
+ container2 = ensureAdContainer();
1775
+ container2.appendChild(videoEl);
1776
+ adVideoElement = videoEl;
1777
+ setupAdEventListeners();
1778
+ if (!continueLiveStreamDuringAds) {
1779
+ contentVideo.pause();
1780
+ }
1781
+ contentVideo.muted = true;
1782
+ contentVideo.volume = 0;
1783
+ adPlaying = true;
1784
+ setAdPlayingFlag(true);
1785
+ adVolume21 = originalMutedState ? 1 : originalVolume;
1786
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume21));
1787
+ adVideoElement.muted = false;
1788
+ container2.style.display = "flex";
1789
+ container2.style.pointerEvents = "auto";
1790
+ emit("content_pause");
1791
+ if (debug) console.log("".concat(LOG, " [preload] smartTVMode deferred: creating video element and loading, url=").concat(slot.mediaFile.url));
1792
+ startPlayback(slot.mediaFile);
1793
+ return [
1794
+ 2
1795
+ ];
1796
+ }
1797
+ teardownCurrentPlayback();
1798
+ if (adVideoElement && adVideoElement !== slot.videoEl) {
1799
+ adVideoElement.remove();
1800
+ }
1801
+ slot.videoEl.style.visibility = "visible";
1802
+ slot.videoEl.style.pointerEvents = "none";
1803
+ adVideoElement = slot.videoEl;
1804
+ adHls = slot.hlsInstance;
1805
+ if (adHls) {
1806
+ nonFatalNetworkErrors = 0;
1807
+ adHls.on(import_hls.default.Events.ERROR, function(_event, data) {
1808
+ if (!adPlaying) return;
1809
+ if (data.fatal) {
1810
+ handleAdError();
1811
+ } else if (data.type === import_hls.default.ErrorTypes.NETWORK_ERROR) {
1812
+ nonFatalNetworkErrors++;
1813
+ if (nonFatalNetworkErrors >= 3) {
1814
+ if (debug) console.warn("".concat(LOG, " [preload] Too many non-fatal HLS network errors during playback, treating as fatal"));
1815
+ handleAdError();
1816
+ }
1817
+ }
1818
+ });
1819
+ }
1820
+ setupAdEventListeners();
1821
+ if (!continueLiveStreamDuringAds) {
1822
+ contentVideo.pause();
1823
+ }
1824
+ contentVideo.muted = true;
1825
+ contentVideo.volume = 0;
1826
+ adPlaying = true;
1827
+ setAdPlayingFlag(true);
1828
+ adVolume = originalMutedState ? 1 : originalVolume;
1829
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1830
+ adVideoElement.muted = false;
1831
+ container = ensureAdContainer();
1832
+ container.style.display = "flex";
1833
+ container.style.pointerEvents = "auto";
1834
+ emit("content_pause");
1835
+ adVideoElement.play().catch(function(error) {
1836
+ console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1837
+ handleAdError();
1838
+ });
1839
+ return [
1840
+ 2
1841
+ ];
1725
1842
  }
1726
- slot.videoEl.style.visibility = "visible";
1727
- slot.videoEl.style.pointerEvents = "none";
1728
- adVideoElement = slot.videoEl;
1729
- adHls = slot.hlsInstance;
1730
- setupAdEventListeners();
1731
- if (!continueLiveStreamDuringAds) {
1732
- contentVideo.pause();
1733
- }
1734
- contentVideo.muted = true;
1735
- contentVideo.volume = 0;
1736
- adPlaying = true;
1737
- setAdPlayingFlag(true);
1738
- adVolume = originalMutedState ? 1 : originalVolume;
1739
- adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1740
- adVideoElement.muted = false;
1741
- container = ensureAdContainer();
1742
- container.style.display = "flex";
1743
- container.style.pointerEvents = "auto";
1744
- emit("content_pause");
1745
- adVideoElement.play().catch(function(error) {
1746
- console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1747
- handleAdError();
1748
- });
1749
- return [
1750
- 2
1751
- ];
1752
1843
  });
1753
1844
  })();
1754
1845
  }
@@ -3103,10 +3194,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3103
3194
  debug: !!config.debugAdTiming
3104
3195
  } : {});
3105
3196
  var browserForAdLayer = detectBrowser();
3197
+ var isSinglePipeline = browserForAdLayer.isSmartTV || !!this.config.singlePipelineMode;
3106
3198
  this.adLayer = createVastAdLayer(this.video, {
3107
3199
  continueLiveStreamDuringAds: false,
3108
- smartTVMode: browserForAdLayer.isSmartTV,
3109
- singleElementMode: browserForAdLayer.isSmartTV,
3200
+ smartTVMode: isSinglePipeline,
3201
+ singleElementMode: isSinglePipeline,
3202
+ forceMP4Ads: isSinglePipeline,
3110
3203
  debug: !!config.debugAdTiming
3111
3204
  });
3112
3205
  }
@@ -3752,6 +3845,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3752
3845
  if (_this.fillerVideo) {
3753
3846
  _this.fillerVideo.style.display = "none";
3754
3847
  }
3848
+ if (!_this.adLayer.isAdPlaying()) {
3849
+ if (_this.config.debugAdTiming) {
3850
+ console.log("[StormcloudVideoPlayer] Filler video failed \u2014 restoring main video");
3851
+ }
3852
+ _this.adLayer.hidePlaceholder();
3853
+ if (_this.video.paused && _this.video.readyState >= 2) {
3854
+ var _this_video_play;
3855
+ (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
3856
+ }
3857
+ }
3755
3858
  });
3756
3859
  if (this.config.debugAdTiming) {
3757
3860
  console.log("[StormcloudVideoPlayer] Showing filler video layer");
@@ -4017,7 +4120,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4017
4120
  };
4018
4121
  this.adLayer.updateOriginalMutedState(this.video.muted, this.video.volume);
4019
4122
  }
4020
- if (!this.config.disableFiller && !this.video.muted) {
4123
+ if (!this.config.disableFiller && !this.video.muted && !this.adLayer.isAdPlaying()) {
4021
4124
  this.video.muted = true;
4022
4125
  this.video.volume = 0;
4023
4126
  if (this.config.debugAdTiming) {
@@ -4468,6 +4571,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4468
4571
  if (!this.isLiveStream) {
4469
4572
  return false;
4470
4573
  }
4574
+ if (this.config.singlePipelineMode) {
4575
+ return false;
4576
+ }
4471
4577
  var browser = detectBrowser();
4472
4578
  if (browser.isSmartTV) {
4473
4579
  return false;
@@ -5618,32 +5724,55 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5618
5724
  this.video.volume = restoredVolume;
5619
5725
  }
5620
5726
  var browser = detectBrowser();
5621
- var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0;
5727
+ var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0 || !!this.config.singlePipelineMode;
5622
5728
  if (isSmartTV && this.hls) {
5623
5729
  var hlsRef = this.hls;
5624
5730
  var savedMuted = restoredMuted;
5625
5731
  var savedVolume = restoredVolume;
5732
+ var videoRef = this.video;
5733
+ var debugEnabled = this.config.debugAdTiming;
5734
+ var tryPlay = function tryPlay1(attempt) {
5735
+ var _videoRef_play;
5736
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5737
+ (_videoRef_play = videoRef.play()) === null || _videoRef_play === void 0 ? void 0 : _videoRef_play.catch(function() {
5738
+ if (attempt < 3) {
5739
+ if (debugEnabled) {
5740
+ console.log("[StormcloudVideoPlayer] Smart TV: play() retry ".concat(attempt + 1, "/3 in ").concat(500 * (attempt + 1), "ms"));
5741
+ }
5742
+ setTimeout(function() {
5743
+ return tryPlay(attempt + 1);
5744
+ }, 500 * (attempt + 1));
5745
+ }
5746
+ });
5747
+ };
5626
5748
  var onManifestParsedRestore = function onManifestParsedRestore1() {
5627
5749
  hlsRef.off(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5628
5750
  if (!_this.inAdBreak && !_this.adLayer.isAdPlaying()) {
5629
- var _this_video_play;
5630
- if (_this.video.muted !== savedMuted) _this.video.muted = savedMuted;
5631
- if (Math.abs(_this.video.volume - savedVolume) > 0.01) _this.video.volume = savedVolume;
5632
- if (_this.config.debugAdTiming) {
5751
+ if (videoRef.muted !== savedMuted) videoRef.muted = savedMuted;
5752
+ if (Math.abs(videoRef.volume - savedVolume) > 0.01) videoRef.volume = savedVolume;
5753
+ if (debugEnabled) {
5633
5754
  console.log("[StormcloudVideoPlayer] Smart TV: audio state restored on MANIFEST_PARSED after re-attach");
5634
5755
  }
5635
5756
  hlsRef.startLoad(-1);
5636
- (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5637
- if (_this.config.debugAdTiming) {
5757
+ if (debugEnabled) {
5638
5758
  console.log("[StormcloudVideoPlayer] Smart TV: seeking to live edge and resuming playback after re-attach");
5639
5759
  }
5640
5760
  }
5641
5761
  };
5642
5762
  hlsRef.on(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5643
- this.hls.attachMedia(this.video);
5644
- if (this.config.debugAdTiming) {
5645
- console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5763
+ var pipelineDelayMs = 300;
5764
+ if (debugEnabled) {
5765
+ console.log("[StormcloudVideoPlayer] Smart TV: waiting ".concat(pipelineDelayMs, "ms for hardware pipeline release before re-attach"));
5646
5766
  }
5767
+ setTimeout(function() {
5768
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5769
+ if (_this.hls) {
5770
+ _this.hls.attachMedia(_this.video);
5771
+ if (debugEnabled) {
5772
+ console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5773
+ }
5774
+ }
5775
+ }, pipelineDelayMs);
5647
5776
  } else {
5648
5777
  if (this.shouldContinueLiveStreamDuringAds()) {
5649
5778
  var _this_video_play;
@@ -5717,6 +5846,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5717
5846
  }
5718
5847
  this.showPlaceholderLayer();
5719
5848
  this.adLayer.showPlaceholder();
5849
+ } else if (this.inAdBreak) {
5850
+ if (this.config.debugAdTiming) {
5851
+ console.log("[CONTINUOUS-FETCH] Ad failure with no filler \u2014 restoring main video temporarily");
5852
+ }
5853
+ this.adLayer.hidePlaceholder();
5854
+ if (!this.adLayer.isAdPlaying() && this.video.paused && this.video.readyState >= 2) {
5855
+ var _this_video_play;
5856
+ (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5857
+ }
5720
5858
  }
5721
5859
  }
5722
5860
  },