stormcloud-video-player 0.6.0 → 0.6.2

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.
@@ -974,14 +974,17 @@ function resolveBidToVastAd(winner, logPrefix) {
974
974
  return Promise.resolve(null);
975
975
  }
976
976
  function createVastAdLayer(contentVideo, options) {
977
- var _ref, _ref1;
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));
981
981
  var listeners = /* @__PURE__ */ new Map();
982
982
  var mainHlsInstance = options === null || options === void 0 ? void 0 : options.mainHlsInstance;
983
983
  var continueLiveStreamDuringAds = (_ref = options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) !== null && _ref !== void 0 ? _ref : false;
984
- var debug = (_ref1 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref1 !== void 0 ? _ref1 : false;
984
+ var smartTVMode = (_ref1 = options === null || options === void 0 ? void 0 : options.smartTVMode) !== null && _ref1 !== void 0 ? _ref1 : false;
985
+ var singleElementMode = (_ref2 = options === null || options === void 0 ? void 0 : options.singleElementMode) !== null && _ref2 !== void 0 ? _ref2 : 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;
985
988
  var adVideoElement;
986
989
  var adHls;
987
990
  var adContainerEl;
@@ -990,6 +993,7 @@ function createVastAdLayer(contentVideo, options) {
990
993
  var destroyed = false;
991
994
  var tornDown = false;
992
995
  var trackingFired = createEmptyTrackingState();
996
+ var currentAdEventHandlers;
993
997
  var preloadSlots = /* @__PURE__ */ new Map();
994
998
  function emit(event, payload) {
995
999
  var set = listeners.get(event);
@@ -1051,14 +1055,26 @@ function createVastAdLayer(contentVideo, options) {
1051
1055
  var _ref;
1052
1056
  var _scoredFiles_;
1053
1057
  if (mediaFiles.length === 0) throw new Error("No media files available");
1054
- var firstFile = mediaFiles[0];
1055
- 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;
1056
1072
  var mainQuality = getMainStreamQuality();
1057
1073
  if (!mainQuality) {
1058
1074
  if (debug) console.log("".concat(LOG, " No main stream quality info, using first media file"));
1059
1075
  return firstFile;
1060
1076
  }
1061
- var scoredFiles = mediaFiles.map(function(file) {
1077
+ var scoredFiles = candidates.map(function(file) {
1062
1078
  var widthDiff = Math.abs(file.width - mainQuality.width);
1063
1079
  var heightDiff = Math.abs(file.height - mainQuality.height);
1064
1080
  var resolutionDiff = widthDiff + heightDiff;
@@ -1092,63 +1108,86 @@ function createVastAdLayer(contentVideo, options) {
1092
1108
  video.volume = 1;
1093
1109
  return video;
1094
1110
  }
1111
+ function removeAdEventListeners() {
1112
+ if (!currentAdEventHandlers || !adVideoElement) return;
1113
+ var el = adVideoElement;
1114
+ el.removeEventListener("timeupdate", currentAdEventHandlers.timeupdate);
1115
+ el.removeEventListener("playing", currentAdEventHandlers.playing);
1116
+ el.removeEventListener("ended", currentAdEventHandlers.ended);
1117
+ el.removeEventListener("error", currentAdEventHandlers.error);
1118
+ el.removeEventListener("volumechange", currentAdEventHandlers.volumechange);
1119
+ el.removeEventListener("pause", currentAdEventHandlers.pause);
1120
+ el.removeEventListener("play", currentAdEventHandlers.play);
1121
+ currentAdEventHandlers = void 0;
1122
+ }
1095
1123
  function setupAdEventListeners() {
1096
1124
  if (!adVideoElement) return;
1097
- adVideoElement.addEventListener("timeupdate", function() {
1098
- var ad = currentAd;
1099
- if (!ad || !adVideoElement) return;
1100
- var progress = adVideoElement.currentTime / ad.duration;
1101
- if (progress >= 0.25 && !trackingFired.firstQuartile) {
1102
- trackingFired.firstQuartile = true;
1103
- fireTrackingPixels2(ad.trackingUrls.firstQuartile);
1104
- }
1105
- if (progress >= 0.5 && !trackingFired.midpoint) {
1106
- trackingFired.midpoint = true;
1107
- fireTrackingPixels2(ad.trackingUrls.midpoint);
1108
- }
1109
- if (progress >= 0.75 && !trackingFired.thirdQuartile) {
1110
- trackingFired.thirdQuartile = true;
1111
- fireTrackingPixels2(ad.trackingUrls.thirdQuartile);
1112
- }
1113
- });
1114
- adVideoElement.addEventListener("playing", function() {
1115
- var ad = currentAd;
1116
- if (!ad || trackingFired.start) return;
1117
- trackingFired.start = true;
1118
- fireTrackingPixels2(ad.trackingUrls.start);
1119
- if (debug) console.log("".concat(LOG, " Ad started playing"));
1120
- });
1121
- adVideoElement.addEventListener("ended", function() {
1122
- if (tornDown || !currentAd || trackingFired.complete) return;
1123
- trackingFired.complete = true;
1124
- fireTrackingPixels2(currentAd.trackingUrls.complete);
1125
- if (debug) console.log("".concat(LOG, " Ad completed"));
1126
- handleAdComplete();
1127
- });
1128
- adVideoElement.addEventListener("error", function(e) {
1129
- if (tornDown) return;
1130
- console.error("".concat(LOG, " Ad video error:"), e);
1131
- if (currentAd) fireTrackingPixels2(currentAd.trackingUrls.error);
1132
- handleAdError();
1133
- });
1134
- adVideoElement.addEventListener("volumechange", function() {
1135
- if (!currentAd || !adVideoElement) return;
1136
- if (adVideoElement.muted) {
1137
- fireTrackingPixels2(currentAd.trackingUrls.mute);
1138
- } else {
1139
- fireTrackingPixels2(currentAd.trackingUrls.unmute);
1140
- }
1141
- });
1142
- adVideoElement.addEventListener("pause", function() {
1143
- if (currentAd && adVideoElement && !adVideoElement.ended) {
1144
- fireTrackingPixels2(currentAd.trackingUrls.pause);
1145
- }
1146
- });
1147
- adVideoElement.addEventListener("play", function() {
1148
- if (currentAd && adVideoElement && adVideoElement.currentTime > 0) {
1149
- fireTrackingPixels2(currentAd.trackingUrls.resume);
1125
+ removeAdEventListeners();
1126
+ var handlers = {
1127
+ timeupdate: function timeupdate() {
1128
+ var ad = currentAd;
1129
+ if (!ad || !adVideoElement) return;
1130
+ var progress = adVideoElement.currentTime / ad.duration;
1131
+ if (progress >= 0.25 && !trackingFired.firstQuartile) {
1132
+ trackingFired.firstQuartile = true;
1133
+ fireTrackingPixels2(ad.trackingUrls.firstQuartile);
1134
+ }
1135
+ if (progress >= 0.5 && !trackingFired.midpoint) {
1136
+ trackingFired.midpoint = true;
1137
+ fireTrackingPixels2(ad.trackingUrls.midpoint);
1138
+ }
1139
+ if (progress >= 0.75 && !trackingFired.thirdQuartile) {
1140
+ trackingFired.thirdQuartile = true;
1141
+ fireTrackingPixels2(ad.trackingUrls.thirdQuartile);
1142
+ }
1143
+ },
1144
+ playing: function playing() {
1145
+ var ad = currentAd;
1146
+ if (!ad || trackingFired.start) return;
1147
+ trackingFired.start = true;
1148
+ fireTrackingPixels2(ad.trackingUrls.start);
1149
+ if (debug) console.log("".concat(LOG, " Ad started playing"));
1150
+ },
1151
+ ended: function ended() {
1152
+ if (tornDown || !currentAd || trackingFired.complete) return;
1153
+ trackingFired.complete = true;
1154
+ fireTrackingPixels2(currentAd.trackingUrls.complete);
1155
+ if (debug) console.log("".concat(LOG, " Ad completed"));
1156
+ handleAdComplete();
1157
+ },
1158
+ error: function error(e) {
1159
+ if (tornDown) return;
1160
+ console.error("".concat(LOG, " Ad video error:"), e);
1161
+ if (currentAd) fireTrackingPixels2(currentAd.trackingUrls.error);
1162
+ handleAdError();
1163
+ },
1164
+ volumechange: function volumechange() {
1165
+ if (!currentAd || !adVideoElement) return;
1166
+ if (adVideoElement.muted) {
1167
+ fireTrackingPixels2(currentAd.trackingUrls.mute);
1168
+ } else {
1169
+ fireTrackingPixels2(currentAd.trackingUrls.unmute);
1170
+ }
1171
+ },
1172
+ pause: function pause() {
1173
+ if (currentAd && adVideoElement && !adVideoElement.ended) {
1174
+ fireTrackingPixels2(currentAd.trackingUrls.pause);
1175
+ }
1176
+ },
1177
+ play: function play() {
1178
+ if (currentAd && adVideoElement && adVideoElement.currentTime > 0) {
1179
+ fireTrackingPixels2(currentAd.trackingUrls.resume);
1180
+ }
1150
1181
  }
1151
- });
1182
+ };
1183
+ adVideoElement.addEventListener("timeupdate", handlers.timeupdate);
1184
+ adVideoElement.addEventListener("playing", handlers.playing);
1185
+ adVideoElement.addEventListener("ended", handlers.ended);
1186
+ adVideoElement.addEventListener("error", handlers.error);
1187
+ adVideoElement.addEventListener("volumechange", handlers.volumechange);
1188
+ adVideoElement.addEventListener("pause", handlers.pause);
1189
+ adVideoElement.addEventListener("play", handlers.play);
1190
+ currentAdEventHandlers = handlers;
1152
1191
  }
1153
1192
  function setAdPlayingFlag(isPlaying) {
1154
1193
  if (isPlaying) {
@@ -1171,6 +1210,7 @@ function createVastAdLayer(contentVideo, options) {
1171
1210
  }
1172
1211
  function handleAdError() {
1173
1212
  if (tornDown) return;
1213
+ if (!adPlaying) return;
1174
1214
  if (debug) console.log("".concat(LOG, " Handling ad error"));
1175
1215
  adPlaying = false;
1176
1216
  setAdPlayingFlag(false);
@@ -1181,14 +1221,19 @@ function createVastAdLayer(contentVideo, options) {
1181
1221
  emit("ad_error");
1182
1222
  }
1183
1223
  function teardownCurrentPlayback() {
1224
+ removeAdEventListeners();
1184
1225
  if (adHls) {
1185
1226
  adHls.destroy();
1186
1227
  adHls = void 0;
1187
1228
  }
1188
1229
  if (adVideoElement) {
1189
- adVideoElement.pause();
1190
- adVideoElement.removeAttribute("src");
1191
- adVideoElement.load();
1230
+ if (singleElementMode && adVideoElement === contentVideo) {
1231
+ contentVideo.pause();
1232
+ } else {
1233
+ adVideoElement.pause();
1234
+ adVideoElement.removeAttribute("src");
1235
+ adVideoElement.load();
1236
+ }
1192
1237
  }
1193
1238
  }
1194
1239
  function startNativePlayback(mediaFile) {
@@ -1221,8 +1266,17 @@ function createVastAdLayer(contentVideo, options) {
1221
1266
  handleAdError();
1222
1267
  });
1223
1268
  });
1269
+ var nonFatalNetworkErrors = 0;
1224
1270
  adHls.on(import_hls.default.Events.ERROR, function(_event, data) {
1225
- if (data.fatal) handleAdError();
1271
+ if (data.fatal) {
1272
+ handleAdError();
1273
+ } else if (data.type === import_hls.default.ErrorTypes.NETWORK_ERROR) {
1274
+ nonFatalNetworkErrors++;
1275
+ if (nonFatalNetworkErrors >= 3) {
1276
+ if (debug) console.warn("".concat(LOG, " Too many non-fatal HLS network errors (").concat(nonFatalNetworkErrors, "), treating as fatal"));
1277
+ handleAdError();
1278
+ }
1279
+ }
1226
1280
  });
1227
1281
  } else if (adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1228
1282
  adVideoElement.src = mediaFile.url;
@@ -1237,6 +1291,16 @@ function createVastAdLayer(contentVideo, options) {
1237
1291
  }
1238
1292
  function startPlayback(mediaFile) {
1239
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
+ }
1240
1304
  if (isHlsMediaFile(mediaFile)) {
1241
1305
  startHlsPlayback(mediaFile);
1242
1306
  } else {
@@ -1245,7 +1309,7 @@ function createVastAdLayer(contentVideo, options) {
1245
1309
  }
1246
1310
  function playAd(bids) {
1247
1311
  return _async_to_generator(function() {
1248
- var winner, ad, _contentVideo_parentElement, container, contentVolume, adVolume, mediaFile;
1312
+ var winner, ad, contentVolume, _contentVideo_parentElement, container, adVolume, mediaFile;
1249
1313
  return _ts_generator(this, function(_state) {
1250
1314
  switch(_state.label){
1251
1315
  case 0:
@@ -1287,32 +1351,40 @@ function createVastAdLayer(contentVideo, options) {
1287
1351
  trackingFired = _object_spread({}, createEmptyTrackingState());
1288
1352
  fireTrackingPixels2(ad.trackingUrls.impression);
1289
1353
  trackingFired.impression = true;
1290
- if (!adContainerEl) {
1291
- ;
1292
- container = document.createElement("div");
1293
- container.style.position = "absolute";
1294
- container.style.left = "0";
1295
- container.style.top = "0";
1296
- container.style.right = "0";
1297
- container.style.bottom = "0";
1298
- container.style.display = "none";
1299
- container.style.alignItems = "center";
1300
- container.style.justifyContent = "center";
1301
- container.style.pointerEvents = "none";
1302
- container.style.zIndex = "10";
1303
- container.style.backgroundColor = "#000";
1304
- (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1305
- adContainerEl = container;
1306
- }
1307
- if (!adVideoElement) {
1308
- adVideoElement = createAdVideoElement();
1309
- adContainerEl.appendChild(adVideoElement);
1354
+ contentVolume = contentVideo.volume;
1355
+ originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1356
+ if (singleElementMode) {
1357
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1358
+ teardownCurrentPlayback();
1359
+ adVideoElement = contentVideo;
1360
+ adHls = void 0;
1310
1361
  setupAdEventListeners();
1311
1362
  } else {
1312
- teardownCurrentPlayback();
1363
+ if (!adContainerEl) {
1364
+ ;
1365
+ container = document.createElement("div");
1366
+ container.style.position = "absolute";
1367
+ container.style.left = "0";
1368
+ container.style.top = "0";
1369
+ container.style.right = "0";
1370
+ container.style.bottom = "0";
1371
+ container.style.display = "none";
1372
+ container.style.alignItems = "center";
1373
+ container.style.justifyContent = "center";
1374
+ container.style.pointerEvents = "none";
1375
+ container.style.zIndex = "10";
1376
+ container.style.backgroundColor = "#000";
1377
+ (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1378
+ adContainerEl = container;
1379
+ }
1380
+ if (!adVideoElement) {
1381
+ adVideoElement = createAdVideoElement();
1382
+ adContainerEl.appendChild(adVideoElement);
1383
+ setupAdEventListeners();
1384
+ } else {
1385
+ teardownCurrentPlayback();
1386
+ }
1313
1387
  }
1314
- contentVolume = contentVideo.volume;
1315
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1316
1388
  if (!continueLiveStreamDuringAds) {
1317
1389
  contentVideo.pause();
1318
1390
  }
@@ -1323,7 +1395,7 @@ function createVastAdLayer(contentVideo, options) {
1323
1395
  adVolume = originalMutedState ? 1 : originalVolume;
1324
1396
  adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1325
1397
  adVideoElement.muted = false;
1326
- if (adContainerEl) {
1398
+ if (!singleElementMode && adContainerEl) {
1327
1399
  adContainerEl.style.display = "flex";
1328
1400
  adContainerEl.style.pointerEvents = "auto";
1329
1401
  }
@@ -1360,7 +1432,7 @@ function createVastAdLayer(contentVideo, options) {
1360
1432
  }
1361
1433
  function preloadAd(bids, token) {
1362
1434
  return _async_to_generator(function() {
1363
- var winner, ad, mediaFile, videoEl, container, hls, slot, slot1;
1435
+ var winner, ad, mediaFile, slot, videoEl, container, hls, slot1, slot2;
1364
1436
  return _ts_generator(this, function(_state) {
1365
1437
  switch(_state.label){
1366
1438
  case 0:
@@ -1385,6 +1457,20 @@ function createVastAdLayer(contentVideo, options) {
1385
1457
  if (!mediaFile) return [
1386
1458
  2
1387
1459
  ];
1460
+ if (smartTVMode || singleElementMode) {
1461
+ slot = {
1462
+ bids: bids,
1463
+ ad: ad,
1464
+ mediaFile: mediaFile,
1465
+ videoEl: null,
1466
+ ready: true
1467
+ };
1468
+ preloadSlots.set(token, slot);
1469
+ if (debug) console.log("".concat(LOG, " [preload] Metadata-only preload (smartTV/singleElement), token=").concat(token, ", url=").concat(mediaFile.url));
1470
+ return [
1471
+ 2
1472
+ ];
1473
+ }
1388
1474
  videoEl = createAdVideoElement();
1389
1475
  videoEl.style.visibility = "hidden";
1390
1476
  videoEl.style.pointerEvents = "none";
@@ -1398,7 +1484,7 @@ function createVastAdLayer(contentVideo, options) {
1398
1484
  });
1399
1485
  hls.loadSource(mediaFile.url);
1400
1486
  hls.attachMedia(videoEl);
1401
- slot = {
1487
+ slot1 = {
1402
1488
  bids: bids,
1403
1489
  ad: ad,
1404
1490
  mediaFile: mediaFile,
@@ -1406,7 +1492,7 @@ function createVastAdLayer(contentVideo, options) {
1406
1492
  hlsInstance: hls,
1407
1493
  ready: false
1408
1494
  };
1409
- preloadSlots.set(token, slot);
1495
+ preloadSlots.set(token, slot1);
1410
1496
  hls.on(import_hls.default.Events.MANIFEST_PARSED, function() {
1411
1497
  var s = preloadSlots.get(token);
1412
1498
  if (s) s.ready = true;
@@ -1423,14 +1509,14 @@ function createVastAdLayer(contentVideo, options) {
1423
1509
  } else {
1424
1510
  videoEl.src = mediaFile.url;
1425
1511
  videoEl.load();
1426
- slot1 = {
1512
+ slot2 = {
1427
1513
  bids: bids,
1428
1514
  ad: ad,
1429
1515
  mediaFile: mediaFile,
1430
1516
  videoEl: videoEl,
1431
1517
  ready: false
1432
1518
  };
1433
- preloadSlots.set(token, slot1);
1519
+ preloadSlots.set(token, slot2);
1434
1520
  videoEl.addEventListener("canplay", function() {
1435
1521
  var s = preloadSlots.get(token);
1436
1522
  if (s) s.ready = true;
@@ -1449,7 +1535,7 @@ function createVastAdLayer(contentVideo, options) {
1449
1535
  }
1450
1536
  function playPreloaded(token) {
1451
1537
  return _async_to_generator(function() {
1452
- var slot, contentVolume, adVolume, container;
1538
+ var slot, contentVolume, adVolume2, videoEl, container2, adVolume21, adVolume, container;
1453
1539
  return _ts_generator(this, function(_state) {
1454
1540
  if (destroyed) return [
1455
1541
  2,
@@ -1464,6 +1550,68 @@ function createVastAdLayer(contentVideo, options) {
1464
1550
  }
1465
1551
  preloadSlots.delete(token);
1466
1552
  if (debug) console.log("".concat(LOG, " [preload] Playing preloaded ad, token=").concat(token, ", ready=").concat(slot.ready));
1553
+ contentVolume = contentVideo.volume;
1554
+ originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1555
+ sessionId = generateSessionId();
1556
+ currentAd = slot.ad;
1557
+ trackingFired = _object_spread({}, createEmptyTrackingState());
1558
+ fireTrackingPixels2(slot.ad.trackingUrls.impression);
1559
+ trackingFired.impression = true;
1560
+ if (singleElementMode) {
1561
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1562
+ teardownCurrentPlayback();
1563
+ adVideoElement = contentVideo;
1564
+ adHls = void 0;
1565
+ setupAdEventListeners();
1566
+ if (!continueLiveStreamDuringAds) {
1567
+ contentVideo.pause();
1568
+ }
1569
+ contentVideo.muted = true;
1570
+ contentVideo.volume = 0;
1571
+ adPlaying = true;
1572
+ setAdPlayingFlag(true);
1573
+ adVolume2 = originalMutedState ? 1 : originalVolume;
1574
+ contentVideo.volume = Math.max(0, Math.min(1, adVolume2));
1575
+ contentVideo.muted = false;
1576
+ emit("content_pause");
1577
+ if (debug) console.log("".concat(LOG, " [preload] singleElementMode: attaching ad to contentVideo, url=").concat(slot.mediaFile.url));
1578
+ startPlayback(slot.mediaFile);
1579
+ return [
1580
+ 2
1581
+ ];
1582
+ }
1583
+ if (smartTVMode && !slot.videoEl) {
1584
+ teardownCurrentPlayback();
1585
+ if (adVideoElement) {
1586
+ adVideoElement.remove();
1587
+ adVideoElement = void 0;
1588
+ }
1589
+ videoEl = createAdVideoElement();
1590
+ videoEl.style.visibility = "visible";
1591
+ videoEl.style.pointerEvents = "none";
1592
+ container2 = ensureAdContainer();
1593
+ container2.appendChild(videoEl);
1594
+ adVideoElement = videoEl;
1595
+ setupAdEventListeners();
1596
+ if (!continueLiveStreamDuringAds) {
1597
+ contentVideo.pause();
1598
+ }
1599
+ contentVideo.muted = true;
1600
+ contentVideo.volume = 0;
1601
+ adPlaying = true;
1602
+ setAdPlayingFlag(true);
1603
+ adVolume21 = originalMutedState ? 1 : originalVolume;
1604
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume21));
1605
+ adVideoElement.muted = false;
1606
+ container2.style.display = "flex";
1607
+ container2.style.pointerEvents = "auto";
1608
+ emit("content_pause");
1609
+ if (debug) console.log("".concat(LOG, " [preload] smartTVMode deferred: creating video element and loading, url=").concat(slot.mediaFile.url));
1610
+ startPlayback(slot.mediaFile);
1611
+ return [
1612
+ 2
1613
+ ];
1614
+ }
1467
1615
  teardownCurrentPlayback();
1468
1616
  if (adVideoElement && adVideoElement !== slot.videoEl) {
1469
1617
  adVideoElement.remove();
@@ -1473,13 +1621,6 @@ function createVastAdLayer(contentVideo, options) {
1473
1621
  adVideoElement = slot.videoEl;
1474
1622
  adHls = slot.hlsInstance;
1475
1623
  setupAdEventListeners();
1476
- sessionId = generateSessionId();
1477
- currentAd = slot.ad;
1478
- trackingFired = _object_spread({}, createEmptyTrackingState());
1479
- fireTrackingPixels2(slot.ad.trackingUrls.impression);
1480
- trackingFired.impression = true;
1481
- contentVolume = contentVideo.volume;
1482
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1483
1624
  if (!continueLiveStreamDuringAds) {
1484
1625
  contentVideo.pause();
1485
1626
  }
@@ -1494,17 +1635,10 @@ function createVastAdLayer(contentVideo, options) {
1494
1635
  container.style.display = "flex";
1495
1636
  container.style.pointerEvents = "auto";
1496
1637
  emit("content_pause");
1497
- if (slot.hlsInstance) {
1498
- adVideoElement.play().catch(function(error) {
1499
- console.error("".concat(LOG, " [preload] Error playing preloaded HLS ad:"), error);
1500
- handleAdError();
1501
- });
1502
- } else {
1503
- adVideoElement.play().catch(function(error) {
1504
- console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1505
- handleAdError();
1506
- });
1507
- }
1638
+ adVideoElement.play().catch(function(error) {
1639
+ console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1640
+ handleAdError();
1641
+ });
1508
1642
  return [
1509
1643
  2
1510
1644
  ];
@@ -1518,10 +1652,12 @@ function createVastAdLayer(contentVideo, options) {
1518
1652
  if (slot.hlsInstance) {
1519
1653
  slot.hlsInstance.destroy();
1520
1654
  }
1521
- slot.videoEl.pause();
1522
- slot.videoEl.removeAttribute("src");
1523
- slot.videoEl.load();
1524
- slot.videoEl.remove();
1655
+ if (slot.videoEl) {
1656
+ slot.videoEl.pause();
1657
+ slot.videoEl.removeAttribute("src");
1658
+ slot.videoEl.load();
1659
+ slot.videoEl.remove();
1660
+ }
1525
1661
  if (debug) console.log("".concat(LOG, " [preload] Cancelled and cleaned up token=").concat(token));
1526
1662
  }
1527
1663
  return {
@@ -1569,20 +1705,27 @@ function createVastAdLayer(contentVideo, options) {
1569
1705
  setAdPlayingFlag(false);
1570
1706
  contentVideo.muted = originalMutedState;
1571
1707
  contentVideo.volume = originalMutedState ? 0 : originalVolume;
1572
- if (adContainerEl) {
1573
- adContainerEl.style.display = "none";
1574
- adContainerEl.style.pointerEvents = "none";
1575
- }
1576
1708
  contentVideo.style.visibility = "visible";
1577
1709
  contentVideo.style.opacity = "1";
1578
- if (continueLiveStreamDuringAds) {
1579
- contentVideo.play().catch(function() {});
1580
- }
1581
- teardownCurrentPlayback();
1582
- if (adVideoElement) {
1583
- adVideoElement.pause();
1584
- adVideoElement.removeAttribute("src");
1585
- adVideoElement.load();
1710
+ if (singleElementMode) {
1711
+ teardownCurrentPlayback();
1712
+ contentVideo.removeAttribute("src");
1713
+ contentVideo.load();
1714
+ adVideoElement = void 0;
1715
+ } else {
1716
+ if (adContainerEl) {
1717
+ adContainerEl.style.display = "none";
1718
+ adContainerEl.style.pointerEvents = "none";
1719
+ }
1720
+ if (continueLiveStreamDuringAds) {
1721
+ contentVideo.play().catch(function() {});
1722
+ }
1723
+ teardownCurrentPlayback();
1724
+ if (adVideoElement) {
1725
+ adVideoElement.pause();
1726
+ adVideoElement.removeAttribute("src");
1727
+ adVideoElement.load();
1728
+ }
1586
1729
  }
1587
1730
  currentAd = void 0;
1588
1731
  tornDown = false;
@@ -1622,9 +1765,14 @@ function createVastAdLayer(contentVideo, options) {
1622
1765
  }
1623
1766
  teardownCurrentPlayback();
1624
1767
  if (adVideoElement) {
1625
- adVideoElement.pause();
1626
- adVideoElement.removeAttribute("src");
1627
- adVideoElement.remove();
1768
+ if (singleElementMode && adVideoElement === contentVideo) {
1769
+ contentVideo.removeAttribute("src");
1770
+ contentVideo.load();
1771
+ } else {
1772
+ adVideoElement.pause();
1773
+ adVideoElement.removeAttribute("src");
1774
+ adVideoElement.remove();
1775
+ }
1628
1776
  adVideoElement = void 0;
1629
1777
  }
1630
1778
  if (adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.parentElement) {
@@ -2822,8 +2970,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2822
2970
  this.vastManager = createVastManager(config.debugAdTiming !== void 0 ? {
2823
2971
  debug: !!config.debugAdTiming
2824
2972
  } : {});
2973
+ var browserForAdLayer = detectBrowser();
2974
+ var isSinglePipeline = browserForAdLayer.isSmartTV || !!this.config.singlePipelineMode;
2825
2975
  this.adLayer = createVastAdLayer(this.video, {
2826
2976
  continueLiveStreamDuringAds: false,
2977
+ smartTVMode: isSinglePipeline,
2978
+ singleElementMode: isSinglePipeline,
2979
+ forceMP4Ads: isSinglePipeline,
2827
2980
  debug: !!config.debugAdTiming
2828
2981
  });
2829
2982
  }
@@ -3469,6 +3622,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3469
3622
  if (_this.fillerVideo) {
3470
3623
  _this.fillerVideo.style.display = "none";
3471
3624
  }
3625
+ if (!_this.adLayer.isAdPlaying()) {
3626
+ if (_this.config.debugAdTiming) {
3627
+ console.log("[StormcloudVideoPlayer] Filler video failed \u2014 restoring main video");
3628
+ }
3629
+ _this.adLayer.hidePlaceholder();
3630
+ if (_this.video.paused && _this.video.readyState >= 2) {
3631
+ var _this_video_play;
3632
+ (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
3633
+ }
3634
+ }
3472
3635
  });
3473
3636
  if (this.config.debugAdTiming) {
3474
3637
  console.log("[StormcloudVideoPlayer] Showing filler video layer");
@@ -4185,6 +4348,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4185
4348
  if (!this.isLiveStream) {
4186
4349
  return false;
4187
4350
  }
4351
+ if (this.config.singlePipelineMode) {
4352
+ return false;
4353
+ }
4354
+ var browser = detectBrowser();
4355
+ if (browser.isSmartTV) {
4356
+ return false;
4357
+ }
4188
4358
  return true;
4189
4359
  }
4190
4360
  },
@@ -4392,7 +4562,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4392
4562
  switch(_state.label){
4393
4563
  case 0:
4394
4564
  _loop = function() {
4395
- var remaining, prefetchContext, bids, err, bids1, remainingNow, delay, elapsed, remaining1, context, bids2, remainingNow1, err1;
4565
+ var remaining, prefetchContext, bids, err, bids1, remainingNow, urgentNeedAd, delay, elapsed, remaining1, context, bids2, remainingNow1, err1, sleepMs;
4396
4566
  return _ts_generator(this, function(_state) {
4397
4567
  switch(_state.label){
4398
4568
  case 0:
@@ -4528,7 +4698,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4528
4698
  "continue"
4529
4699
  ];
4530
4700
  case 12:
4531
- delay = _this.lastAdRequestTime ? _this.minAdRequestIntervalMs + (_this.consecutiveFailures > 0 ? backoffMs() : 0) : 0;
4701
+ urgentNeedAd = _this.inAdBreak && !_this.adLayer.isAdPlaying();
4702
+ delay = _this.lastAdRequestTime ? _this.minAdRequestIntervalMs + (!urgentNeedAd && _this.consecutiveFailures > 0 ? backoffMs() : 0) : 0;
4532
4703
  elapsed = Date.now() - _this.lastAdRequestTime;
4533
4704
  if (!(elapsed < delay && _this.lastAdRequestTime > 0)) return [
4534
4705
  3,
@@ -4647,10 +4818,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4647
4818
  24
4648
4819
  ];
4649
4820
  case 24:
4821
+ sleepMs = _this.inAdBreak && !_this.adLayer.isAdPlaying() ? 0 : backoffMs();
4650
4822
  return [
4651
4823
  4,
4652
4824
  new Promise(function(r) {
4653
- return setTimeout(r, backoffMs());
4825
+ return setTimeout(r, sleepMs);
4654
4826
  })
4655
4827
  ];
4656
4828
  case 25:
@@ -5329,42 +5501,71 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5329
5501
  this.video.volume = restoredVolume;
5330
5502
  }
5331
5503
  var browser = detectBrowser();
5332
- var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0;
5504
+ var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0 || !!this.config.singlePipelineMode;
5333
5505
  if (isSmartTV && this.hls) {
5334
- if (!restoredMuted) {
5335
- var hlsRef = this.hls;
5336
- var savedMuted = restoredMuted;
5337
- var savedVolume = restoredVolume;
5338
- var onManifestParsedRestore = function onManifestParsedRestore1() {
5339
- hlsRef.off(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5340
- if (!_this.inAdBreak && !_this.adLayer.isAdPlaying()) {
5341
- if (_this.video.muted !== savedMuted) _this.video.muted = savedMuted;
5342
- if (Math.abs(_this.video.volume - savedVolume) > 0.01) _this.video.volume = savedVolume;
5343
- if (_this.config.debugAdTiming) {
5344
- console.log("[StormcloudVideoPlayer] Smart TV: audio state restored on MANIFEST_PARSED after re-attach");
5506
+ var hlsRef = this.hls;
5507
+ var savedMuted = restoredMuted;
5508
+ var savedVolume = restoredVolume;
5509
+ var videoRef = this.video;
5510
+ var debugEnabled = this.config.debugAdTiming;
5511
+ var tryPlay = function tryPlay1(attempt) {
5512
+ var _videoRef_play;
5513
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5514
+ (_videoRef_play = videoRef.play()) === null || _videoRef_play === void 0 ? void 0 : _videoRef_play.catch(function() {
5515
+ if (attempt < 3) {
5516
+ if (debugEnabled) {
5517
+ console.log("[StormcloudVideoPlayer] Smart TV: play() retry ".concat(attempt + 1, "/3 in ").concat(500 * (attempt + 1), "ms"));
5345
5518
  }
5519
+ setTimeout(function() {
5520
+ return tryPlay(attempt + 1);
5521
+ }, 500 * (attempt + 1));
5346
5522
  }
5347
- };
5348
- hlsRef.on(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5349
- }
5350
- this.hls.attachMedia(this.video);
5351
- if (this.config.debugAdTiming) {
5352
- console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5523
+ });
5524
+ };
5525
+ var onManifestParsedRestore = function onManifestParsedRestore1() {
5526
+ hlsRef.off(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5527
+ if (!_this.inAdBreak && !_this.adLayer.isAdPlaying()) {
5528
+ if (videoRef.muted !== savedMuted) videoRef.muted = savedMuted;
5529
+ if (Math.abs(videoRef.volume - savedVolume) > 0.01) videoRef.volume = savedVolume;
5530
+ if (debugEnabled) {
5531
+ console.log("[StormcloudVideoPlayer] Smart TV: audio state restored on MANIFEST_PARSED after re-attach");
5532
+ }
5533
+ hlsRef.startLoad(-1);
5534
+ tryPlay(0);
5535
+ if (debugEnabled) {
5536
+ console.log("[StormcloudVideoPlayer] Smart TV: seeking to live edge and resuming playback after re-attach");
5537
+ }
5538
+ }
5539
+ };
5540
+ hlsRef.on(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5541
+ var pipelineDelayMs = 300;
5542
+ if (debugEnabled) {
5543
+ console.log("[StormcloudVideoPlayer] Smart TV: waiting ".concat(pipelineDelayMs, "ms for hardware pipeline release before re-attach"));
5353
5544
  }
5354
- }
5355
- if (this.shouldContinueLiveStreamDuringAds()) {
5356
- var _this_video_play;
5357
- if (this.config.debugAdTiming) {
5358
- if (this.video.paused) {
5359
- console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
5360
- } else {
5361
- console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
5545
+ setTimeout(function() {
5546
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5547
+ if (_this.hls) {
5548
+ _this.hls.attachMedia(_this.video);
5549
+ if (debugEnabled) {
5550
+ console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5551
+ }
5552
+ }
5553
+ }, pipelineDelayMs);
5554
+ } else {
5555
+ if (this.shouldContinueLiveStreamDuringAds()) {
5556
+ var _this_video_play;
5557
+ if (this.config.debugAdTiming) {
5558
+ if (this.video.paused) {
5559
+ console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
5560
+ } else {
5561
+ console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
5562
+ }
5362
5563
  }
5564
+ (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5565
+ } else if (this.video.paused) {
5566
+ var _this_video_play1;
5567
+ (_this_video_play1 = this.video.play()) === null || _this_video_play1 === void 0 ? void 0 : _this_video_play1.catch(function() {});
5363
5568
  }
5364
- (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5365
- } else if (this.video.paused) {
5366
- var _this_video_play1;
5367
- (_this_video_play1 = this.video.play()) === null || _this_video_play1 === void 0 ? void 0 : _this_video_play1.catch(function() {});
5368
5569
  }
5369
5570
  this.syncMainContentAudioWhenVisible();
5370
5571
  if (!restoredMuted) {
@@ -5415,6 +5616,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5415
5616
  console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Max consecutive failures reached (".concat(this.consecutiveFailures, "), ending ad break gracefully"));
5416
5617
  }
5417
5618
  this.handleAdPodComplete();
5619
+ return;
5620
+ }
5621
+ if (this.inAdBreak && !this.config.disableFiller) {
5622
+ if (this.config.debugAdTiming) {
5623
+ console.log("[CONTINUOUS-FETCH] Ad failure in active break \u2014 showing filler while awaiting next ad");
5624
+ }
5625
+ this.showPlaceholderLayer();
5626
+ this.adLayer.showPlaceholder();
5627
+ } else if (this.inAdBreak) {
5628
+ if (this.config.debugAdTiming) {
5629
+ console.log("[CONTINUOUS-FETCH] Ad failure with no filler \u2014 restoring main video temporarily");
5630
+ }
5631
+ this.adLayer.hidePlaceholder();
5632
+ if (!this.adLayer.isAdPlaying() && this.video.paused && this.video.readyState >= 2) {
5633
+ var _this_video_play;
5634
+ (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5635
+ }
5418
5636
  }
5419
5637
  }
5420
5638
  },