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.d.cts CHANGED
@@ -35,6 +35,7 @@ interface StormcloudVideoPlayerConfig {
35
35
  disableAds?: boolean;
36
36
  disableFiller?: boolean;
37
37
  adTransitionGapMs?: number;
38
+ singlePipelineMode?: boolean;
38
39
  }
39
40
  interface ClientInfo {
40
41
  brand: string;
@@ -4008,6 +4009,7 @@ interface VastAdLayerOptions {
4008
4009
  mainHlsInstance?: Hls;
4009
4010
  smartTVMode?: boolean;
4010
4011
  singleElementMode?: boolean;
4012
+ forceMP4Ads?: boolean;
4011
4013
  debug?: boolean;
4012
4014
  }
4013
4015
  interface VastAdLayerOptionsUpdate {
package/lib/index.d.ts CHANGED
@@ -35,6 +35,7 @@ interface StormcloudVideoPlayerConfig {
35
35
  disableAds?: boolean;
36
36
  disableFiller?: boolean;
37
37
  adTransitionGapMs?: number;
38
+ singlePipelineMode?: boolean;
38
39
  }
39
40
  interface ClientInfo {
40
41
  brand: string;
@@ -4008,6 +4009,7 @@ interface VastAdLayerOptions {
4008
4009
  mainHlsInstance?: Hls;
4009
4010
  smartTVMode?: boolean;
4010
4011
  singleElementMode?: boolean;
4012
+ forceMP4Ads?: boolean;
4011
4013
  debug?: boolean;
4012
4014
  }
4013
4015
  interface VastAdLayerOptionsUpdate {
package/lib/index.js CHANGED
@@ -944,7 +944,7 @@ function resolveBidToVastAd(winner, logPrefix) {
944
944
  return Promise.resolve(null);
945
945
  }
946
946
  function createVastAdLayer(contentVideo, options) {
947
- var _ref, _ref1, _ref2, _ref3;
947
+ var _ref, _ref1, _ref2, _ref3, _ref4;
948
948
  var adPlaying = false;
949
949
  var originalMutedState = false;
950
950
  var originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));
@@ -953,7 +953,8 @@ function createVastAdLayer(contentVideo, options) {
953
953
  var continueLiveStreamDuringAds = (_ref = options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) !== null && _ref !== void 0 ? _ref : false;
954
954
  var smartTVMode = (_ref1 = options === null || options === void 0 ? void 0 : options.smartTVMode) !== null && _ref1 !== void 0 ? _ref1 : false;
955
955
  var singleElementMode = (_ref2 = options === null || options === void 0 ? void 0 : options.singleElementMode) !== null && _ref2 !== void 0 ? _ref2 : false;
956
- var debug = (_ref3 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref3 !== void 0 ? _ref3 : false;
956
+ var forceMP4Ads = (_ref3 = options === null || options === void 0 ? void 0 : options.forceMP4Ads) !== null && _ref3 !== void 0 ? _ref3 : smartTVMode || singleElementMode;
957
+ var debug = (_ref4 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref4 !== void 0 ? _ref4 : false;
957
958
  var adVideoElement;
958
959
  var adHls;
959
960
  var adContainerEl;
@@ -1024,14 +1025,26 @@ function createVastAdLayer(contentVideo, options) {
1024
1025
  var _ref;
1025
1026
  var _scoredFiles_;
1026
1027
  if (mediaFiles.length === 0) throw new Error("No media files available");
1027
- var firstFile = mediaFiles[0];
1028
- if (mediaFiles.length === 1) return firstFile;
1028
+ var candidates = mediaFiles;
1029
+ if (forceMP4Ads) {
1030
+ var mp4Only = candidates.filter(function(f) {
1031
+ return !isHlsMediaFile(f);
1032
+ });
1033
+ if (mp4Only.length > 0) {
1034
+ candidates = mp4Only;
1035
+ if (debug) console.log("".concat(LOG, " forceMP4Ads: filtered to ").concat(mp4Only.length, " MP4-only file(s)"));
1036
+ } else if (debug) {
1037
+ console.warn("".concat(LOG, " forceMP4Ads: no MP4 files available, falling back to all media files"));
1038
+ }
1039
+ }
1040
+ var firstFile = candidates[0];
1041
+ if (candidates.length === 1) return firstFile;
1029
1042
  var mainQuality = getMainStreamQuality();
1030
1043
  if (!mainQuality) {
1031
1044
  if (debug) console.log("".concat(LOG, " No main stream quality info, using first media file"));
1032
1045
  return firstFile;
1033
1046
  }
1034
- var scoredFiles = mediaFiles.map(function(file) {
1047
+ var scoredFiles = candidates.map(function(file) {
1035
1048
  var widthDiff = Math.abs(file.width - mainQuality.width);
1036
1049
  var heightDiff = Math.abs(file.height - mainQuality.height);
1037
1050
  var resolutionDiff = widthDiff + heightDiff;
@@ -1248,6 +1261,16 @@ function createVastAdLayer(contentVideo, options) {
1248
1261
  }
1249
1262
  function startPlayback(mediaFile) {
1250
1263
  if (!adVideoElement) return;
1264
+ if (singleElementMode && isHlsMediaFile(mediaFile)) {
1265
+ var mp4Fallback = currentAd === null || currentAd === void 0 ? void 0 : currentAd.mediaFiles.find(function(f) {
1266
+ return !isHlsMediaFile(f);
1267
+ });
1268
+ if (mp4Fallback) {
1269
+ if (debug) console.log("".concat(LOG, " singleElementMode: HLS ad blocked, using MP4 fallback"));
1270
+ startNativePlayback(mp4Fallback);
1271
+ return;
1272
+ }
1273
+ }
1251
1274
  if (isHlsMediaFile(mediaFile)) {
1252
1275
  startHlsPlayback(mediaFile);
1253
1276
  } else {
@@ -1256,7 +1279,7 @@ function createVastAdLayer(contentVideo, options) {
1256
1279
  }
1257
1280
  function playAd(bids) {
1258
1281
  return _async_to_generator(function() {
1259
- var winner, ad, contentVolume, _contentVideo_parentElement, container, adVolume, mediaFile;
1282
+ var winner, ad, contentVolume, adVolume2, mediaFile2, _contentVideo_parentElement, container, adVolume, mediaFile;
1260
1283
  return _ts_generator(this, function(_state) {
1261
1284
  switch(_state.label){
1262
1285
  case 0:
@@ -1300,37 +1323,71 @@ function createVastAdLayer(contentVideo, options) {
1300
1323
  trackingFired.impression = true;
1301
1324
  contentVolume = contentVideo.volume;
1302
1325
  originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1303
- if (singleElementMode) {
1304
- mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1305
- teardownCurrentPlayback();
1306
- adVideoElement = contentVideo;
1307
- adHls = void 0;
1326
+ if (!singleElementMode) return [
1327
+ 3,
1328
+ 3
1329
+ ];
1330
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1331
+ teardownCurrentPlayback();
1332
+ adVideoElement = contentVideo;
1333
+ adHls = void 0;
1334
+ adPlaying = true;
1335
+ setAdPlayingFlag(true);
1336
+ contentVideo.removeAttribute("src");
1337
+ contentVideo.load();
1338
+ if (!continueLiveStreamDuringAds) {
1339
+ contentVideo.pause();
1340
+ }
1341
+ contentVideo.muted = true;
1342
+ contentVideo.volume = 0;
1343
+ return [
1344
+ 4,
1345
+ new Promise(function(resolve) {
1346
+ return setTimeout(resolve, 200);
1347
+ })
1348
+ ];
1349
+ case 2:
1350
+ _state.sent();
1351
+ if (destroyed || tornDown) return [
1352
+ 2
1353
+ ];
1354
+ contentVideo.style.visibility = "visible";
1355
+ contentVideo.style.opacity = "1";
1356
+ emit("content_pause");
1357
+ setupAdEventListeners();
1358
+ adVolume2 = originalMutedState ? 1 : originalVolume;
1359
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume2));
1360
+ adVideoElement.muted = false;
1361
+ mediaFile2 = selectBestMediaFile(ad.mediaFiles);
1362
+ if (debug) console.log("".concat(LOG, " Loading ad from: ").concat(mediaFile2.url));
1363
+ startPlayback(mediaFile2);
1364
+ return [
1365
+ 2
1366
+ ];
1367
+ case 3:
1368
+ if (!adContainerEl) {
1369
+ ;
1370
+ container = document.createElement("div");
1371
+ container.style.position = "absolute";
1372
+ container.style.left = "0";
1373
+ container.style.top = "0";
1374
+ container.style.right = "0";
1375
+ container.style.bottom = "0";
1376
+ container.style.display = "none";
1377
+ container.style.alignItems = "center";
1378
+ container.style.justifyContent = "center";
1379
+ container.style.pointerEvents = "none";
1380
+ container.style.zIndex = "10";
1381
+ container.style.backgroundColor = "#000";
1382
+ (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1383
+ adContainerEl = container;
1384
+ }
1385
+ if (!adVideoElement) {
1386
+ adVideoElement = createAdVideoElement();
1387
+ adContainerEl.appendChild(adVideoElement);
1308
1388
  setupAdEventListeners();
1309
1389
  } else {
1310
- if (!adContainerEl) {
1311
- ;
1312
- container = document.createElement("div");
1313
- container.style.position = "absolute";
1314
- container.style.left = "0";
1315
- container.style.top = "0";
1316
- container.style.right = "0";
1317
- container.style.bottom = "0";
1318
- container.style.display = "none";
1319
- container.style.alignItems = "center";
1320
- container.style.justifyContent = "center";
1321
- container.style.pointerEvents = "none";
1322
- container.style.zIndex = "10";
1323
- container.style.backgroundColor = "#000";
1324
- (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1325
- adContainerEl = container;
1326
- }
1327
- if (!adVideoElement) {
1328
- adVideoElement = createAdVideoElement();
1329
- adContainerEl.appendChild(adVideoElement);
1330
- setupAdEventListeners();
1331
- } else {
1332
- teardownCurrentPlayback();
1333
- }
1390
+ teardownCurrentPlayback();
1334
1391
  }
1335
1392
  if (!continueLiveStreamDuringAds) {
1336
1393
  contentVideo.pause();
@@ -1342,7 +1399,7 @@ function createVastAdLayer(contentVideo, options) {
1342
1399
  adVolume = originalMutedState ? 1 : originalVolume;
1343
1400
  adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1344
1401
  adVideoElement.muted = false;
1345
- if (!singleElementMode && adContainerEl) {
1402
+ if (adContainerEl) {
1346
1403
  adContainerEl.style.display = "flex";
1347
1404
  adContainerEl.style.pointerEvents = "auto";
1348
1405
  }
@@ -1446,6 +1503,7 @@ function createVastAdLayer(contentVideo, options) {
1446
1503
  if (debug) console.log("".concat(LOG, " [preload] HLS manifest parsed, token=").concat(token));
1447
1504
  });
1448
1505
  hls.on(Hls.Events.ERROR, function(_evt, data) {
1506
+ if (!preloadSlots.has(token)) return;
1449
1507
  if (data.fatal) {
1450
1508
  if (debug) console.warn("".concat(LOG, " [preload] HLS error for token=").concat(token));
1451
1509
  preloadSlots.delete(token);
@@ -1482,113 +1540,146 @@ function createVastAdLayer(contentVideo, options) {
1482
1540
  }
1483
1541
  function playPreloaded(token) {
1484
1542
  return _async_to_generator(function() {
1485
- var slot, contentVolume, adVolume2, videoEl, container2, adVolume21, adVolume, container;
1543
+ var slot, contentVolume, adVolume2, videoEl, container2, adVolume21, nonFatalNetworkErrors, adVolume, container;
1486
1544
  return _ts_generator(this, function(_state) {
1487
- if (destroyed) return [
1488
- 2,
1489
- Promise.reject(new Error("Layer has been destroyed"))
1490
- ];
1491
- slot = preloadSlots.get(token);
1492
- if (!slot) {
1493
- if (debug) console.warn("".concat(LOG, " [preload] No slot found for token=").concat(token, ", nothing to play"));
1494
- return [
1495
- 2
1496
- ];
1497
- }
1498
- preloadSlots.delete(token);
1499
- if (debug) console.log("".concat(LOG, " [preload] Playing preloaded ad, token=").concat(token, ", ready=").concat(slot.ready));
1500
- contentVolume = contentVideo.volume;
1501
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1502
- sessionId = generateSessionId();
1503
- currentAd = slot.ad;
1504
- trackingFired = _object_spread({}, createEmptyTrackingState());
1505
- fireTrackingPixels2(slot.ad.trackingUrls.impression);
1506
- trackingFired.impression = true;
1507
- if (singleElementMode) {
1508
- mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1509
- teardownCurrentPlayback();
1510
- adVideoElement = contentVideo;
1511
- adHls = void 0;
1512
- setupAdEventListeners();
1513
- if (!continueLiveStreamDuringAds) {
1514
- contentVideo.pause();
1515
- }
1516
- contentVideo.muted = true;
1517
- contentVideo.volume = 0;
1518
- adPlaying = true;
1519
- setAdPlayingFlag(true);
1520
- adVolume2 = originalMutedState ? 1 : originalVolume;
1521
- contentVideo.volume = Math.max(0, Math.min(1, adVolume2));
1522
- contentVideo.muted = false;
1523
- emit("content_pause");
1524
- if (debug) console.log("".concat(LOG, " [preload] singleElementMode: attaching ad to contentVideo, url=").concat(slot.mediaFile.url));
1525
- startPlayback(slot.mediaFile);
1526
- return [
1527
- 2
1528
- ];
1529
- }
1530
- if (smartTVMode && !slot.videoEl) {
1531
- teardownCurrentPlayback();
1532
- if (adVideoElement) {
1533
- adVideoElement.remove();
1534
- adVideoElement = void 0;
1535
- }
1536
- videoEl = createAdVideoElement();
1537
- videoEl.style.visibility = "visible";
1538
- videoEl.style.pointerEvents = "none";
1539
- container2 = ensureAdContainer();
1540
- container2.appendChild(videoEl);
1541
- adVideoElement = videoEl;
1542
- setupAdEventListeners();
1543
- if (!continueLiveStreamDuringAds) {
1544
- contentVideo.pause();
1545
- }
1546
- contentVideo.muted = true;
1547
- contentVideo.volume = 0;
1548
- adPlaying = true;
1549
- setAdPlayingFlag(true);
1550
- adVolume21 = originalMutedState ? 1 : originalVolume;
1551
- adVideoElement.volume = Math.max(0, Math.min(1, adVolume21));
1552
- adVideoElement.muted = false;
1553
- container2.style.display = "flex";
1554
- container2.style.pointerEvents = "auto";
1555
- emit("content_pause");
1556
- if (debug) console.log("".concat(LOG, " [preload] smartTVMode deferred: creating video element and loading, url=").concat(slot.mediaFile.url));
1557
- startPlayback(slot.mediaFile);
1558
- return [
1559
- 2
1560
- ];
1561
- }
1562
- teardownCurrentPlayback();
1563
- if (adVideoElement && adVideoElement !== slot.videoEl) {
1564
- adVideoElement.remove();
1545
+ switch(_state.label){
1546
+ case 0:
1547
+ if (destroyed) return [
1548
+ 2,
1549
+ Promise.reject(new Error("Layer has been destroyed"))
1550
+ ];
1551
+ slot = preloadSlots.get(token);
1552
+ if (!slot) {
1553
+ if (debug) console.warn("".concat(LOG, " [preload] No slot found for token=").concat(token, ", nothing to play"));
1554
+ return [
1555
+ 2
1556
+ ];
1557
+ }
1558
+ preloadSlots.delete(token);
1559
+ if (debug) console.log("".concat(LOG, " [preload] Playing preloaded ad, token=").concat(token, ", ready=").concat(slot.ready));
1560
+ contentVolume = contentVideo.volume;
1561
+ originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1562
+ sessionId = generateSessionId();
1563
+ currentAd = slot.ad;
1564
+ trackingFired = _object_spread({}, createEmptyTrackingState());
1565
+ fireTrackingPixels2(slot.ad.trackingUrls.impression);
1566
+ trackingFired.impression = true;
1567
+ if (!singleElementMode) return [
1568
+ 3,
1569
+ 2
1570
+ ];
1571
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1572
+ teardownCurrentPlayback();
1573
+ adVideoElement = contentVideo;
1574
+ adHls = void 0;
1575
+ adPlaying = true;
1576
+ setAdPlayingFlag(true);
1577
+ contentVideo.removeAttribute("src");
1578
+ contentVideo.load();
1579
+ contentVideo.muted = true;
1580
+ contentVideo.volume = 0;
1581
+ return [
1582
+ 4,
1583
+ new Promise(function(resolve) {
1584
+ return setTimeout(resolve, 200);
1585
+ })
1586
+ ];
1587
+ case 1:
1588
+ _state.sent();
1589
+ if (destroyed || tornDown) return [
1590
+ 2
1591
+ ];
1592
+ contentVideo.style.visibility = "visible";
1593
+ contentVideo.style.opacity = "1";
1594
+ emit("content_pause");
1595
+ setupAdEventListeners();
1596
+ adVolume2 = originalMutedState ? 1 : originalVolume;
1597
+ contentVideo.volume = Math.max(0, Math.min(1, adVolume2));
1598
+ contentVideo.muted = false;
1599
+ if (debug) console.log("".concat(LOG, " [preload] singleElementMode: attaching ad to contentVideo, url=").concat(slot.mediaFile.url));
1600
+ startPlayback(slot.mediaFile);
1601
+ return [
1602
+ 2
1603
+ ];
1604
+ case 2:
1605
+ if (smartTVMode && !slot.videoEl) {
1606
+ teardownCurrentPlayback();
1607
+ if (adVideoElement) {
1608
+ adVideoElement.remove();
1609
+ adVideoElement = void 0;
1610
+ }
1611
+ videoEl = createAdVideoElement();
1612
+ videoEl.style.visibility = "visible";
1613
+ videoEl.style.pointerEvents = "none";
1614
+ container2 = ensureAdContainer();
1615
+ container2.appendChild(videoEl);
1616
+ adVideoElement = videoEl;
1617
+ setupAdEventListeners();
1618
+ if (!continueLiveStreamDuringAds) {
1619
+ contentVideo.pause();
1620
+ }
1621
+ contentVideo.muted = true;
1622
+ contentVideo.volume = 0;
1623
+ adPlaying = true;
1624
+ setAdPlayingFlag(true);
1625
+ adVolume21 = originalMutedState ? 1 : originalVolume;
1626
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume21));
1627
+ adVideoElement.muted = false;
1628
+ container2.style.display = "flex";
1629
+ container2.style.pointerEvents = "auto";
1630
+ emit("content_pause");
1631
+ if (debug) console.log("".concat(LOG, " [preload] smartTVMode deferred: creating video element and loading, url=").concat(slot.mediaFile.url));
1632
+ startPlayback(slot.mediaFile);
1633
+ return [
1634
+ 2
1635
+ ];
1636
+ }
1637
+ teardownCurrentPlayback();
1638
+ if (adVideoElement && adVideoElement !== slot.videoEl) {
1639
+ adVideoElement.remove();
1640
+ }
1641
+ slot.videoEl.style.visibility = "visible";
1642
+ slot.videoEl.style.pointerEvents = "none";
1643
+ adVideoElement = slot.videoEl;
1644
+ adHls = slot.hlsInstance;
1645
+ if (adHls) {
1646
+ nonFatalNetworkErrors = 0;
1647
+ adHls.on(Hls.Events.ERROR, function(_event, data) {
1648
+ if (!adPlaying) return;
1649
+ if (data.fatal) {
1650
+ handleAdError();
1651
+ } else if (data.type === Hls.ErrorTypes.NETWORK_ERROR) {
1652
+ nonFatalNetworkErrors++;
1653
+ if (nonFatalNetworkErrors >= 3) {
1654
+ if (debug) console.warn("".concat(LOG, " [preload] Too many non-fatal HLS network errors during playback, treating as fatal"));
1655
+ handleAdError();
1656
+ }
1657
+ }
1658
+ });
1659
+ }
1660
+ setupAdEventListeners();
1661
+ if (!continueLiveStreamDuringAds) {
1662
+ contentVideo.pause();
1663
+ }
1664
+ contentVideo.muted = true;
1665
+ contentVideo.volume = 0;
1666
+ adPlaying = true;
1667
+ setAdPlayingFlag(true);
1668
+ adVolume = originalMutedState ? 1 : originalVolume;
1669
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1670
+ adVideoElement.muted = false;
1671
+ container = ensureAdContainer();
1672
+ container.style.display = "flex";
1673
+ container.style.pointerEvents = "auto";
1674
+ emit("content_pause");
1675
+ adVideoElement.play().catch(function(error) {
1676
+ console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1677
+ handleAdError();
1678
+ });
1679
+ return [
1680
+ 2
1681
+ ];
1565
1682
  }
1566
- slot.videoEl.style.visibility = "visible";
1567
- slot.videoEl.style.pointerEvents = "none";
1568
- adVideoElement = slot.videoEl;
1569
- adHls = slot.hlsInstance;
1570
- setupAdEventListeners();
1571
- if (!continueLiveStreamDuringAds) {
1572
- contentVideo.pause();
1573
- }
1574
- contentVideo.muted = true;
1575
- contentVideo.volume = 0;
1576
- adPlaying = true;
1577
- setAdPlayingFlag(true);
1578
- adVolume = originalMutedState ? 1 : originalVolume;
1579
- adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1580
- adVideoElement.muted = false;
1581
- container = ensureAdContainer();
1582
- container.style.display = "flex";
1583
- container.style.pointerEvents = "auto";
1584
- emit("content_pause");
1585
- adVideoElement.play().catch(function(error) {
1586
- console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1587
- handleAdError();
1588
- });
1589
- return [
1590
- 2
1591
- ];
1592
1683
  });
1593
1684
  })();
1594
1685
  }
@@ -2946,10 +3037,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2946
3037
  debug: !!config.debugAdTiming
2947
3038
  } : {});
2948
3039
  var browserForAdLayer = detectBrowser();
3040
+ var isSinglePipeline = browserForAdLayer.isSmartTV || !!this.config.singlePipelineMode;
2949
3041
  this.adLayer = createVastAdLayer(this.video, {
2950
3042
  continueLiveStreamDuringAds: false,
2951
- smartTVMode: browserForAdLayer.isSmartTV,
2952
- singleElementMode: browserForAdLayer.isSmartTV,
3043
+ smartTVMode: isSinglePipeline,
3044
+ singleElementMode: isSinglePipeline,
3045
+ forceMP4Ads: isSinglePipeline,
2953
3046
  debug: !!config.debugAdTiming
2954
3047
  });
2955
3048
  }
@@ -3595,6 +3688,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3595
3688
  if (_this.fillerVideo) {
3596
3689
  _this.fillerVideo.style.display = "none";
3597
3690
  }
3691
+ if (!_this.adLayer.isAdPlaying()) {
3692
+ if (_this.config.debugAdTiming) {
3693
+ console.log("[StormcloudVideoPlayer] Filler video failed \u2014 restoring main video");
3694
+ }
3695
+ _this.adLayer.hidePlaceholder();
3696
+ if (_this.video.paused && _this.video.readyState >= 2) {
3697
+ var _this_video_play;
3698
+ (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
3699
+ }
3700
+ }
3598
3701
  });
3599
3702
  if (this.config.debugAdTiming) {
3600
3703
  console.log("[StormcloudVideoPlayer] Showing filler video layer");
@@ -3860,7 +3963,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3860
3963
  };
3861
3964
  this.adLayer.updateOriginalMutedState(this.video.muted, this.video.volume);
3862
3965
  }
3863
- if (!this.config.disableFiller && !this.video.muted) {
3966
+ if (!this.config.disableFiller && !this.video.muted && !this.adLayer.isAdPlaying()) {
3864
3967
  this.video.muted = true;
3865
3968
  this.video.volume = 0;
3866
3969
  if (this.config.debugAdTiming) {
@@ -4311,6 +4414,9 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4311
4414
  if (!this.isLiveStream) {
4312
4415
  return false;
4313
4416
  }
4417
+ if (this.config.singlePipelineMode) {
4418
+ return false;
4419
+ }
4314
4420
  var browser = detectBrowser();
4315
4421
  if (browser.isSmartTV) {
4316
4422
  return false;
@@ -5461,32 +5567,55 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5461
5567
  this.video.volume = restoredVolume;
5462
5568
  }
5463
5569
  var browser = detectBrowser();
5464
- var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0;
5570
+ var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0 || !!this.config.singlePipelineMode;
5465
5571
  if (isSmartTV && this.hls) {
5466
5572
  var hlsRef = this.hls;
5467
5573
  var savedMuted = restoredMuted;
5468
5574
  var savedVolume = restoredVolume;
5575
+ var videoRef = this.video;
5576
+ var debugEnabled = this.config.debugAdTiming;
5577
+ var tryPlay = function tryPlay1(attempt) {
5578
+ var _videoRef_play;
5579
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5580
+ (_videoRef_play = videoRef.play()) === null || _videoRef_play === void 0 ? void 0 : _videoRef_play.catch(function() {
5581
+ if (attempt < 3) {
5582
+ if (debugEnabled) {
5583
+ console.log("[StormcloudVideoPlayer] Smart TV: play() retry ".concat(attempt + 1, "/3 in ").concat(500 * (attempt + 1), "ms"));
5584
+ }
5585
+ setTimeout(function() {
5586
+ return tryPlay(attempt + 1);
5587
+ }, 500 * (attempt + 1));
5588
+ }
5589
+ });
5590
+ };
5469
5591
  var onManifestParsedRestore = function onManifestParsedRestore1() {
5470
5592
  hlsRef.off(Hls2.Events.MANIFEST_PARSED, onManifestParsedRestore);
5471
5593
  if (!_this.inAdBreak && !_this.adLayer.isAdPlaying()) {
5472
- var _this_video_play;
5473
- if (_this.video.muted !== savedMuted) _this.video.muted = savedMuted;
5474
- if (Math.abs(_this.video.volume - savedVolume) > 0.01) _this.video.volume = savedVolume;
5475
- if (_this.config.debugAdTiming) {
5594
+ if (videoRef.muted !== savedMuted) videoRef.muted = savedMuted;
5595
+ if (Math.abs(videoRef.volume - savedVolume) > 0.01) videoRef.volume = savedVolume;
5596
+ if (debugEnabled) {
5476
5597
  console.log("[StormcloudVideoPlayer] Smart TV: audio state restored on MANIFEST_PARSED after re-attach");
5477
5598
  }
5478
5599
  hlsRef.startLoad(-1);
5479
- (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5480
- if (_this.config.debugAdTiming) {
5600
+ if (debugEnabled) {
5481
5601
  console.log("[StormcloudVideoPlayer] Smart TV: seeking to live edge and resuming playback after re-attach");
5482
5602
  }
5483
5603
  }
5484
5604
  };
5485
5605
  hlsRef.on(Hls2.Events.MANIFEST_PARSED, onManifestParsedRestore);
5486
- this.hls.attachMedia(this.video);
5487
- if (this.config.debugAdTiming) {
5488
- console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5606
+ var pipelineDelayMs = 300;
5607
+ if (debugEnabled) {
5608
+ console.log("[StormcloudVideoPlayer] Smart TV: waiting ".concat(pipelineDelayMs, "ms for hardware pipeline release before re-attach"));
5489
5609
  }
5610
+ setTimeout(function() {
5611
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5612
+ if (_this.hls) {
5613
+ _this.hls.attachMedia(_this.video);
5614
+ if (debugEnabled) {
5615
+ console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5616
+ }
5617
+ }
5618
+ }, pipelineDelayMs);
5490
5619
  } else {
5491
5620
  if (this.shouldContinueLiveStreamDuringAds()) {
5492
5621
  var _this_video_play;
@@ -5560,6 +5689,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5560
5689
  }
5561
5690
  this.showPlaceholderLayer();
5562
5691
  this.adLayer.showPlaceholder();
5692
+ } else if (this.inAdBreak) {
5693
+ if (this.config.debugAdTiming) {
5694
+ console.log("[CONTINUOUS-FETCH] Ad failure with no filler \u2014 restoring main video temporarily");
5695
+ }
5696
+ this.adLayer.hidePlaceholder();
5697
+ if (!this.adLayer.isAdPlaying() && this.video.paused && this.video.readyState >= 2) {
5698
+ var _this_video_play;
5699
+ (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5700
+ }
5563
5701
  }
5564
5702
  }
5565
5703
  },