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.
@@ -1,5 +1,5 @@
1
1
  import { Component } from 'react';
2
- import { S as StormcloudVideoPlayerConfig } from '../types-CwDRIvJm.cjs';
2
+ import { S as StormcloudVideoPlayerConfig } from '../types-BYwfSJb5.cjs';
3
3
 
4
4
  interface HlsPlayerProps extends StormcloudVideoPlayerConfig {
5
5
  onMount?: (player: any) => void;
@@ -1010,7 +1010,7 @@ function resolveBidToVastAd(winner, logPrefix) {
1010
1010
  return Promise.resolve(null);
1011
1011
  }
1012
1012
  function createVastAdLayer(contentVideo, options) {
1013
- var _ref, _ref1, _ref2, _ref3;
1013
+ var _ref, _ref1, _ref2, _ref3, _ref4;
1014
1014
  var adPlaying = false;
1015
1015
  var originalMutedState = false;
1016
1016
  var originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));
@@ -1019,7 +1019,8 @@ function createVastAdLayer(contentVideo, options) {
1019
1019
  var continueLiveStreamDuringAds = (_ref = options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) !== null && _ref !== void 0 ? _ref : false;
1020
1020
  var smartTVMode = (_ref1 = options === null || options === void 0 ? void 0 : options.smartTVMode) !== null && _ref1 !== void 0 ? _ref1 : false;
1021
1021
  var singleElementMode = (_ref2 = options === null || options === void 0 ? void 0 : options.singleElementMode) !== null && _ref2 !== void 0 ? _ref2 : false;
1022
- var debug = (_ref3 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref3 !== void 0 ? _ref3 : false;
1022
+ var forceMP4Ads = (_ref3 = options === null || options === void 0 ? void 0 : options.forceMP4Ads) !== null && _ref3 !== void 0 ? _ref3 : smartTVMode || singleElementMode;
1023
+ var debug = (_ref4 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref4 !== void 0 ? _ref4 : false;
1023
1024
  var adVideoElement;
1024
1025
  var adHls;
1025
1026
  var adContainerEl;
@@ -1090,14 +1091,26 @@ function createVastAdLayer(contentVideo, options) {
1090
1091
  var _ref;
1091
1092
  var _scoredFiles_;
1092
1093
  if (mediaFiles.length === 0) throw new Error("No media files available");
1093
- var firstFile = mediaFiles[0];
1094
- if (mediaFiles.length === 1) return firstFile;
1094
+ var candidates = mediaFiles;
1095
+ if (forceMP4Ads) {
1096
+ var mp4Only = candidates.filter(function(f) {
1097
+ return !isHlsMediaFile(f);
1098
+ });
1099
+ if (mp4Only.length > 0) {
1100
+ candidates = mp4Only;
1101
+ if (debug) console.log("".concat(LOG, " forceMP4Ads: filtered to ").concat(mp4Only.length, " MP4-only file(s)"));
1102
+ } else if (debug) {
1103
+ console.warn("".concat(LOG, " forceMP4Ads: no MP4 files available, falling back to all media files"));
1104
+ }
1105
+ }
1106
+ var firstFile = candidates[0];
1107
+ if (candidates.length === 1) return firstFile;
1095
1108
  var mainQuality = getMainStreamQuality();
1096
1109
  if (!mainQuality) {
1097
1110
  if (debug) console.log("".concat(LOG, " No main stream quality info, using first media file"));
1098
1111
  return firstFile;
1099
1112
  }
1100
- var scoredFiles = mediaFiles.map(function(file) {
1113
+ var scoredFiles = candidates.map(function(file) {
1101
1114
  var widthDiff = Math.abs(file.width - mainQuality.width);
1102
1115
  var heightDiff = Math.abs(file.height - mainQuality.height);
1103
1116
  var resolutionDiff = widthDiff + heightDiff;
@@ -1314,6 +1327,16 @@ function createVastAdLayer(contentVideo, options) {
1314
1327
  }
1315
1328
  function startPlayback(mediaFile) {
1316
1329
  if (!adVideoElement) return;
1330
+ if (singleElementMode && isHlsMediaFile(mediaFile)) {
1331
+ var mp4Fallback = currentAd === null || currentAd === void 0 ? void 0 : currentAd.mediaFiles.find(function(f) {
1332
+ return !isHlsMediaFile(f);
1333
+ });
1334
+ if (mp4Fallback) {
1335
+ if (debug) console.log("".concat(LOG, " singleElementMode: HLS ad blocked, using MP4 fallback"));
1336
+ startNativePlayback(mp4Fallback);
1337
+ return;
1338
+ }
1339
+ }
1317
1340
  if (isHlsMediaFile(mediaFile)) {
1318
1341
  startHlsPlayback(mediaFile);
1319
1342
  } else {
@@ -1322,7 +1345,7 @@ function createVastAdLayer(contentVideo, options) {
1322
1345
  }
1323
1346
  function playAd(bids) {
1324
1347
  return _async_to_generator(function() {
1325
- var winner, ad, contentVolume, _contentVideo_parentElement, container, adVolume, mediaFile;
1348
+ var winner, ad, contentVolume, adVolume2, mediaFile2, _contentVideo_parentElement, container, adVolume, mediaFile;
1326
1349
  return _ts_generator(this, function(_state) {
1327
1350
  switch(_state.label){
1328
1351
  case 0:
@@ -1366,37 +1389,71 @@ function createVastAdLayer(contentVideo, options) {
1366
1389
  trackingFired.impression = true;
1367
1390
  contentVolume = contentVideo.volume;
1368
1391
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1369
- if (singleElementMode) {
1370
- mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1371
- teardownCurrentPlayback();
1372
- adVideoElement = contentVideo;
1373
- adHls = void 0;
1392
+ if (!singleElementMode) return [
1393
+ 3,
1394
+ 3
1395
+ ];
1396
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1397
+ teardownCurrentPlayback();
1398
+ adVideoElement = contentVideo;
1399
+ adHls = void 0;
1400
+ adPlaying = true;
1401
+ setAdPlayingFlag(true);
1402
+ contentVideo.removeAttribute("src");
1403
+ contentVideo.load();
1404
+ if (!continueLiveStreamDuringAds) {
1405
+ contentVideo.pause();
1406
+ }
1407
+ contentVideo.muted = true;
1408
+ contentVideo.volume = 0;
1409
+ return [
1410
+ 4,
1411
+ new Promise(function(resolve) {
1412
+ return setTimeout(resolve, 200);
1413
+ })
1414
+ ];
1415
+ case 2:
1416
+ _state.sent();
1417
+ if (destroyed || tornDown) return [
1418
+ 2
1419
+ ];
1420
+ contentVideo.style.visibility = "visible";
1421
+ contentVideo.style.opacity = "1";
1422
+ emit("content_pause");
1423
+ setupAdEventListeners();
1424
+ adVolume2 = originalMutedState ? 1 : originalVolume;
1425
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume2));
1426
+ adVideoElement.muted = false;
1427
+ mediaFile2 = selectBestMediaFile(ad.mediaFiles);
1428
+ if (debug) console.log("".concat(LOG, " Loading ad from: ").concat(mediaFile2.url));
1429
+ startPlayback(mediaFile2);
1430
+ return [
1431
+ 2
1432
+ ];
1433
+ case 3:
1434
+ if (!adContainerEl) {
1435
+ ;
1436
+ container = document.createElement("div");
1437
+ container.style.position = "absolute";
1438
+ container.style.left = "0";
1439
+ container.style.top = "0";
1440
+ container.style.right = "0";
1441
+ container.style.bottom = "0";
1442
+ container.style.display = "none";
1443
+ container.style.alignItems = "center";
1444
+ container.style.justifyContent = "center";
1445
+ container.style.pointerEvents = "none";
1446
+ container.style.zIndex = "10";
1447
+ container.style.backgroundColor = "#000";
1448
+ (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1449
+ adContainerEl = container;
1450
+ }
1451
+ if (!adVideoElement) {
1452
+ adVideoElement = createAdVideoElement();
1453
+ adContainerEl.appendChild(adVideoElement);
1374
1454
  setupAdEventListeners();
1375
1455
  } else {
1376
- if (!adContainerEl) {
1377
- ;
1378
- container = document.createElement("div");
1379
- container.style.position = "absolute";
1380
- container.style.left = "0";
1381
- container.style.top = "0";
1382
- container.style.right = "0";
1383
- container.style.bottom = "0";
1384
- container.style.display = "none";
1385
- container.style.alignItems = "center";
1386
- container.style.justifyContent = "center";
1387
- container.style.pointerEvents = "none";
1388
- container.style.zIndex = "10";
1389
- container.style.backgroundColor = "#000";
1390
- (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1391
- adContainerEl = container;
1392
- }
1393
- if (!adVideoElement) {
1394
- adVideoElement = createAdVideoElement();
1395
- adContainerEl.appendChild(adVideoElement);
1396
- setupAdEventListeners();
1397
- } else {
1398
- teardownCurrentPlayback();
1399
- }
1456
+ teardownCurrentPlayback();
1400
1457
  }
1401
1458
  if (!continueLiveStreamDuringAds) {
1402
1459
  contentVideo.pause();
@@ -1408,7 +1465,7 @@ function createVastAdLayer(contentVideo, options) {
1408
1465
  adVolume = originalMutedState ? 1 : originalVolume;
1409
1466
  adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1410
1467
  adVideoElement.muted = false;
1411
- if (!singleElementMode && adContainerEl) {
1468
+ if (adContainerEl) {
1412
1469
  adContainerEl.style.display = "flex";
1413
1470
  adContainerEl.style.pointerEvents = "auto";
1414
1471
  }
@@ -1512,6 +1569,7 @@ function createVastAdLayer(contentVideo, options) {
1512
1569
  if (debug) console.log("".concat(LOG, " [preload] HLS manifest parsed, token=").concat(token));
1513
1570
  });
1514
1571
  hls.on(import_hls.default.Events.ERROR, function(_evt, data) {
1572
+ if (!preloadSlots.has(token)) return;
1515
1573
  if (data.fatal) {
1516
1574
  if (debug) console.warn("".concat(LOG, " [preload] HLS error for token=").concat(token));
1517
1575
  preloadSlots.delete(token);
@@ -1548,113 +1606,146 @@ function createVastAdLayer(contentVideo, options) {
1548
1606
  }
1549
1607
  function playPreloaded(token) {
1550
1608
  return _async_to_generator(function() {
1551
- var slot, contentVolume, adVolume2, videoEl, container2, adVolume21, adVolume, container;
1609
+ var slot, contentVolume, adVolume2, videoEl, container2, adVolume21, nonFatalNetworkErrors, adVolume, container;
1552
1610
  return _ts_generator(this, function(_state) {
1553
- if (destroyed) return [
1554
- 2,
1555
- Promise.reject(new Error("Layer has been destroyed"))
1556
- ];
1557
- slot = preloadSlots.get(token);
1558
- if (!slot) {
1559
- if (debug) console.warn("".concat(LOG, " [preload] No slot found for token=").concat(token, ", nothing to play"));
1560
- return [
1561
- 2
1562
- ];
1563
- }
1564
- preloadSlots.delete(token);
1565
- if (debug) console.log("".concat(LOG, " [preload] Playing preloaded ad, token=").concat(token, ", ready=").concat(slot.ready));
1566
- contentVolume = contentVideo.volume;
1567
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1568
- sessionId = generateSessionId();
1569
- currentAd = slot.ad;
1570
- trackingFired = _object_spread({}, createEmptyTrackingState());
1571
- fireTrackingPixels2(slot.ad.trackingUrls.impression);
1572
- trackingFired.impression = true;
1573
- if (singleElementMode) {
1574
- mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1575
- teardownCurrentPlayback();
1576
- adVideoElement = contentVideo;
1577
- adHls = void 0;
1578
- setupAdEventListeners();
1579
- if (!continueLiveStreamDuringAds) {
1580
- contentVideo.pause();
1581
- }
1582
- contentVideo.muted = true;
1583
- contentVideo.volume = 0;
1584
- adPlaying = true;
1585
- setAdPlayingFlag(true);
1586
- adVolume2 = originalMutedState ? 1 : originalVolume;
1587
- contentVideo.volume = Math.max(0, Math.min(1, adVolume2));
1588
- contentVideo.muted = false;
1589
- emit("content_pause");
1590
- if (debug) console.log("".concat(LOG, " [preload] singleElementMode: attaching ad to contentVideo, url=").concat(slot.mediaFile.url));
1591
- startPlayback(slot.mediaFile);
1592
- return [
1593
- 2
1594
- ];
1595
- }
1596
- if (smartTVMode && !slot.videoEl) {
1597
- teardownCurrentPlayback();
1598
- if (adVideoElement) {
1599
- adVideoElement.remove();
1600
- adVideoElement = void 0;
1601
- }
1602
- videoEl = createAdVideoElement();
1603
- videoEl.style.visibility = "visible";
1604
- videoEl.style.pointerEvents = "none";
1605
- container2 = ensureAdContainer();
1606
- container2.appendChild(videoEl);
1607
- adVideoElement = videoEl;
1608
- setupAdEventListeners();
1609
- if (!continueLiveStreamDuringAds) {
1610
- contentVideo.pause();
1611
- }
1612
- contentVideo.muted = true;
1613
- contentVideo.volume = 0;
1614
- adPlaying = true;
1615
- setAdPlayingFlag(true);
1616
- adVolume21 = originalMutedState ? 1 : originalVolume;
1617
- adVideoElement.volume = Math.max(0, Math.min(1, adVolume21));
1618
- adVideoElement.muted = false;
1619
- container2.style.display = "flex";
1620
- container2.style.pointerEvents = "auto";
1621
- emit("content_pause");
1622
- if (debug) console.log("".concat(LOG, " [preload] smartTVMode deferred: creating video element and loading, url=").concat(slot.mediaFile.url));
1623
- startPlayback(slot.mediaFile);
1624
- return [
1625
- 2
1626
- ];
1627
- }
1628
- teardownCurrentPlayback();
1629
- if (adVideoElement && adVideoElement !== slot.videoEl) {
1630
- adVideoElement.remove();
1631
- }
1632
- slot.videoEl.style.visibility = "visible";
1633
- slot.videoEl.style.pointerEvents = "none";
1634
- adVideoElement = slot.videoEl;
1635
- adHls = slot.hlsInstance;
1636
- setupAdEventListeners();
1637
- if (!continueLiveStreamDuringAds) {
1638
- contentVideo.pause();
1611
+ switch(_state.label){
1612
+ case 0:
1613
+ if (destroyed) return [
1614
+ 2,
1615
+ Promise.reject(new Error("Layer has been destroyed"))
1616
+ ];
1617
+ slot = preloadSlots.get(token);
1618
+ if (!slot) {
1619
+ if (debug) console.warn("".concat(LOG, " [preload] No slot found for token=").concat(token, ", nothing to play"));
1620
+ return [
1621
+ 2
1622
+ ];
1623
+ }
1624
+ preloadSlots.delete(token);
1625
+ if (debug) console.log("".concat(LOG, " [preload] Playing preloaded ad, token=").concat(token, ", ready=").concat(slot.ready));
1626
+ contentVolume = contentVideo.volume;
1627
+ originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1628
+ sessionId = generateSessionId();
1629
+ currentAd = slot.ad;
1630
+ trackingFired = _object_spread({}, createEmptyTrackingState());
1631
+ fireTrackingPixels2(slot.ad.trackingUrls.impression);
1632
+ trackingFired.impression = true;
1633
+ if (!singleElementMode) return [
1634
+ 3,
1635
+ 2
1636
+ ];
1637
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1638
+ teardownCurrentPlayback();
1639
+ adVideoElement = contentVideo;
1640
+ adHls = void 0;
1641
+ adPlaying = true;
1642
+ setAdPlayingFlag(true);
1643
+ contentVideo.removeAttribute("src");
1644
+ contentVideo.load();
1645
+ contentVideo.muted = true;
1646
+ contentVideo.volume = 0;
1647
+ return [
1648
+ 4,
1649
+ new Promise(function(resolve) {
1650
+ return setTimeout(resolve, 200);
1651
+ })
1652
+ ];
1653
+ case 1:
1654
+ _state.sent();
1655
+ if (destroyed || tornDown) return [
1656
+ 2
1657
+ ];
1658
+ contentVideo.style.visibility = "visible";
1659
+ contentVideo.style.opacity = "1";
1660
+ emit("content_pause");
1661
+ setupAdEventListeners();
1662
+ adVolume2 = originalMutedState ? 1 : originalVolume;
1663
+ contentVideo.volume = Math.max(0, Math.min(1, adVolume2));
1664
+ contentVideo.muted = false;
1665
+ if (debug) console.log("".concat(LOG, " [preload] singleElementMode: attaching ad to contentVideo, url=").concat(slot.mediaFile.url));
1666
+ startPlayback(slot.mediaFile);
1667
+ return [
1668
+ 2
1669
+ ];
1670
+ case 2:
1671
+ if (smartTVMode && !slot.videoEl) {
1672
+ teardownCurrentPlayback();
1673
+ if (adVideoElement) {
1674
+ adVideoElement.remove();
1675
+ adVideoElement = void 0;
1676
+ }
1677
+ videoEl = createAdVideoElement();
1678
+ videoEl.style.visibility = "visible";
1679
+ videoEl.style.pointerEvents = "none";
1680
+ container2 = ensureAdContainer();
1681
+ container2.appendChild(videoEl);
1682
+ adVideoElement = videoEl;
1683
+ setupAdEventListeners();
1684
+ if (!continueLiveStreamDuringAds) {
1685
+ contentVideo.pause();
1686
+ }
1687
+ contentVideo.muted = true;
1688
+ contentVideo.volume = 0;
1689
+ adPlaying = true;
1690
+ setAdPlayingFlag(true);
1691
+ adVolume21 = originalMutedState ? 1 : originalVolume;
1692
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume21));
1693
+ adVideoElement.muted = false;
1694
+ container2.style.display = "flex";
1695
+ container2.style.pointerEvents = "auto";
1696
+ emit("content_pause");
1697
+ if (debug) console.log("".concat(LOG, " [preload] smartTVMode deferred: creating video element and loading, url=").concat(slot.mediaFile.url));
1698
+ startPlayback(slot.mediaFile);
1699
+ return [
1700
+ 2
1701
+ ];
1702
+ }
1703
+ teardownCurrentPlayback();
1704
+ if (adVideoElement && adVideoElement !== slot.videoEl) {
1705
+ adVideoElement.remove();
1706
+ }
1707
+ slot.videoEl.style.visibility = "visible";
1708
+ slot.videoEl.style.pointerEvents = "none";
1709
+ adVideoElement = slot.videoEl;
1710
+ adHls = slot.hlsInstance;
1711
+ if (adHls) {
1712
+ nonFatalNetworkErrors = 0;
1713
+ adHls.on(import_hls.default.Events.ERROR, function(_event, data) {
1714
+ if (!adPlaying) return;
1715
+ if (data.fatal) {
1716
+ handleAdError();
1717
+ } else if (data.type === import_hls.default.ErrorTypes.NETWORK_ERROR) {
1718
+ nonFatalNetworkErrors++;
1719
+ if (nonFatalNetworkErrors >= 3) {
1720
+ if (debug) console.warn("".concat(LOG, " [preload] Too many non-fatal HLS network errors during playback, treating as fatal"));
1721
+ handleAdError();
1722
+ }
1723
+ }
1724
+ });
1725
+ }
1726
+ setupAdEventListeners();
1727
+ if (!continueLiveStreamDuringAds) {
1728
+ contentVideo.pause();
1729
+ }
1730
+ contentVideo.muted = true;
1731
+ contentVideo.volume = 0;
1732
+ adPlaying = true;
1733
+ setAdPlayingFlag(true);
1734
+ adVolume = originalMutedState ? 1 : originalVolume;
1735
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1736
+ adVideoElement.muted = false;
1737
+ container = ensureAdContainer();
1738
+ container.style.display = "flex";
1739
+ container.style.pointerEvents = "auto";
1740
+ emit("content_pause");
1741
+ adVideoElement.play().catch(function(error) {
1742
+ console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1743
+ handleAdError();
1744
+ });
1745
+ return [
1746
+ 2
1747
+ ];
1639
1748
  }
1640
- contentVideo.muted = true;
1641
- contentVideo.volume = 0;
1642
- adPlaying = true;
1643
- setAdPlayingFlag(true);
1644
- adVolume = originalMutedState ? 1 : originalVolume;
1645
- adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1646
- adVideoElement.muted = false;
1647
- container = ensureAdContainer();
1648
- container.style.display = "flex";
1649
- container.style.pointerEvents = "auto";
1650
- emit("content_pause");
1651
- adVideoElement.play().catch(function(error) {
1652
- console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1653
- handleAdError();
1654
- });
1655
- return [
1656
- 2
1657
- ];
1658
1749
  });
1659
1750
  })();
1660
1751
  }
@@ -2984,10 +3075,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2984
3075
  debug: !!config.debugAdTiming
2985
3076
  } : {});
2986
3077
  var browserForAdLayer = detectBrowser();
3078
+ var isSinglePipeline = browserForAdLayer.isSmartTV || !!this.config.singlePipelineMode;
2987
3079
  this.adLayer = createVastAdLayer(this.video, {
2988
3080
  continueLiveStreamDuringAds: false,
2989
- smartTVMode: browserForAdLayer.isSmartTV,
2990
- singleElementMode: browserForAdLayer.isSmartTV,
3081
+ smartTVMode: isSinglePipeline,
3082
+ singleElementMode: isSinglePipeline,
3083
+ forceMP4Ads: isSinglePipeline,
2991
3084
  debug: !!config.debugAdTiming
2992
3085
  });
2993
3086
  }
@@ -3633,6 +3726,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3633
3726
  if (_this.fillerVideo) {
3634
3727
  _this.fillerVideo.style.display = "none";
3635
3728
  }
3729
+ if (!_this.adLayer.isAdPlaying()) {
3730
+ if (_this.config.debugAdTiming) {
3731
+ console.log("[StormcloudVideoPlayer] Filler video failed \u2014 restoring main video");
3732
+ }
3733
+ _this.adLayer.hidePlaceholder();
3734
+ if (_this.video.paused && _this.video.readyState >= 2) {
3735
+ var _this_video_play;
3736
+ (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
3737
+ }
3738
+ }
3636
3739
  });
3637
3740
  if (this.config.debugAdTiming) {
3638
3741
  console.log("[StormcloudVideoPlayer] Showing filler video layer");
@@ -3898,7 +4001,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3898
4001
  };
3899
4002
  this.adLayer.updateOriginalMutedState(this.video.muted, this.video.volume);
3900
4003
  }
3901
- if (!this.config.disableFiller && !this.video.muted) {
4004
+ if (!this.config.disableFiller && !this.video.muted && !this.adLayer.isAdPlaying()) {
3902
4005
  this.video.muted = true;
3903
4006
  this.video.volume = 0;
3904
4007
  if (this.config.debugAdTiming) {
@@ -4349,6 +4452,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4349
4452
  if (!this.isLiveStream) {
4350
4453
  return false;
4351
4454
  }
4455
+ if (this.config.singlePipelineMode) {
4456
+ return false;
4457
+ }
4352
4458
  var browser = detectBrowser();
4353
4459
  if (browser.isSmartTV) {
4354
4460
  return false;
@@ -5499,32 +5605,55 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5499
5605
  this.video.volume = restoredVolume;
5500
5606
  }
5501
5607
  var browser = detectBrowser();
5502
- var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0;
5608
+ var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0 || !!this.config.singlePipelineMode;
5503
5609
  if (isSmartTV && this.hls) {
5504
5610
  var hlsRef = this.hls;
5505
5611
  var savedMuted = restoredMuted;
5506
5612
  var savedVolume = restoredVolume;
5613
+ var videoRef = this.video;
5614
+ var debugEnabled = this.config.debugAdTiming;
5615
+ var tryPlay = function tryPlay1(attempt) {
5616
+ var _videoRef_play;
5617
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5618
+ (_videoRef_play = videoRef.play()) === null || _videoRef_play === void 0 ? void 0 : _videoRef_play.catch(function() {
5619
+ if (attempt < 3) {
5620
+ if (debugEnabled) {
5621
+ console.log("[StormcloudVideoPlayer] Smart TV: play() retry ".concat(attempt + 1, "/3 in ").concat(500 * (attempt + 1), "ms"));
5622
+ }
5623
+ setTimeout(function() {
5624
+ return tryPlay(attempt + 1);
5625
+ }, 500 * (attempt + 1));
5626
+ }
5627
+ });
5628
+ };
5507
5629
  var onManifestParsedRestore = function onManifestParsedRestore1() {
5508
5630
  hlsRef.off(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5509
5631
  if (!_this.inAdBreak && !_this.adLayer.isAdPlaying()) {
5510
- var _this_video_play;
5511
- if (_this.video.muted !== savedMuted) _this.video.muted = savedMuted;
5512
- if (Math.abs(_this.video.volume - savedVolume) > 0.01) _this.video.volume = savedVolume;
5513
- if (_this.config.debugAdTiming) {
5632
+ if (videoRef.muted !== savedMuted) videoRef.muted = savedMuted;
5633
+ if (Math.abs(videoRef.volume - savedVolume) > 0.01) videoRef.volume = savedVolume;
5634
+ if (debugEnabled) {
5514
5635
  console.log("[StormcloudVideoPlayer] Smart TV: audio state restored on MANIFEST_PARSED after re-attach");
5515
5636
  }
5516
5637
  hlsRef.startLoad(-1);
5517
- (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5518
- if (_this.config.debugAdTiming) {
5638
+ if (debugEnabled) {
5519
5639
  console.log("[StormcloudVideoPlayer] Smart TV: seeking to live edge and resuming playback after re-attach");
5520
5640
  }
5521
5641
  }
5522
5642
  };
5523
5643
  hlsRef.on(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5524
- this.hls.attachMedia(this.video);
5525
- if (this.config.debugAdTiming) {
5526
- console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5644
+ var pipelineDelayMs = 300;
5645
+ if (debugEnabled) {
5646
+ console.log("[StormcloudVideoPlayer] Smart TV: waiting ".concat(pipelineDelayMs, "ms for hardware pipeline release before re-attach"));
5527
5647
  }
5648
+ setTimeout(function() {
5649
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5650
+ if (_this.hls) {
5651
+ _this.hls.attachMedia(_this.video);
5652
+ if (debugEnabled) {
5653
+ console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5654
+ }
5655
+ }
5656
+ }, pipelineDelayMs);
5528
5657
  } else {
5529
5658
  if (this.shouldContinueLiveStreamDuringAds()) {
5530
5659
  var _this_video_play;
@@ -5598,6 +5727,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5598
5727
  }
5599
5728
  this.showPlaceholderLayer();
5600
5729
  this.adLayer.showPlaceholder();
5730
+ } else if (this.inAdBreak) {
5731
+ if (this.config.debugAdTiming) {
5732
+ console.log("[CONTINUOUS-FETCH] Ad failure with no filler \u2014 restoring main video temporarily");
5733
+ }
5734
+ this.adLayer.hidePlaceholder();
5735
+ if (!this.adLayer.isAdPlaying() && this.video.paused && this.video.readyState >= 2) {
5736
+ var _this_video_play;
5737
+ (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5738
+ }
5601
5739
  }
5602
5740
  }
5603
5741
  },