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.
@@ -960,14 +960,17 @@ function resolveBidToVastAd(winner, logPrefix) {
960
960
  return Promise.resolve(null);
961
961
  }
962
962
  function createVastAdLayer(contentVideo, options) {
963
- var _ref, _ref1;
963
+ var _ref, _ref1, _ref2, _ref3, _ref4;
964
964
  var adPlaying = false;
965
965
  var originalMutedState = false;
966
966
  var originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));
967
967
  var listeners = /* @__PURE__ */ new Map();
968
968
  var mainHlsInstance = options === null || options === void 0 ? void 0 : options.mainHlsInstance;
969
969
  var continueLiveStreamDuringAds = (_ref = options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) !== null && _ref !== void 0 ? _ref : false;
970
- var debug = (_ref1 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref1 !== void 0 ? _ref1 : false;
970
+ var smartTVMode = (_ref1 = options === null || options === void 0 ? void 0 : options.smartTVMode) !== null && _ref1 !== void 0 ? _ref1 : false;
971
+ var singleElementMode = (_ref2 = options === null || options === void 0 ? void 0 : options.singleElementMode) !== null && _ref2 !== void 0 ? _ref2 : false;
972
+ var forceMP4Ads = (_ref3 = options === null || options === void 0 ? void 0 : options.forceMP4Ads) !== null && _ref3 !== void 0 ? _ref3 : smartTVMode || singleElementMode;
973
+ var debug = (_ref4 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref4 !== void 0 ? _ref4 : false;
971
974
  var adVideoElement;
972
975
  var adHls;
973
976
  var adContainerEl;
@@ -976,6 +979,7 @@ function createVastAdLayer(contentVideo, options) {
976
979
  var destroyed = false;
977
980
  var tornDown = false;
978
981
  var trackingFired = createEmptyTrackingState();
982
+ var currentAdEventHandlers;
979
983
  var preloadSlots = /* @__PURE__ */ new Map();
980
984
  function emit(event, payload) {
981
985
  var set = listeners.get(event);
@@ -1037,14 +1041,26 @@ function createVastAdLayer(contentVideo, options) {
1037
1041
  var _ref;
1038
1042
  var _scoredFiles_;
1039
1043
  if (mediaFiles.length === 0) throw new Error("No media files available");
1040
- var firstFile = mediaFiles[0];
1041
- if (mediaFiles.length === 1) return firstFile;
1044
+ var candidates = mediaFiles;
1045
+ if (forceMP4Ads) {
1046
+ var mp4Only = candidates.filter(function(f) {
1047
+ return !isHlsMediaFile(f);
1048
+ });
1049
+ if (mp4Only.length > 0) {
1050
+ candidates = mp4Only;
1051
+ if (debug) console.log("".concat(LOG, " forceMP4Ads: filtered to ").concat(mp4Only.length, " MP4-only file(s)"));
1052
+ } else if (debug) {
1053
+ console.warn("".concat(LOG, " forceMP4Ads: no MP4 files available, falling back to all media files"));
1054
+ }
1055
+ }
1056
+ var firstFile = candidates[0];
1057
+ if (candidates.length === 1) return firstFile;
1042
1058
  var mainQuality = getMainStreamQuality();
1043
1059
  if (!mainQuality) {
1044
1060
  if (debug) console.log("".concat(LOG, " No main stream quality info, using first media file"));
1045
1061
  return firstFile;
1046
1062
  }
1047
- var scoredFiles = mediaFiles.map(function(file) {
1063
+ var scoredFiles = candidates.map(function(file) {
1048
1064
  var widthDiff = Math.abs(file.width - mainQuality.width);
1049
1065
  var heightDiff = Math.abs(file.height - mainQuality.height);
1050
1066
  var resolutionDiff = widthDiff + heightDiff;
@@ -1078,63 +1094,86 @@ function createVastAdLayer(contentVideo, options) {
1078
1094
  video.volume = 1;
1079
1095
  return video;
1080
1096
  }
1097
+ function removeAdEventListeners() {
1098
+ if (!currentAdEventHandlers || !adVideoElement) return;
1099
+ var el = adVideoElement;
1100
+ el.removeEventListener("timeupdate", currentAdEventHandlers.timeupdate);
1101
+ el.removeEventListener("playing", currentAdEventHandlers.playing);
1102
+ el.removeEventListener("ended", currentAdEventHandlers.ended);
1103
+ el.removeEventListener("error", currentAdEventHandlers.error);
1104
+ el.removeEventListener("volumechange", currentAdEventHandlers.volumechange);
1105
+ el.removeEventListener("pause", currentAdEventHandlers.pause);
1106
+ el.removeEventListener("play", currentAdEventHandlers.play);
1107
+ currentAdEventHandlers = void 0;
1108
+ }
1081
1109
  function setupAdEventListeners() {
1082
1110
  if (!adVideoElement) return;
1083
- adVideoElement.addEventListener("timeupdate", function() {
1084
- var ad = currentAd;
1085
- if (!ad || !adVideoElement) return;
1086
- var progress = adVideoElement.currentTime / ad.duration;
1087
- if (progress >= 0.25 && !trackingFired.firstQuartile) {
1088
- trackingFired.firstQuartile = true;
1089
- fireTrackingPixels2(ad.trackingUrls.firstQuartile);
1090
- }
1091
- if (progress >= 0.5 && !trackingFired.midpoint) {
1092
- trackingFired.midpoint = true;
1093
- fireTrackingPixels2(ad.trackingUrls.midpoint);
1094
- }
1095
- if (progress >= 0.75 && !trackingFired.thirdQuartile) {
1096
- trackingFired.thirdQuartile = true;
1097
- fireTrackingPixels2(ad.trackingUrls.thirdQuartile);
1098
- }
1099
- });
1100
- adVideoElement.addEventListener("playing", function() {
1101
- var ad = currentAd;
1102
- if (!ad || trackingFired.start) return;
1103
- trackingFired.start = true;
1104
- fireTrackingPixels2(ad.trackingUrls.start);
1105
- if (debug) console.log("".concat(LOG, " Ad started playing"));
1106
- });
1107
- adVideoElement.addEventListener("ended", function() {
1108
- if (tornDown || !currentAd || trackingFired.complete) return;
1109
- trackingFired.complete = true;
1110
- fireTrackingPixels2(currentAd.trackingUrls.complete);
1111
- if (debug) console.log("".concat(LOG, " Ad completed"));
1112
- handleAdComplete();
1113
- });
1114
- adVideoElement.addEventListener("error", function(e) {
1115
- if (tornDown) return;
1116
- console.error("".concat(LOG, " Ad video error:"), e);
1117
- if (currentAd) fireTrackingPixels2(currentAd.trackingUrls.error);
1118
- handleAdError();
1119
- });
1120
- adVideoElement.addEventListener("volumechange", function() {
1121
- if (!currentAd || !adVideoElement) return;
1122
- if (adVideoElement.muted) {
1123
- fireTrackingPixels2(currentAd.trackingUrls.mute);
1124
- } else {
1125
- fireTrackingPixels2(currentAd.trackingUrls.unmute);
1126
- }
1127
- });
1128
- adVideoElement.addEventListener("pause", function() {
1129
- if (currentAd && adVideoElement && !adVideoElement.ended) {
1130
- fireTrackingPixels2(currentAd.trackingUrls.pause);
1131
- }
1132
- });
1133
- adVideoElement.addEventListener("play", function() {
1134
- if (currentAd && adVideoElement && adVideoElement.currentTime > 0) {
1135
- fireTrackingPixels2(currentAd.trackingUrls.resume);
1111
+ removeAdEventListeners();
1112
+ var handlers = {
1113
+ timeupdate: function timeupdate() {
1114
+ var ad = currentAd;
1115
+ if (!ad || !adVideoElement) return;
1116
+ var progress = adVideoElement.currentTime / ad.duration;
1117
+ if (progress >= 0.25 && !trackingFired.firstQuartile) {
1118
+ trackingFired.firstQuartile = true;
1119
+ fireTrackingPixels2(ad.trackingUrls.firstQuartile);
1120
+ }
1121
+ if (progress >= 0.5 && !trackingFired.midpoint) {
1122
+ trackingFired.midpoint = true;
1123
+ fireTrackingPixels2(ad.trackingUrls.midpoint);
1124
+ }
1125
+ if (progress >= 0.75 && !trackingFired.thirdQuartile) {
1126
+ trackingFired.thirdQuartile = true;
1127
+ fireTrackingPixels2(ad.trackingUrls.thirdQuartile);
1128
+ }
1129
+ },
1130
+ playing: function playing() {
1131
+ var ad = currentAd;
1132
+ if (!ad || trackingFired.start) return;
1133
+ trackingFired.start = true;
1134
+ fireTrackingPixels2(ad.trackingUrls.start);
1135
+ if (debug) console.log("".concat(LOG, " Ad started playing"));
1136
+ },
1137
+ ended: function ended() {
1138
+ if (tornDown || !currentAd || trackingFired.complete) return;
1139
+ trackingFired.complete = true;
1140
+ fireTrackingPixels2(currentAd.trackingUrls.complete);
1141
+ if (debug) console.log("".concat(LOG, " Ad completed"));
1142
+ handleAdComplete();
1143
+ },
1144
+ error: function error(e) {
1145
+ if (tornDown) return;
1146
+ console.error("".concat(LOG, " Ad video error:"), e);
1147
+ if (currentAd) fireTrackingPixels2(currentAd.trackingUrls.error);
1148
+ handleAdError();
1149
+ },
1150
+ volumechange: function volumechange() {
1151
+ if (!currentAd || !adVideoElement) return;
1152
+ if (adVideoElement.muted) {
1153
+ fireTrackingPixels2(currentAd.trackingUrls.mute);
1154
+ } else {
1155
+ fireTrackingPixels2(currentAd.trackingUrls.unmute);
1156
+ }
1157
+ },
1158
+ pause: function pause() {
1159
+ if (currentAd && adVideoElement && !adVideoElement.ended) {
1160
+ fireTrackingPixels2(currentAd.trackingUrls.pause);
1161
+ }
1162
+ },
1163
+ play: function play() {
1164
+ if (currentAd && adVideoElement && adVideoElement.currentTime > 0) {
1165
+ fireTrackingPixels2(currentAd.trackingUrls.resume);
1166
+ }
1136
1167
  }
1137
- });
1168
+ };
1169
+ adVideoElement.addEventListener("timeupdate", handlers.timeupdate);
1170
+ adVideoElement.addEventListener("playing", handlers.playing);
1171
+ adVideoElement.addEventListener("ended", handlers.ended);
1172
+ adVideoElement.addEventListener("error", handlers.error);
1173
+ adVideoElement.addEventListener("volumechange", handlers.volumechange);
1174
+ adVideoElement.addEventListener("pause", handlers.pause);
1175
+ adVideoElement.addEventListener("play", handlers.play);
1176
+ currentAdEventHandlers = handlers;
1138
1177
  }
1139
1178
  function setAdPlayingFlag(isPlaying) {
1140
1179
  if (isPlaying) {
@@ -1157,6 +1196,7 @@ function createVastAdLayer(contentVideo, options) {
1157
1196
  }
1158
1197
  function handleAdError() {
1159
1198
  if (tornDown) return;
1199
+ if (!adPlaying) return;
1160
1200
  if (debug) console.log("".concat(LOG, " Handling ad error"));
1161
1201
  adPlaying = false;
1162
1202
  setAdPlayingFlag(false);
@@ -1167,14 +1207,19 @@ function createVastAdLayer(contentVideo, options) {
1167
1207
  emit("ad_error");
1168
1208
  }
1169
1209
  function teardownCurrentPlayback() {
1210
+ removeAdEventListeners();
1170
1211
  if (adHls) {
1171
1212
  adHls.destroy();
1172
1213
  adHls = void 0;
1173
1214
  }
1174
1215
  if (adVideoElement) {
1175
- adVideoElement.pause();
1176
- adVideoElement.removeAttribute("src");
1177
- adVideoElement.load();
1216
+ if (singleElementMode && adVideoElement === contentVideo) {
1217
+ contentVideo.pause();
1218
+ } else {
1219
+ adVideoElement.pause();
1220
+ adVideoElement.removeAttribute("src");
1221
+ adVideoElement.load();
1222
+ }
1178
1223
  }
1179
1224
  }
1180
1225
  function startNativePlayback(mediaFile) {
@@ -1207,8 +1252,17 @@ function createVastAdLayer(contentVideo, options) {
1207
1252
  handleAdError();
1208
1253
  });
1209
1254
  });
1255
+ var nonFatalNetworkErrors = 0;
1210
1256
  adHls.on(import_hls.default.Events.ERROR, function(_event, data) {
1211
- if (data.fatal) handleAdError();
1257
+ if (data.fatal) {
1258
+ handleAdError();
1259
+ } else if (data.type === import_hls.default.ErrorTypes.NETWORK_ERROR) {
1260
+ nonFatalNetworkErrors++;
1261
+ if (nonFatalNetworkErrors >= 3) {
1262
+ if (debug) console.warn("".concat(LOG, " Too many non-fatal HLS network errors (").concat(nonFatalNetworkErrors, "), treating as fatal"));
1263
+ handleAdError();
1264
+ }
1265
+ }
1212
1266
  });
1213
1267
  } else if (adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1214
1268
  adVideoElement.src = mediaFile.url;
@@ -1223,6 +1277,16 @@ function createVastAdLayer(contentVideo, options) {
1223
1277
  }
1224
1278
  function startPlayback(mediaFile) {
1225
1279
  if (!adVideoElement) return;
1280
+ if (singleElementMode && isHlsMediaFile(mediaFile)) {
1281
+ var mp4Fallback = currentAd === null || currentAd === void 0 ? void 0 : currentAd.mediaFiles.find(function(f) {
1282
+ return !isHlsMediaFile(f);
1283
+ });
1284
+ if (mp4Fallback) {
1285
+ if (debug) console.log("".concat(LOG, " singleElementMode: HLS ad blocked, using MP4 fallback"));
1286
+ startNativePlayback(mp4Fallback);
1287
+ return;
1288
+ }
1289
+ }
1226
1290
  if (isHlsMediaFile(mediaFile)) {
1227
1291
  startHlsPlayback(mediaFile);
1228
1292
  } else {
@@ -1231,7 +1295,7 @@ function createVastAdLayer(contentVideo, options) {
1231
1295
  }
1232
1296
  function playAd(bids) {
1233
1297
  return _async_to_generator(function() {
1234
- var winner, ad, _contentVideo_parentElement, container, contentVolume, adVolume, mediaFile;
1298
+ var winner, ad, contentVolume, _contentVideo_parentElement, container, adVolume, mediaFile;
1235
1299
  return _ts_generator(this, function(_state) {
1236
1300
  switch(_state.label){
1237
1301
  case 0:
@@ -1273,32 +1337,40 @@ function createVastAdLayer(contentVideo, options) {
1273
1337
  trackingFired = _object_spread({}, createEmptyTrackingState());
1274
1338
  fireTrackingPixels2(ad.trackingUrls.impression);
1275
1339
  trackingFired.impression = true;
1276
- if (!adContainerEl) {
1277
- ;
1278
- container = document.createElement("div");
1279
- container.style.position = "absolute";
1280
- container.style.left = "0";
1281
- container.style.top = "0";
1282
- container.style.right = "0";
1283
- container.style.bottom = "0";
1284
- container.style.display = "none";
1285
- container.style.alignItems = "center";
1286
- container.style.justifyContent = "center";
1287
- container.style.pointerEvents = "none";
1288
- container.style.zIndex = "10";
1289
- container.style.backgroundColor = "#000";
1290
- (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1291
- adContainerEl = container;
1292
- }
1293
- if (!adVideoElement) {
1294
- adVideoElement = createAdVideoElement();
1295
- adContainerEl.appendChild(adVideoElement);
1340
+ contentVolume = contentVideo.volume;
1341
+ originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1342
+ if (singleElementMode) {
1343
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1344
+ teardownCurrentPlayback();
1345
+ adVideoElement = contentVideo;
1346
+ adHls = void 0;
1296
1347
  setupAdEventListeners();
1297
1348
  } else {
1298
- teardownCurrentPlayback();
1349
+ if (!adContainerEl) {
1350
+ ;
1351
+ container = document.createElement("div");
1352
+ container.style.position = "absolute";
1353
+ container.style.left = "0";
1354
+ container.style.top = "0";
1355
+ container.style.right = "0";
1356
+ container.style.bottom = "0";
1357
+ container.style.display = "none";
1358
+ container.style.alignItems = "center";
1359
+ container.style.justifyContent = "center";
1360
+ container.style.pointerEvents = "none";
1361
+ container.style.zIndex = "10";
1362
+ container.style.backgroundColor = "#000";
1363
+ (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1364
+ adContainerEl = container;
1365
+ }
1366
+ if (!adVideoElement) {
1367
+ adVideoElement = createAdVideoElement();
1368
+ adContainerEl.appendChild(adVideoElement);
1369
+ setupAdEventListeners();
1370
+ } else {
1371
+ teardownCurrentPlayback();
1372
+ }
1299
1373
  }
1300
- contentVolume = contentVideo.volume;
1301
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1302
1374
  if (!continueLiveStreamDuringAds) {
1303
1375
  contentVideo.pause();
1304
1376
  }
@@ -1309,7 +1381,7 @@ function createVastAdLayer(contentVideo, options) {
1309
1381
  adVolume = originalMutedState ? 1 : originalVolume;
1310
1382
  adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1311
1383
  adVideoElement.muted = false;
1312
- if (adContainerEl) {
1384
+ if (!singleElementMode && adContainerEl) {
1313
1385
  adContainerEl.style.display = "flex";
1314
1386
  adContainerEl.style.pointerEvents = "auto";
1315
1387
  }
@@ -1346,7 +1418,7 @@ function createVastAdLayer(contentVideo, options) {
1346
1418
  }
1347
1419
  function preloadAd(bids, token) {
1348
1420
  return _async_to_generator(function() {
1349
- var winner, ad, mediaFile, videoEl, container, hls, slot, slot1;
1421
+ var winner, ad, mediaFile, slot, videoEl, container, hls, slot1, slot2;
1350
1422
  return _ts_generator(this, function(_state) {
1351
1423
  switch(_state.label){
1352
1424
  case 0:
@@ -1371,6 +1443,20 @@ function createVastAdLayer(contentVideo, options) {
1371
1443
  if (!mediaFile) return [
1372
1444
  2
1373
1445
  ];
1446
+ if (smartTVMode || singleElementMode) {
1447
+ slot = {
1448
+ bids: bids,
1449
+ ad: ad,
1450
+ mediaFile: mediaFile,
1451
+ videoEl: null,
1452
+ ready: true
1453
+ };
1454
+ preloadSlots.set(token, slot);
1455
+ if (debug) console.log("".concat(LOG, " [preload] Metadata-only preload (smartTV/singleElement), token=").concat(token, ", url=").concat(mediaFile.url));
1456
+ return [
1457
+ 2
1458
+ ];
1459
+ }
1374
1460
  videoEl = createAdVideoElement();
1375
1461
  videoEl.style.visibility = "hidden";
1376
1462
  videoEl.style.pointerEvents = "none";
@@ -1384,7 +1470,7 @@ function createVastAdLayer(contentVideo, options) {
1384
1470
  });
1385
1471
  hls.loadSource(mediaFile.url);
1386
1472
  hls.attachMedia(videoEl);
1387
- slot = {
1473
+ slot1 = {
1388
1474
  bids: bids,
1389
1475
  ad: ad,
1390
1476
  mediaFile: mediaFile,
@@ -1392,7 +1478,7 @@ function createVastAdLayer(contentVideo, options) {
1392
1478
  hlsInstance: hls,
1393
1479
  ready: false
1394
1480
  };
1395
- preloadSlots.set(token, slot);
1481
+ preloadSlots.set(token, slot1);
1396
1482
  hls.on(import_hls.default.Events.MANIFEST_PARSED, function() {
1397
1483
  var s = preloadSlots.get(token);
1398
1484
  if (s) s.ready = true;
@@ -1409,14 +1495,14 @@ function createVastAdLayer(contentVideo, options) {
1409
1495
  } else {
1410
1496
  videoEl.src = mediaFile.url;
1411
1497
  videoEl.load();
1412
- slot1 = {
1498
+ slot2 = {
1413
1499
  bids: bids,
1414
1500
  ad: ad,
1415
1501
  mediaFile: mediaFile,
1416
1502
  videoEl: videoEl,
1417
1503
  ready: false
1418
1504
  };
1419
- preloadSlots.set(token, slot1);
1505
+ preloadSlots.set(token, slot2);
1420
1506
  videoEl.addEventListener("canplay", function() {
1421
1507
  var s = preloadSlots.get(token);
1422
1508
  if (s) s.ready = true;
@@ -1435,7 +1521,7 @@ function createVastAdLayer(contentVideo, options) {
1435
1521
  }
1436
1522
  function playPreloaded(token) {
1437
1523
  return _async_to_generator(function() {
1438
- var slot, contentVolume, adVolume, container;
1524
+ var slot, contentVolume, adVolume2, videoEl, container2, adVolume21, adVolume, container;
1439
1525
  return _ts_generator(this, function(_state) {
1440
1526
  if (destroyed) return [
1441
1527
  2,
@@ -1450,6 +1536,68 @@ function createVastAdLayer(contentVideo, options) {
1450
1536
  }
1451
1537
  preloadSlots.delete(token);
1452
1538
  if (debug) console.log("".concat(LOG, " [preload] Playing preloaded ad, token=").concat(token, ", ready=").concat(slot.ready));
1539
+ contentVolume = contentVideo.volume;
1540
+ originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1541
+ sessionId = generateSessionId();
1542
+ currentAd = slot.ad;
1543
+ trackingFired = _object_spread({}, createEmptyTrackingState());
1544
+ fireTrackingPixels2(slot.ad.trackingUrls.impression);
1545
+ trackingFired.impression = true;
1546
+ if (singleElementMode) {
1547
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1548
+ teardownCurrentPlayback();
1549
+ adVideoElement = contentVideo;
1550
+ adHls = void 0;
1551
+ setupAdEventListeners();
1552
+ if (!continueLiveStreamDuringAds) {
1553
+ contentVideo.pause();
1554
+ }
1555
+ contentVideo.muted = true;
1556
+ contentVideo.volume = 0;
1557
+ adPlaying = true;
1558
+ setAdPlayingFlag(true);
1559
+ adVolume2 = originalMutedState ? 1 : originalVolume;
1560
+ contentVideo.volume = Math.max(0, Math.min(1, adVolume2));
1561
+ contentVideo.muted = false;
1562
+ emit("content_pause");
1563
+ if (debug) console.log("".concat(LOG, " [preload] singleElementMode: attaching ad to contentVideo, url=").concat(slot.mediaFile.url));
1564
+ startPlayback(slot.mediaFile);
1565
+ return [
1566
+ 2
1567
+ ];
1568
+ }
1569
+ if (smartTVMode && !slot.videoEl) {
1570
+ teardownCurrentPlayback();
1571
+ if (adVideoElement) {
1572
+ adVideoElement.remove();
1573
+ adVideoElement = void 0;
1574
+ }
1575
+ videoEl = createAdVideoElement();
1576
+ videoEl.style.visibility = "visible";
1577
+ videoEl.style.pointerEvents = "none";
1578
+ container2 = ensureAdContainer();
1579
+ container2.appendChild(videoEl);
1580
+ adVideoElement = videoEl;
1581
+ setupAdEventListeners();
1582
+ if (!continueLiveStreamDuringAds) {
1583
+ contentVideo.pause();
1584
+ }
1585
+ contentVideo.muted = true;
1586
+ contentVideo.volume = 0;
1587
+ adPlaying = true;
1588
+ setAdPlayingFlag(true);
1589
+ adVolume21 = originalMutedState ? 1 : originalVolume;
1590
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume21));
1591
+ adVideoElement.muted = false;
1592
+ container2.style.display = "flex";
1593
+ container2.style.pointerEvents = "auto";
1594
+ emit("content_pause");
1595
+ if (debug) console.log("".concat(LOG, " [preload] smartTVMode deferred: creating video element and loading, url=").concat(slot.mediaFile.url));
1596
+ startPlayback(slot.mediaFile);
1597
+ return [
1598
+ 2
1599
+ ];
1600
+ }
1453
1601
  teardownCurrentPlayback();
1454
1602
  if (adVideoElement && adVideoElement !== slot.videoEl) {
1455
1603
  adVideoElement.remove();
@@ -1459,13 +1607,6 @@ function createVastAdLayer(contentVideo, options) {
1459
1607
  adVideoElement = slot.videoEl;
1460
1608
  adHls = slot.hlsInstance;
1461
1609
  setupAdEventListeners();
1462
- sessionId = generateSessionId();
1463
- currentAd = slot.ad;
1464
- trackingFired = _object_spread({}, createEmptyTrackingState());
1465
- fireTrackingPixels2(slot.ad.trackingUrls.impression);
1466
- trackingFired.impression = true;
1467
- contentVolume = contentVideo.volume;
1468
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1469
1610
  if (!continueLiveStreamDuringAds) {
1470
1611
  contentVideo.pause();
1471
1612
  }
@@ -1480,17 +1621,10 @@ function createVastAdLayer(contentVideo, options) {
1480
1621
  container.style.display = "flex";
1481
1622
  container.style.pointerEvents = "auto";
1482
1623
  emit("content_pause");
1483
- if (slot.hlsInstance) {
1484
- adVideoElement.play().catch(function(error) {
1485
- console.error("".concat(LOG, " [preload] Error playing preloaded HLS ad:"), error);
1486
- handleAdError();
1487
- });
1488
- } else {
1489
- adVideoElement.play().catch(function(error) {
1490
- console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1491
- handleAdError();
1492
- });
1493
- }
1624
+ adVideoElement.play().catch(function(error) {
1625
+ console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1626
+ handleAdError();
1627
+ });
1494
1628
  return [
1495
1629
  2
1496
1630
  ];
@@ -1504,10 +1638,12 @@ function createVastAdLayer(contentVideo, options) {
1504
1638
  if (slot.hlsInstance) {
1505
1639
  slot.hlsInstance.destroy();
1506
1640
  }
1507
- slot.videoEl.pause();
1508
- slot.videoEl.removeAttribute("src");
1509
- slot.videoEl.load();
1510
- slot.videoEl.remove();
1641
+ if (slot.videoEl) {
1642
+ slot.videoEl.pause();
1643
+ slot.videoEl.removeAttribute("src");
1644
+ slot.videoEl.load();
1645
+ slot.videoEl.remove();
1646
+ }
1511
1647
  if (debug) console.log("".concat(LOG, " [preload] Cancelled and cleaned up token=").concat(token));
1512
1648
  }
1513
1649
  return {
@@ -1555,20 +1691,27 @@ function createVastAdLayer(contentVideo, options) {
1555
1691
  setAdPlayingFlag(false);
1556
1692
  contentVideo.muted = originalMutedState;
1557
1693
  contentVideo.volume = originalMutedState ? 0 : originalVolume;
1558
- if (adContainerEl) {
1559
- adContainerEl.style.display = "none";
1560
- adContainerEl.style.pointerEvents = "none";
1561
- }
1562
1694
  contentVideo.style.visibility = "visible";
1563
1695
  contentVideo.style.opacity = "1";
1564
- if (continueLiveStreamDuringAds) {
1565
- contentVideo.play().catch(function() {});
1566
- }
1567
- teardownCurrentPlayback();
1568
- if (adVideoElement) {
1569
- adVideoElement.pause();
1570
- adVideoElement.removeAttribute("src");
1571
- adVideoElement.load();
1696
+ if (singleElementMode) {
1697
+ teardownCurrentPlayback();
1698
+ contentVideo.removeAttribute("src");
1699
+ contentVideo.load();
1700
+ adVideoElement = void 0;
1701
+ } else {
1702
+ if (adContainerEl) {
1703
+ adContainerEl.style.display = "none";
1704
+ adContainerEl.style.pointerEvents = "none";
1705
+ }
1706
+ if (continueLiveStreamDuringAds) {
1707
+ contentVideo.play().catch(function() {});
1708
+ }
1709
+ teardownCurrentPlayback();
1710
+ if (adVideoElement) {
1711
+ adVideoElement.pause();
1712
+ adVideoElement.removeAttribute("src");
1713
+ adVideoElement.load();
1714
+ }
1572
1715
  }
1573
1716
  currentAd = void 0;
1574
1717
  tornDown = false;
@@ -1608,9 +1751,14 @@ function createVastAdLayer(contentVideo, options) {
1608
1751
  }
1609
1752
  teardownCurrentPlayback();
1610
1753
  if (adVideoElement) {
1611
- adVideoElement.pause();
1612
- adVideoElement.removeAttribute("src");
1613
- adVideoElement.remove();
1754
+ if (singleElementMode && adVideoElement === contentVideo) {
1755
+ contentVideo.removeAttribute("src");
1756
+ contentVideo.load();
1757
+ } else {
1758
+ adVideoElement.pause();
1759
+ adVideoElement.removeAttribute("src");
1760
+ adVideoElement.remove();
1761
+ }
1614
1762
  adVideoElement = void 0;
1615
1763
  }
1616
1764
  if (adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.parentElement) {
@@ -2808,8 +2956,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2808
2956
  this.vastManager = createVastManager(config.debugAdTiming !== void 0 ? {
2809
2957
  debug: !!config.debugAdTiming
2810
2958
  } : {});
2959
+ var browserForAdLayer = detectBrowser();
2960
+ var isSinglePipeline = browserForAdLayer.isSmartTV || !!this.config.singlePipelineMode;
2811
2961
  this.adLayer = createVastAdLayer(this.video, {
2812
2962
  continueLiveStreamDuringAds: false,
2963
+ smartTVMode: isSinglePipeline,
2964
+ singleElementMode: isSinglePipeline,
2965
+ forceMP4Ads: isSinglePipeline,
2813
2966
  debug: !!config.debugAdTiming
2814
2967
  });
2815
2968
  }
@@ -3455,6 +3608,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3455
3608
  if (_this.fillerVideo) {
3456
3609
  _this.fillerVideo.style.display = "none";
3457
3610
  }
3611
+ if (!_this.adLayer.isAdPlaying()) {
3612
+ if (_this.config.debugAdTiming) {
3613
+ console.log("[StormcloudVideoPlayer] Filler video failed \u2014 restoring main video");
3614
+ }
3615
+ _this.adLayer.hidePlaceholder();
3616
+ if (_this.video.paused && _this.video.readyState >= 2) {
3617
+ var _this_video_play;
3618
+ (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
3619
+ }
3620
+ }
3458
3621
  });
3459
3622
  if (this.config.debugAdTiming) {
3460
3623
  console.log("[StormcloudVideoPlayer] Showing filler video layer");
@@ -4171,6 +4334,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4171
4334
  if (!this.isLiveStream) {
4172
4335
  return false;
4173
4336
  }
4337
+ if (this.config.singlePipelineMode) {
4338
+ return false;
4339
+ }
4340
+ var browser = detectBrowser();
4341
+ if (browser.isSmartTV) {
4342
+ return false;
4343
+ }
4174
4344
  return true;
4175
4345
  }
4176
4346
  },
@@ -4378,7 +4548,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4378
4548
  switch(_state.label){
4379
4549
  case 0:
4380
4550
  _loop = function() {
4381
- var remaining, prefetchContext, bids, err, bids1, remainingNow, delay, elapsed, remaining1, context, bids2, remainingNow1, err1;
4551
+ var remaining, prefetchContext, bids, err, bids1, remainingNow, urgentNeedAd, delay, elapsed, remaining1, context, bids2, remainingNow1, err1, sleepMs;
4382
4552
  return _ts_generator(this, function(_state) {
4383
4553
  switch(_state.label){
4384
4554
  case 0:
@@ -4514,7 +4684,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4514
4684
  "continue"
4515
4685
  ];
4516
4686
  case 12:
4517
- delay = _this.lastAdRequestTime ? _this.minAdRequestIntervalMs + (_this.consecutiveFailures > 0 ? backoffMs() : 0) : 0;
4687
+ urgentNeedAd = _this.inAdBreak && !_this.adLayer.isAdPlaying();
4688
+ delay = _this.lastAdRequestTime ? _this.minAdRequestIntervalMs + (!urgentNeedAd && _this.consecutiveFailures > 0 ? backoffMs() : 0) : 0;
4518
4689
  elapsed = Date.now() - _this.lastAdRequestTime;
4519
4690
  if (!(elapsed < delay && _this.lastAdRequestTime > 0)) return [
4520
4691
  3,
@@ -4633,10 +4804,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4633
4804
  24
4634
4805
  ];
4635
4806
  case 24:
4807
+ sleepMs = _this.inAdBreak && !_this.adLayer.isAdPlaying() ? 0 : backoffMs();
4636
4808
  return [
4637
4809
  4,
4638
4810
  new Promise(function(r) {
4639
- return setTimeout(r, backoffMs());
4811
+ return setTimeout(r, sleepMs);
4640
4812
  })
4641
4813
  ];
4642
4814
  case 25:
@@ -5315,42 +5487,71 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5315
5487
  this.video.volume = restoredVolume;
5316
5488
  }
5317
5489
  var browser = detectBrowser();
5318
- var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0;
5490
+ var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0 || !!this.config.singlePipelineMode;
5319
5491
  if (isSmartTV && this.hls) {
5320
- if (!restoredMuted) {
5321
- var hlsRef = this.hls;
5322
- var savedMuted = restoredMuted;
5323
- var savedVolume = restoredVolume;
5324
- var onManifestParsedRestore = function onManifestParsedRestore1() {
5325
- hlsRef.off(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5326
- if (!_this.inAdBreak && !_this.adLayer.isAdPlaying()) {
5327
- if (_this.video.muted !== savedMuted) _this.video.muted = savedMuted;
5328
- if (Math.abs(_this.video.volume - savedVolume) > 0.01) _this.video.volume = savedVolume;
5329
- if (_this.config.debugAdTiming) {
5330
- console.log("[StormcloudVideoPlayer] Smart TV: audio state restored on MANIFEST_PARSED after re-attach");
5492
+ var hlsRef = this.hls;
5493
+ var savedMuted = restoredMuted;
5494
+ var savedVolume = restoredVolume;
5495
+ var videoRef = this.video;
5496
+ var debugEnabled = this.config.debugAdTiming;
5497
+ var tryPlay = function tryPlay1(attempt) {
5498
+ var _videoRef_play;
5499
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5500
+ (_videoRef_play = videoRef.play()) === null || _videoRef_play === void 0 ? void 0 : _videoRef_play.catch(function() {
5501
+ if (attempt < 3) {
5502
+ if (debugEnabled) {
5503
+ console.log("[StormcloudVideoPlayer] Smart TV: play() retry ".concat(attempt + 1, "/3 in ").concat(500 * (attempt + 1), "ms"));
5331
5504
  }
5505
+ setTimeout(function() {
5506
+ return tryPlay(attempt + 1);
5507
+ }, 500 * (attempt + 1));
5332
5508
  }
5333
- };
5334
- hlsRef.on(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5335
- }
5336
- this.hls.attachMedia(this.video);
5337
- if (this.config.debugAdTiming) {
5338
- console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5509
+ });
5510
+ };
5511
+ var onManifestParsedRestore = function onManifestParsedRestore1() {
5512
+ hlsRef.off(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5513
+ if (!_this.inAdBreak && !_this.adLayer.isAdPlaying()) {
5514
+ if (videoRef.muted !== savedMuted) videoRef.muted = savedMuted;
5515
+ if (Math.abs(videoRef.volume - savedVolume) > 0.01) videoRef.volume = savedVolume;
5516
+ if (debugEnabled) {
5517
+ console.log("[StormcloudVideoPlayer] Smart TV: audio state restored on MANIFEST_PARSED after re-attach");
5518
+ }
5519
+ hlsRef.startLoad(-1);
5520
+ tryPlay(0);
5521
+ if (debugEnabled) {
5522
+ console.log("[StormcloudVideoPlayer] Smart TV: seeking to live edge and resuming playback after re-attach");
5523
+ }
5524
+ }
5525
+ };
5526
+ hlsRef.on(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5527
+ var pipelineDelayMs = 300;
5528
+ if (debugEnabled) {
5529
+ console.log("[StormcloudVideoPlayer] Smart TV: waiting ".concat(pipelineDelayMs, "ms for hardware pipeline release before re-attach"));
5339
5530
  }
5340
- }
5341
- if (this.shouldContinueLiveStreamDuringAds()) {
5342
- var _this_video_play;
5343
- if (this.config.debugAdTiming) {
5344
- if (this.video.paused) {
5345
- console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
5346
- } else {
5347
- console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
5531
+ setTimeout(function() {
5532
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5533
+ if (_this.hls) {
5534
+ _this.hls.attachMedia(_this.video);
5535
+ if (debugEnabled) {
5536
+ console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5537
+ }
5348
5538
  }
5539
+ }, pipelineDelayMs);
5540
+ } else {
5541
+ if (this.shouldContinueLiveStreamDuringAds()) {
5542
+ var _this_video_play;
5543
+ if (this.config.debugAdTiming) {
5544
+ if (this.video.paused) {
5545
+ console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
5546
+ } else {
5547
+ console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
5548
+ }
5549
+ }
5550
+ (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5551
+ } else if (this.video.paused) {
5552
+ var _this_video_play1;
5553
+ (_this_video_play1 = this.video.play()) === null || _this_video_play1 === void 0 ? void 0 : _this_video_play1.catch(function() {});
5349
5554
  }
5350
- (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5351
- } else if (this.video.paused) {
5352
- var _this_video_play1;
5353
- (_this_video_play1 = this.video.play()) === null || _this_video_play1 === void 0 ? void 0 : _this_video_play1.catch(function() {});
5354
5555
  }
5355
5556
  this.syncMainContentAudioWhenVisible();
5356
5557
  if (!restoredMuted) {
@@ -5401,6 +5602,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5401
5602
  console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Max consecutive failures reached (".concat(this.consecutiveFailures, "), ending ad break gracefully"));
5402
5603
  }
5403
5604
  this.handleAdPodComplete();
5605
+ return;
5606
+ }
5607
+ if (this.inAdBreak && !this.config.disableFiller) {
5608
+ if (this.config.debugAdTiming) {
5609
+ console.log("[CONTINUOUS-FETCH] Ad failure in active break \u2014 showing filler while awaiting next ad");
5610
+ }
5611
+ this.showPlaceholderLayer();
5612
+ this.adLayer.showPlaceholder();
5613
+ } else if (this.inAdBreak) {
5614
+ if (this.config.debugAdTiming) {
5615
+ console.log("[CONTINUOUS-FETCH] Ad failure with no filler \u2014 restoring main video temporarily");
5616
+ }
5617
+ this.adLayer.hidePlaceholder();
5618
+ if (!this.adLayer.isAdPlaying() && this.video.paused && this.video.readyState >= 2) {
5619
+ var _this_video_play;
5620
+ (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5621
+ }
5404
5622
  }
5405
5623
  }
5406
5624
  },