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,4 +1,4 @@
1
- import { S as StormcloudVideoPlayerConfig } from '../types-CwDRIvJm.cjs';
1
+ import { S as StormcloudVideoPlayerConfig } from '../types-BYwfSJb5.cjs';
2
2
 
3
3
  declare class StormcloudVideoPlayer {
4
4
  private readonly video;
@@ -974,7 +974,7 @@ function resolveBidToVastAd(winner, logPrefix) {
974
974
  return Promise.resolve(null);
975
975
  }
976
976
  function createVastAdLayer(contentVideo, options) {
977
- var _ref, _ref1, _ref2, _ref3;
977
+ var _ref, _ref1, _ref2, _ref3, _ref4;
978
978
  var adPlaying = false;
979
979
  var originalMutedState = false;
980
980
  var originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));
@@ -983,7 +983,8 @@ function createVastAdLayer(contentVideo, options) {
983
983
  var continueLiveStreamDuringAds = (_ref = options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) !== null && _ref !== void 0 ? _ref : false;
984
984
  var smartTVMode = (_ref1 = options === null || options === void 0 ? void 0 : options.smartTVMode) !== null && _ref1 !== void 0 ? _ref1 : false;
985
985
  var singleElementMode = (_ref2 = options === null || options === void 0 ? void 0 : options.singleElementMode) !== null && _ref2 !== void 0 ? _ref2 : false;
986
- var debug = (_ref3 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref3 !== void 0 ? _ref3 : false;
986
+ var forceMP4Ads = (_ref3 = options === null || options === void 0 ? void 0 : options.forceMP4Ads) !== null && _ref3 !== void 0 ? _ref3 : smartTVMode || singleElementMode;
987
+ var debug = (_ref4 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref4 !== void 0 ? _ref4 : false;
987
988
  var adVideoElement;
988
989
  var adHls;
989
990
  var adContainerEl;
@@ -1054,14 +1055,26 @@ function createVastAdLayer(contentVideo, options) {
1054
1055
  var _ref;
1055
1056
  var _scoredFiles_;
1056
1057
  if (mediaFiles.length === 0) throw new Error("No media files available");
1057
- var firstFile = mediaFiles[0];
1058
- if (mediaFiles.length === 1) return firstFile;
1058
+ var candidates = mediaFiles;
1059
+ if (forceMP4Ads) {
1060
+ var mp4Only = candidates.filter(function(f) {
1061
+ return !isHlsMediaFile(f);
1062
+ });
1063
+ if (mp4Only.length > 0) {
1064
+ candidates = mp4Only;
1065
+ if (debug) console.log("".concat(LOG, " forceMP4Ads: filtered to ").concat(mp4Only.length, " MP4-only file(s)"));
1066
+ } else if (debug) {
1067
+ console.warn("".concat(LOG, " forceMP4Ads: no MP4 files available, falling back to all media files"));
1068
+ }
1069
+ }
1070
+ var firstFile = candidates[0];
1071
+ if (candidates.length === 1) return firstFile;
1059
1072
  var mainQuality = getMainStreamQuality();
1060
1073
  if (!mainQuality) {
1061
1074
  if (debug) console.log("".concat(LOG, " No main stream quality info, using first media file"));
1062
1075
  return firstFile;
1063
1076
  }
1064
- var scoredFiles = mediaFiles.map(function(file) {
1077
+ var scoredFiles = candidates.map(function(file) {
1065
1078
  var widthDiff = Math.abs(file.width - mainQuality.width);
1066
1079
  var heightDiff = Math.abs(file.height - mainQuality.height);
1067
1080
  var resolutionDiff = widthDiff + heightDiff;
@@ -1278,6 +1291,16 @@ function createVastAdLayer(contentVideo, options) {
1278
1291
  }
1279
1292
  function startPlayback(mediaFile) {
1280
1293
  if (!adVideoElement) return;
1294
+ if (singleElementMode && isHlsMediaFile(mediaFile)) {
1295
+ var mp4Fallback = currentAd === null || currentAd === void 0 ? void 0 : currentAd.mediaFiles.find(function(f) {
1296
+ return !isHlsMediaFile(f);
1297
+ });
1298
+ if (mp4Fallback) {
1299
+ if (debug) console.log("".concat(LOG, " singleElementMode: HLS ad blocked, using MP4 fallback"));
1300
+ startNativePlayback(mp4Fallback);
1301
+ return;
1302
+ }
1303
+ }
1281
1304
  if (isHlsMediaFile(mediaFile)) {
1282
1305
  startHlsPlayback(mediaFile);
1283
1306
  } else {
@@ -1286,7 +1309,7 @@ function createVastAdLayer(contentVideo, options) {
1286
1309
  }
1287
1310
  function playAd(bids) {
1288
1311
  return _async_to_generator(function() {
1289
- var winner, ad, contentVolume, _contentVideo_parentElement, container, adVolume, mediaFile;
1312
+ var winner, ad, contentVolume, adVolume2, mediaFile2, _contentVideo_parentElement, container, adVolume, mediaFile;
1290
1313
  return _ts_generator(this, function(_state) {
1291
1314
  switch(_state.label){
1292
1315
  case 0:
@@ -1330,37 +1353,71 @@ function createVastAdLayer(contentVideo, options) {
1330
1353
  trackingFired.impression = true;
1331
1354
  contentVolume = contentVideo.volume;
1332
1355
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1333
- if (singleElementMode) {
1334
- mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1335
- teardownCurrentPlayback();
1336
- adVideoElement = contentVideo;
1337
- adHls = void 0;
1356
+ if (!singleElementMode) return [
1357
+ 3,
1358
+ 3
1359
+ ];
1360
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1361
+ teardownCurrentPlayback();
1362
+ adVideoElement = contentVideo;
1363
+ adHls = void 0;
1364
+ adPlaying = true;
1365
+ setAdPlayingFlag(true);
1366
+ contentVideo.removeAttribute("src");
1367
+ contentVideo.load();
1368
+ if (!continueLiveStreamDuringAds) {
1369
+ contentVideo.pause();
1370
+ }
1371
+ contentVideo.muted = true;
1372
+ contentVideo.volume = 0;
1373
+ return [
1374
+ 4,
1375
+ new Promise(function(resolve) {
1376
+ return setTimeout(resolve, 200);
1377
+ })
1378
+ ];
1379
+ case 2:
1380
+ _state.sent();
1381
+ if (destroyed || tornDown) return [
1382
+ 2
1383
+ ];
1384
+ contentVideo.style.visibility = "visible";
1385
+ contentVideo.style.opacity = "1";
1386
+ emit("content_pause");
1387
+ setupAdEventListeners();
1388
+ adVolume2 = originalMutedState ? 1 : originalVolume;
1389
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume2));
1390
+ adVideoElement.muted = false;
1391
+ mediaFile2 = selectBestMediaFile(ad.mediaFiles);
1392
+ if (debug) console.log("".concat(LOG, " Loading ad from: ").concat(mediaFile2.url));
1393
+ startPlayback(mediaFile2);
1394
+ return [
1395
+ 2
1396
+ ];
1397
+ case 3:
1398
+ if (!adContainerEl) {
1399
+ ;
1400
+ container = document.createElement("div");
1401
+ container.style.position = "absolute";
1402
+ container.style.left = "0";
1403
+ container.style.top = "0";
1404
+ container.style.right = "0";
1405
+ container.style.bottom = "0";
1406
+ container.style.display = "none";
1407
+ container.style.alignItems = "center";
1408
+ container.style.justifyContent = "center";
1409
+ container.style.pointerEvents = "none";
1410
+ container.style.zIndex = "10";
1411
+ container.style.backgroundColor = "#000";
1412
+ (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1413
+ adContainerEl = container;
1414
+ }
1415
+ if (!adVideoElement) {
1416
+ adVideoElement = createAdVideoElement();
1417
+ adContainerEl.appendChild(adVideoElement);
1338
1418
  setupAdEventListeners();
1339
1419
  } else {
1340
- if (!adContainerEl) {
1341
- ;
1342
- container = document.createElement("div");
1343
- container.style.position = "absolute";
1344
- container.style.left = "0";
1345
- container.style.top = "0";
1346
- container.style.right = "0";
1347
- container.style.bottom = "0";
1348
- container.style.display = "none";
1349
- container.style.alignItems = "center";
1350
- container.style.justifyContent = "center";
1351
- container.style.pointerEvents = "none";
1352
- container.style.zIndex = "10";
1353
- container.style.backgroundColor = "#000";
1354
- (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1355
- adContainerEl = container;
1356
- }
1357
- if (!adVideoElement) {
1358
- adVideoElement = createAdVideoElement();
1359
- adContainerEl.appendChild(adVideoElement);
1360
- setupAdEventListeners();
1361
- } else {
1362
- teardownCurrentPlayback();
1363
- }
1420
+ teardownCurrentPlayback();
1364
1421
  }
1365
1422
  if (!continueLiveStreamDuringAds) {
1366
1423
  contentVideo.pause();
@@ -1372,7 +1429,7 @@ function createVastAdLayer(contentVideo, options) {
1372
1429
  adVolume = originalMutedState ? 1 : originalVolume;
1373
1430
  adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1374
1431
  adVideoElement.muted = false;
1375
- if (!singleElementMode && adContainerEl) {
1432
+ if (adContainerEl) {
1376
1433
  adContainerEl.style.display = "flex";
1377
1434
  adContainerEl.style.pointerEvents = "auto";
1378
1435
  }
@@ -1476,6 +1533,7 @@ function createVastAdLayer(contentVideo, options) {
1476
1533
  if (debug) console.log("".concat(LOG, " [preload] HLS manifest parsed, token=").concat(token));
1477
1534
  });
1478
1535
  hls.on(import_hls.default.Events.ERROR, function(_evt, data) {
1536
+ if (!preloadSlots.has(token)) return;
1479
1537
  if (data.fatal) {
1480
1538
  if (debug) console.warn("".concat(LOG, " [preload] HLS error for token=").concat(token));
1481
1539
  preloadSlots.delete(token);
@@ -1512,113 +1570,146 @@ function createVastAdLayer(contentVideo, options) {
1512
1570
  }
1513
1571
  function playPreloaded(token) {
1514
1572
  return _async_to_generator(function() {
1515
- var slot, contentVolume, adVolume2, videoEl, container2, adVolume21, adVolume, container;
1573
+ var slot, contentVolume, adVolume2, videoEl, container2, adVolume21, nonFatalNetworkErrors, adVolume, container;
1516
1574
  return _ts_generator(this, function(_state) {
1517
- if (destroyed) return [
1518
- 2,
1519
- Promise.reject(new Error("Layer has been destroyed"))
1520
- ];
1521
- slot = preloadSlots.get(token);
1522
- if (!slot) {
1523
- if (debug) console.warn("".concat(LOG, " [preload] No slot found for token=").concat(token, ", nothing to play"));
1524
- return [
1525
- 2
1526
- ];
1527
- }
1528
- preloadSlots.delete(token);
1529
- if (debug) console.log("".concat(LOG, " [preload] Playing preloaded ad, token=").concat(token, ", ready=").concat(slot.ready));
1530
- contentVolume = contentVideo.volume;
1531
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1532
- sessionId = generateSessionId();
1533
- currentAd = slot.ad;
1534
- trackingFired = _object_spread({}, createEmptyTrackingState());
1535
- fireTrackingPixels2(slot.ad.trackingUrls.impression);
1536
- trackingFired.impression = true;
1537
- if (singleElementMode) {
1538
- mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1539
- teardownCurrentPlayback();
1540
- adVideoElement = contentVideo;
1541
- adHls = void 0;
1542
- setupAdEventListeners();
1543
- if (!continueLiveStreamDuringAds) {
1544
- contentVideo.pause();
1545
- }
1546
- contentVideo.muted = true;
1547
- contentVideo.volume = 0;
1548
- adPlaying = true;
1549
- setAdPlayingFlag(true);
1550
- adVolume2 = originalMutedState ? 1 : originalVolume;
1551
- contentVideo.volume = Math.max(0, Math.min(1, adVolume2));
1552
- contentVideo.muted = false;
1553
- emit("content_pause");
1554
- if (debug) console.log("".concat(LOG, " [preload] singleElementMode: attaching ad to contentVideo, url=").concat(slot.mediaFile.url));
1555
- startPlayback(slot.mediaFile);
1556
- return [
1557
- 2
1558
- ];
1559
- }
1560
- if (smartTVMode && !slot.videoEl) {
1561
- teardownCurrentPlayback();
1562
- if (adVideoElement) {
1563
- adVideoElement.remove();
1564
- adVideoElement = void 0;
1565
- }
1566
- videoEl = createAdVideoElement();
1567
- videoEl.style.visibility = "visible";
1568
- videoEl.style.pointerEvents = "none";
1569
- container2 = ensureAdContainer();
1570
- container2.appendChild(videoEl);
1571
- adVideoElement = videoEl;
1572
- setupAdEventListeners();
1573
- if (!continueLiveStreamDuringAds) {
1574
- contentVideo.pause();
1575
- }
1576
- contentVideo.muted = true;
1577
- contentVideo.volume = 0;
1578
- adPlaying = true;
1579
- setAdPlayingFlag(true);
1580
- adVolume21 = originalMutedState ? 1 : originalVolume;
1581
- adVideoElement.volume = Math.max(0, Math.min(1, adVolume21));
1582
- adVideoElement.muted = false;
1583
- container2.style.display = "flex";
1584
- container2.style.pointerEvents = "auto";
1585
- emit("content_pause");
1586
- if (debug) console.log("".concat(LOG, " [preload] smartTVMode deferred: creating video element and loading, url=").concat(slot.mediaFile.url));
1587
- startPlayback(slot.mediaFile);
1588
- return [
1589
- 2
1590
- ];
1591
- }
1592
- teardownCurrentPlayback();
1593
- if (adVideoElement && adVideoElement !== slot.videoEl) {
1594
- adVideoElement.remove();
1595
- }
1596
- slot.videoEl.style.visibility = "visible";
1597
- slot.videoEl.style.pointerEvents = "none";
1598
- adVideoElement = slot.videoEl;
1599
- adHls = slot.hlsInstance;
1600
- setupAdEventListeners();
1601
- if (!continueLiveStreamDuringAds) {
1602
- contentVideo.pause();
1575
+ switch(_state.label){
1576
+ case 0:
1577
+ if (destroyed) return [
1578
+ 2,
1579
+ Promise.reject(new Error("Layer has been destroyed"))
1580
+ ];
1581
+ slot = preloadSlots.get(token);
1582
+ if (!slot) {
1583
+ if (debug) console.warn("".concat(LOG, " [preload] No slot found for token=").concat(token, ", nothing to play"));
1584
+ return [
1585
+ 2
1586
+ ];
1587
+ }
1588
+ preloadSlots.delete(token);
1589
+ if (debug) console.log("".concat(LOG, " [preload] Playing preloaded ad, token=").concat(token, ", ready=").concat(slot.ready));
1590
+ contentVolume = contentVideo.volume;
1591
+ originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1592
+ sessionId = generateSessionId();
1593
+ currentAd = slot.ad;
1594
+ trackingFired = _object_spread({}, createEmptyTrackingState());
1595
+ fireTrackingPixels2(slot.ad.trackingUrls.impression);
1596
+ trackingFired.impression = true;
1597
+ if (!singleElementMode) return [
1598
+ 3,
1599
+ 2
1600
+ ];
1601
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1602
+ teardownCurrentPlayback();
1603
+ adVideoElement = contentVideo;
1604
+ adHls = void 0;
1605
+ adPlaying = true;
1606
+ setAdPlayingFlag(true);
1607
+ contentVideo.removeAttribute("src");
1608
+ contentVideo.load();
1609
+ contentVideo.muted = true;
1610
+ contentVideo.volume = 0;
1611
+ return [
1612
+ 4,
1613
+ new Promise(function(resolve) {
1614
+ return setTimeout(resolve, 200);
1615
+ })
1616
+ ];
1617
+ case 1:
1618
+ _state.sent();
1619
+ if (destroyed || tornDown) return [
1620
+ 2
1621
+ ];
1622
+ contentVideo.style.visibility = "visible";
1623
+ contentVideo.style.opacity = "1";
1624
+ emit("content_pause");
1625
+ setupAdEventListeners();
1626
+ adVolume2 = originalMutedState ? 1 : originalVolume;
1627
+ contentVideo.volume = Math.max(0, Math.min(1, adVolume2));
1628
+ contentVideo.muted = false;
1629
+ if (debug) console.log("".concat(LOG, " [preload] singleElementMode: attaching ad to contentVideo, url=").concat(slot.mediaFile.url));
1630
+ startPlayback(slot.mediaFile);
1631
+ return [
1632
+ 2
1633
+ ];
1634
+ case 2:
1635
+ if (smartTVMode && !slot.videoEl) {
1636
+ teardownCurrentPlayback();
1637
+ if (adVideoElement) {
1638
+ adVideoElement.remove();
1639
+ adVideoElement = void 0;
1640
+ }
1641
+ videoEl = createAdVideoElement();
1642
+ videoEl.style.visibility = "visible";
1643
+ videoEl.style.pointerEvents = "none";
1644
+ container2 = ensureAdContainer();
1645
+ container2.appendChild(videoEl);
1646
+ adVideoElement = videoEl;
1647
+ setupAdEventListeners();
1648
+ if (!continueLiveStreamDuringAds) {
1649
+ contentVideo.pause();
1650
+ }
1651
+ contentVideo.muted = true;
1652
+ contentVideo.volume = 0;
1653
+ adPlaying = true;
1654
+ setAdPlayingFlag(true);
1655
+ adVolume21 = originalMutedState ? 1 : originalVolume;
1656
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume21));
1657
+ adVideoElement.muted = false;
1658
+ container2.style.display = "flex";
1659
+ container2.style.pointerEvents = "auto";
1660
+ emit("content_pause");
1661
+ if (debug) console.log("".concat(LOG, " [preload] smartTVMode deferred: creating video element and loading, url=").concat(slot.mediaFile.url));
1662
+ startPlayback(slot.mediaFile);
1663
+ return [
1664
+ 2
1665
+ ];
1666
+ }
1667
+ teardownCurrentPlayback();
1668
+ if (adVideoElement && adVideoElement !== slot.videoEl) {
1669
+ adVideoElement.remove();
1670
+ }
1671
+ slot.videoEl.style.visibility = "visible";
1672
+ slot.videoEl.style.pointerEvents = "none";
1673
+ adVideoElement = slot.videoEl;
1674
+ adHls = slot.hlsInstance;
1675
+ if (adHls) {
1676
+ nonFatalNetworkErrors = 0;
1677
+ adHls.on(import_hls.default.Events.ERROR, function(_event, data) {
1678
+ if (!adPlaying) return;
1679
+ if (data.fatal) {
1680
+ handleAdError();
1681
+ } else if (data.type === import_hls.default.ErrorTypes.NETWORK_ERROR) {
1682
+ nonFatalNetworkErrors++;
1683
+ if (nonFatalNetworkErrors >= 3) {
1684
+ if (debug) console.warn("".concat(LOG, " [preload] Too many non-fatal HLS network errors during playback, treating as fatal"));
1685
+ handleAdError();
1686
+ }
1687
+ }
1688
+ });
1689
+ }
1690
+ setupAdEventListeners();
1691
+ if (!continueLiveStreamDuringAds) {
1692
+ contentVideo.pause();
1693
+ }
1694
+ contentVideo.muted = true;
1695
+ contentVideo.volume = 0;
1696
+ adPlaying = true;
1697
+ setAdPlayingFlag(true);
1698
+ adVolume = originalMutedState ? 1 : originalVolume;
1699
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1700
+ adVideoElement.muted = false;
1701
+ container = ensureAdContainer();
1702
+ container.style.display = "flex";
1703
+ container.style.pointerEvents = "auto";
1704
+ emit("content_pause");
1705
+ adVideoElement.play().catch(function(error) {
1706
+ console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1707
+ handleAdError();
1708
+ });
1709
+ return [
1710
+ 2
1711
+ ];
1603
1712
  }
1604
- contentVideo.muted = true;
1605
- contentVideo.volume = 0;
1606
- adPlaying = true;
1607
- setAdPlayingFlag(true);
1608
- adVolume = originalMutedState ? 1 : originalVolume;
1609
- adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1610
- adVideoElement.muted = false;
1611
- container = ensureAdContainer();
1612
- container.style.display = "flex";
1613
- container.style.pointerEvents = "auto";
1614
- emit("content_pause");
1615
- adVideoElement.play().catch(function(error) {
1616
- console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1617
- handleAdError();
1618
- });
1619
- return [
1620
- 2
1621
- ];
1622
1713
  });
1623
1714
  })();
1624
1715
  }
@@ -2948,10 +3039,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2948
3039
  debug: !!config.debugAdTiming
2949
3040
  } : {});
2950
3041
  var browserForAdLayer = detectBrowser();
3042
+ var isSinglePipeline = browserForAdLayer.isSmartTV || !!this.config.singlePipelineMode;
2951
3043
  this.adLayer = createVastAdLayer(this.video, {
2952
3044
  continueLiveStreamDuringAds: false,
2953
- smartTVMode: browserForAdLayer.isSmartTV,
2954
- singleElementMode: browserForAdLayer.isSmartTV,
3045
+ smartTVMode: isSinglePipeline,
3046
+ singleElementMode: isSinglePipeline,
3047
+ forceMP4Ads: isSinglePipeline,
2955
3048
  debug: !!config.debugAdTiming
2956
3049
  });
2957
3050
  }
@@ -3597,6 +3690,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3597
3690
  if (_this.fillerVideo) {
3598
3691
  _this.fillerVideo.style.display = "none";
3599
3692
  }
3693
+ if (!_this.adLayer.isAdPlaying()) {
3694
+ if (_this.config.debugAdTiming) {
3695
+ console.log("[StormcloudVideoPlayer] Filler video failed \u2014 restoring main video");
3696
+ }
3697
+ _this.adLayer.hidePlaceholder();
3698
+ if (_this.video.paused && _this.video.readyState >= 2) {
3699
+ var _this_video_play;
3700
+ (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
3701
+ }
3702
+ }
3600
3703
  });
3601
3704
  if (this.config.debugAdTiming) {
3602
3705
  console.log("[StormcloudVideoPlayer] Showing filler video layer");
@@ -3862,7 +3965,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3862
3965
  };
3863
3966
  this.adLayer.updateOriginalMutedState(this.video.muted, this.video.volume);
3864
3967
  }
3865
- if (!this.config.disableFiller && !this.video.muted) {
3968
+ if (!this.config.disableFiller && !this.video.muted && !this.adLayer.isAdPlaying()) {
3866
3969
  this.video.muted = true;
3867
3970
  this.video.volume = 0;
3868
3971
  if (this.config.debugAdTiming) {
@@ -4313,6 +4416,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4313
4416
  if (!this.isLiveStream) {
4314
4417
  return false;
4315
4418
  }
4419
+ if (this.config.singlePipelineMode) {
4420
+ return false;
4421
+ }
4316
4422
  var browser = detectBrowser();
4317
4423
  if (browser.isSmartTV) {
4318
4424
  return false;
@@ -5463,32 +5569,55 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5463
5569
  this.video.volume = restoredVolume;
5464
5570
  }
5465
5571
  var browser = detectBrowser();
5466
- var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0;
5572
+ var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0 || !!this.config.singlePipelineMode;
5467
5573
  if (isSmartTV && this.hls) {
5468
5574
  var hlsRef = this.hls;
5469
5575
  var savedMuted = restoredMuted;
5470
5576
  var savedVolume = restoredVolume;
5577
+ var videoRef = this.video;
5578
+ var debugEnabled = this.config.debugAdTiming;
5579
+ var tryPlay = function tryPlay1(attempt) {
5580
+ var _videoRef_play;
5581
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5582
+ (_videoRef_play = videoRef.play()) === null || _videoRef_play === void 0 ? void 0 : _videoRef_play.catch(function() {
5583
+ if (attempt < 3) {
5584
+ if (debugEnabled) {
5585
+ console.log("[StormcloudVideoPlayer] Smart TV: play() retry ".concat(attempt + 1, "/3 in ").concat(500 * (attempt + 1), "ms"));
5586
+ }
5587
+ setTimeout(function() {
5588
+ return tryPlay(attempt + 1);
5589
+ }, 500 * (attempt + 1));
5590
+ }
5591
+ });
5592
+ };
5471
5593
  var onManifestParsedRestore = function onManifestParsedRestore1() {
5472
5594
  hlsRef.off(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5473
5595
  if (!_this.inAdBreak && !_this.adLayer.isAdPlaying()) {
5474
- var _this_video_play;
5475
- if (_this.video.muted !== savedMuted) _this.video.muted = savedMuted;
5476
- if (Math.abs(_this.video.volume - savedVolume) > 0.01) _this.video.volume = savedVolume;
5477
- if (_this.config.debugAdTiming) {
5596
+ if (videoRef.muted !== savedMuted) videoRef.muted = savedMuted;
5597
+ if (Math.abs(videoRef.volume - savedVolume) > 0.01) videoRef.volume = savedVolume;
5598
+ if (debugEnabled) {
5478
5599
  console.log("[StormcloudVideoPlayer] Smart TV: audio state restored on MANIFEST_PARSED after re-attach");
5479
5600
  }
5480
5601
  hlsRef.startLoad(-1);
5481
- (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5482
- if (_this.config.debugAdTiming) {
5602
+ if (debugEnabled) {
5483
5603
  console.log("[StormcloudVideoPlayer] Smart TV: seeking to live edge and resuming playback after re-attach");
5484
5604
  }
5485
5605
  }
5486
5606
  };
5487
5607
  hlsRef.on(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5488
- this.hls.attachMedia(this.video);
5489
- if (this.config.debugAdTiming) {
5490
- console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5608
+ var pipelineDelayMs = 300;
5609
+ if (debugEnabled) {
5610
+ console.log("[StormcloudVideoPlayer] Smart TV: waiting ".concat(pipelineDelayMs, "ms for hardware pipeline release before re-attach"));
5491
5611
  }
5612
+ setTimeout(function() {
5613
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5614
+ if (_this.hls) {
5615
+ _this.hls.attachMedia(_this.video);
5616
+ if (debugEnabled) {
5617
+ console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5618
+ }
5619
+ }
5620
+ }, pipelineDelayMs);
5492
5621
  } else {
5493
5622
  if (this.shouldContinueLiveStreamDuringAds()) {
5494
5623
  var _this_video_play;
@@ -5562,6 +5691,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5562
5691
  }
5563
5692
  this.showPlaceholderLayer();
5564
5693
  this.adLayer.showPlaceholder();
5694
+ } else if (this.inAdBreak) {
5695
+ if (this.config.debugAdTiming) {
5696
+ console.log("[CONTINUOUS-FETCH] Ad failure with no filler \u2014 restoring main video temporarily");
5697
+ }
5698
+ this.adLayer.hidePlaceholder();
5699
+ if (!this.adLayer.isAdPlaying() && this.video.paused && this.video.readyState >= 2) {
5700
+ var _this_video_play;
5701
+ (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5702
+ }
5565
5703
  }
5566
5704
  }
5567
5705
  },