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.
@@ -922,14 +922,17 @@ function resolveBidToVastAd(winner, logPrefix) {
922
922
  return Promise.resolve(null);
923
923
  }
924
924
  function createVastAdLayer(contentVideo, options) {
925
- var _ref, _ref1;
925
+ var _ref, _ref1, _ref2, _ref3, _ref4;
926
926
  var adPlaying = false;
927
927
  var originalMutedState = false;
928
928
  var originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));
929
929
  var listeners = /* @__PURE__ */ new Map();
930
930
  var mainHlsInstance = options === null || options === void 0 ? void 0 : options.mainHlsInstance;
931
931
  var continueLiveStreamDuringAds = (_ref = options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) !== null && _ref !== void 0 ? _ref : false;
932
- var debug = (_ref1 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref1 !== void 0 ? _ref1 : false;
932
+ var smartTVMode = (_ref1 = options === null || options === void 0 ? void 0 : options.smartTVMode) !== null && _ref1 !== void 0 ? _ref1 : false;
933
+ var singleElementMode = (_ref2 = options === null || options === void 0 ? void 0 : options.singleElementMode) !== null && _ref2 !== void 0 ? _ref2 : false;
934
+ var forceMP4Ads = (_ref3 = options === null || options === void 0 ? void 0 : options.forceMP4Ads) !== null && _ref3 !== void 0 ? _ref3 : smartTVMode || singleElementMode;
935
+ var debug = (_ref4 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref4 !== void 0 ? _ref4 : false;
933
936
  var adVideoElement;
934
937
  var adHls;
935
938
  var adContainerEl;
@@ -938,6 +941,7 @@ function createVastAdLayer(contentVideo, options) {
938
941
  var destroyed = false;
939
942
  var tornDown = false;
940
943
  var trackingFired = createEmptyTrackingState();
944
+ var currentAdEventHandlers;
941
945
  var preloadSlots = /* @__PURE__ */ new Map();
942
946
  function emit(event, payload) {
943
947
  var set = listeners.get(event);
@@ -999,14 +1003,26 @@ function createVastAdLayer(contentVideo, options) {
999
1003
  var _ref;
1000
1004
  var _scoredFiles_;
1001
1005
  if (mediaFiles.length === 0) throw new Error("No media files available");
1002
- var firstFile = mediaFiles[0];
1003
- if (mediaFiles.length === 1) return firstFile;
1006
+ var candidates = mediaFiles;
1007
+ if (forceMP4Ads) {
1008
+ var mp4Only = candidates.filter(function(f) {
1009
+ return !isHlsMediaFile(f);
1010
+ });
1011
+ if (mp4Only.length > 0) {
1012
+ candidates = mp4Only;
1013
+ if (debug) console.log("".concat(LOG, " forceMP4Ads: filtered to ").concat(mp4Only.length, " MP4-only file(s)"));
1014
+ } else if (debug) {
1015
+ console.warn("".concat(LOG, " forceMP4Ads: no MP4 files available, falling back to all media files"));
1016
+ }
1017
+ }
1018
+ var firstFile = candidates[0];
1019
+ if (candidates.length === 1) return firstFile;
1004
1020
  var mainQuality = getMainStreamQuality();
1005
1021
  if (!mainQuality) {
1006
1022
  if (debug) console.log("".concat(LOG, " No main stream quality info, using first media file"));
1007
1023
  return firstFile;
1008
1024
  }
1009
- var scoredFiles = mediaFiles.map(function(file) {
1025
+ var scoredFiles = candidates.map(function(file) {
1010
1026
  var widthDiff = Math.abs(file.width - mainQuality.width);
1011
1027
  var heightDiff = Math.abs(file.height - mainQuality.height);
1012
1028
  var resolutionDiff = widthDiff + heightDiff;
@@ -1040,63 +1056,86 @@ function createVastAdLayer(contentVideo, options) {
1040
1056
  video.volume = 1;
1041
1057
  return video;
1042
1058
  }
1059
+ function removeAdEventListeners() {
1060
+ if (!currentAdEventHandlers || !adVideoElement) return;
1061
+ var el = adVideoElement;
1062
+ el.removeEventListener("timeupdate", currentAdEventHandlers.timeupdate);
1063
+ el.removeEventListener("playing", currentAdEventHandlers.playing);
1064
+ el.removeEventListener("ended", currentAdEventHandlers.ended);
1065
+ el.removeEventListener("error", currentAdEventHandlers.error);
1066
+ el.removeEventListener("volumechange", currentAdEventHandlers.volumechange);
1067
+ el.removeEventListener("pause", currentAdEventHandlers.pause);
1068
+ el.removeEventListener("play", currentAdEventHandlers.play);
1069
+ currentAdEventHandlers = void 0;
1070
+ }
1043
1071
  function setupAdEventListeners() {
1044
1072
  if (!adVideoElement) return;
1045
- adVideoElement.addEventListener("timeupdate", function() {
1046
- var ad = currentAd;
1047
- if (!ad || !adVideoElement) return;
1048
- var progress = adVideoElement.currentTime / ad.duration;
1049
- if (progress >= 0.25 && !trackingFired.firstQuartile) {
1050
- trackingFired.firstQuartile = true;
1051
- fireTrackingPixels2(ad.trackingUrls.firstQuartile);
1052
- }
1053
- if (progress >= 0.5 && !trackingFired.midpoint) {
1054
- trackingFired.midpoint = true;
1055
- fireTrackingPixels2(ad.trackingUrls.midpoint);
1056
- }
1057
- if (progress >= 0.75 && !trackingFired.thirdQuartile) {
1058
- trackingFired.thirdQuartile = true;
1059
- fireTrackingPixels2(ad.trackingUrls.thirdQuartile);
1060
- }
1061
- });
1062
- adVideoElement.addEventListener("playing", function() {
1063
- var ad = currentAd;
1064
- if (!ad || trackingFired.start) return;
1065
- trackingFired.start = true;
1066
- fireTrackingPixels2(ad.trackingUrls.start);
1067
- if (debug) console.log("".concat(LOG, " Ad started playing"));
1068
- });
1069
- adVideoElement.addEventListener("ended", function() {
1070
- if (tornDown || !currentAd || trackingFired.complete) return;
1071
- trackingFired.complete = true;
1072
- fireTrackingPixels2(currentAd.trackingUrls.complete);
1073
- if (debug) console.log("".concat(LOG, " Ad completed"));
1074
- handleAdComplete();
1075
- });
1076
- adVideoElement.addEventListener("error", function(e) {
1077
- if (tornDown) return;
1078
- console.error("".concat(LOG, " Ad video error:"), e);
1079
- if (currentAd) fireTrackingPixels2(currentAd.trackingUrls.error);
1080
- handleAdError();
1081
- });
1082
- adVideoElement.addEventListener("volumechange", function() {
1083
- if (!currentAd || !adVideoElement) return;
1084
- if (adVideoElement.muted) {
1085
- fireTrackingPixels2(currentAd.trackingUrls.mute);
1086
- } else {
1087
- fireTrackingPixels2(currentAd.trackingUrls.unmute);
1088
- }
1089
- });
1090
- adVideoElement.addEventListener("pause", function() {
1091
- if (currentAd && adVideoElement && !adVideoElement.ended) {
1092
- fireTrackingPixels2(currentAd.trackingUrls.pause);
1093
- }
1094
- });
1095
- adVideoElement.addEventListener("play", function() {
1096
- if (currentAd && adVideoElement && adVideoElement.currentTime > 0) {
1097
- fireTrackingPixels2(currentAd.trackingUrls.resume);
1073
+ removeAdEventListeners();
1074
+ var handlers = {
1075
+ timeupdate: function timeupdate() {
1076
+ var ad = currentAd;
1077
+ if (!ad || !adVideoElement) return;
1078
+ var progress = adVideoElement.currentTime / ad.duration;
1079
+ if (progress >= 0.25 && !trackingFired.firstQuartile) {
1080
+ trackingFired.firstQuartile = true;
1081
+ fireTrackingPixels2(ad.trackingUrls.firstQuartile);
1082
+ }
1083
+ if (progress >= 0.5 && !trackingFired.midpoint) {
1084
+ trackingFired.midpoint = true;
1085
+ fireTrackingPixels2(ad.trackingUrls.midpoint);
1086
+ }
1087
+ if (progress >= 0.75 && !trackingFired.thirdQuartile) {
1088
+ trackingFired.thirdQuartile = true;
1089
+ fireTrackingPixels2(ad.trackingUrls.thirdQuartile);
1090
+ }
1091
+ },
1092
+ playing: function playing() {
1093
+ var ad = currentAd;
1094
+ if (!ad || trackingFired.start) return;
1095
+ trackingFired.start = true;
1096
+ fireTrackingPixels2(ad.trackingUrls.start);
1097
+ if (debug) console.log("".concat(LOG, " Ad started playing"));
1098
+ },
1099
+ ended: function ended() {
1100
+ if (tornDown || !currentAd || trackingFired.complete) return;
1101
+ trackingFired.complete = true;
1102
+ fireTrackingPixels2(currentAd.trackingUrls.complete);
1103
+ if (debug) console.log("".concat(LOG, " Ad completed"));
1104
+ handleAdComplete();
1105
+ },
1106
+ error: function error(e) {
1107
+ if (tornDown) return;
1108
+ console.error("".concat(LOG, " Ad video error:"), e);
1109
+ if (currentAd) fireTrackingPixels2(currentAd.trackingUrls.error);
1110
+ handleAdError();
1111
+ },
1112
+ volumechange: function volumechange() {
1113
+ if (!currentAd || !adVideoElement) return;
1114
+ if (adVideoElement.muted) {
1115
+ fireTrackingPixels2(currentAd.trackingUrls.mute);
1116
+ } else {
1117
+ fireTrackingPixels2(currentAd.trackingUrls.unmute);
1118
+ }
1119
+ },
1120
+ pause: function pause() {
1121
+ if (currentAd && adVideoElement && !adVideoElement.ended) {
1122
+ fireTrackingPixels2(currentAd.trackingUrls.pause);
1123
+ }
1124
+ },
1125
+ play: function play() {
1126
+ if (currentAd && adVideoElement && adVideoElement.currentTime > 0) {
1127
+ fireTrackingPixels2(currentAd.trackingUrls.resume);
1128
+ }
1098
1129
  }
1099
- });
1130
+ };
1131
+ adVideoElement.addEventListener("timeupdate", handlers.timeupdate);
1132
+ adVideoElement.addEventListener("playing", handlers.playing);
1133
+ adVideoElement.addEventListener("ended", handlers.ended);
1134
+ adVideoElement.addEventListener("error", handlers.error);
1135
+ adVideoElement.addEventListener("volumechange", handlers.volumechange);
1136
+ adVideoElement.addEventListener("pause", handlers.pause);
1137
+ adVideoElement.addEventListener("play", handlers.play);
1138
+ currentAdEventHandlers = handlers;
1100
1139
  }
1101
1140
  function setAdPlayingFlag(isPlaying) {
1102
1141
  if (isPlaying) {
@@ -1119,6 +1158,7 @@ function createVastAdLayer(contentVideo, options) {
1119
1158
  }
1120
1159
  function handleAdError() {
1121
1160
  if (tornDown) return;
1161
+ if (!adPlaying) return;
1122
1162
  if (debug) console.log("".concat(LOG, " Handling ad error"));
1123
1163
  adPlaying = false;
1124
1164
  setAdPlayingFlag(false);
@@ -1129,14 +1169,19 @@ function createVastAdLayer(contentVideo, options) {
1129
1169
  emit("ad_error");
1130
1170
  }
1131
1171
  function teardownCurrentPlayback() {
1172
+ removeAdEventListeners();
1132
1173
  if (adHls) {
1133
1174
  adHls.destroy();
1134
1175
  adHls = void 0;
1135
1176
  }
1136
1177
  if (adVideoElement) {
1137
- adVideoElement.pause();
1138
- adVideoElement.removeAttribute("src");
1139
- adVideoElement.load();
1178
+ if (singleElementMode && adVideoElement === contentVideo) {
1179
+ contentVideo.pause();
1180
+ } else {
1181
+ adVideoElement.pause();
1182
+ adVideoElement.removeAttribute("src");
1183
+ adVideoElement.load();
1184
+ }
1140
1185
  }
1141
1186
  }
1142
1187
  function startNativePlayback(mediaFile) {
@@ -1169,8 +1214,17 @@ function createVastAdLayer(contentVideo, options) {
1169
1214
  handleAdError();
1170
1215
  });
1171
1216
  });
1217
+ var nonFatalNetworkErrors = 0;
1172
1218
  adHls.on(import_hls.default.Events.ERROR, function(_event, data) {
1173
- if (data.fatal) handleAdError();
1219
+ if (data.fatal) {
1220
+ handleAdError();
1221
+ } else if (data.type === import_hls.default.ErrorTypes.NETWORK_ERROR) {
1222
+ nonFatalNetworkErrors++;
1223
+ if (nonFatalNetworkErrors >= 3) {
1224
+ if (debug) console.warn("".concat(LOG, " Too many non-fatal HLS network errors (").concat(nonFatalNetworkErrors, "), treating as fatal"));
1225
+ handleAdError();
1226
+ }
1227
+ }
1174
1228
  });
1175
1229
  } else if (adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1176
1230
  adVideoElement.src = mediaFile.url;
@@ -1185,6 +1239,16 @@ function createVastAdLayer(contentVideo, options) {
1185
1239
  }
1186
1240
  function startPlayback(mediaFile) {
1187
1241
  if (!adVideoElement) return;
1242
+ if (singleElementMode && isHlsMediaFile(mediaFile)) {
1243
+ var mp4Fallback = currentAd === null || currentAd === void 0 ? void 0 : currentAd.mediaFiles.find(function(f) {
1244
+ return !isHlsMediaFile(f);
1245
+ });
1246
+ if (mp4Fallback) {
1247
+ if (debug) console.log("".concat(LOG, " singleElementMode: HLS ad blocked, using MP4 fallback"));
1248
+ startNativePlayback(mp4Fallback);
1249
+ return;
1250
+ }
1251
+ }
1188
1252
  if (isHlsMediaFile(mediaFile)) {
1189
1253
  startHlsPlayback(mediaFile);
1190
1254
  } else {
@@ -1193,7 +1257,7 @@ function createVastAdLayer(contentVideo, options) {
1193
1257
  }
1194
1258
  function playAd(bids) {
1195
1259
  return _async_to_generator(function() {
1196
- var winner, ad, _contentVideo_parentElement, container, contentVolume, adVolume, mediaFile;
1260
+ var winner, ad, contentVolume, _contentVideo_parentElement, container, adVolume, mediaFile;
1197
1261
  return _ts_generator(this, function(_state) {
1198
1262
  switch(_state.label){
1199
1263
  case 0:
@@ -1235,32 +1299,40 @@ function createVastAdLayer(contentVideo, options) {
1235
1299
  trackingFired = _object_spread({}, createEmptyTrackingState());
1236
1300
  fireTrackingPixels2(ad.trackingUrls.impression);
1237
1301
  trackingFired.impression = true;
1238
- if (!adContainerEl) {
1239
- ;
1240
- container = document.createElement("div");
1241
- container.style.position = "absolute";
1242
- container.style.left = "0";
1243
- container.style.top = "0";
1244
- container.style.right = "0";
1245
- container.style.bottom = "0";
1246
- container.style.display = "none";
1247
- container.style.alignItems = "center";
1248
- container.style.justifyContent = "center";
1249
- container.style.pointerEvents = "none";
1250
- container.style.zIndex = "10";
1251
- container.style.backgroundColor = "#000";
1252
- (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1253
- adContainerEl = container;
1254
- }
1255
- if (!adVideoElement) {
1256
- adVideoElement = createAdVideoElement();
1257
- adContainerEl.appendChild(adVideoElement);
1302
+ contentVolume = contentVideo.volume;
1303
+ originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1304
+ if (singleElementMode) {
1305
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1306
+ teardownCurrentPlayback();
1307
+ adVideoElement = contentVideo;
1308
+ adHls = void 0;
1258
1309
  setupAdEventListeners();
1259
1310
  } else {
1260
- teardownCurrentPlayback();
1311
+ if (!adContainerEl) {
1312
+ ;
1313
+ container = document.createElement("div");
1314
+ container.style.position = "absolute";
1315
+ container.style.left = "0";
1316
+ container.style.top = "0";
1317
+ container.style.right = "0";
1318
+ container.style.bottom = "0";
1319
+ container.style.display = "none";
1320
+ container.style.alignItems = "center";
1321
+ container.style.justifyContent = "center";
1322
+ container.style.pointerEvents = "none";
1323
+ container.style.zIndex = "10";
1324
+ container.style.backgroundColor = "#000";
1325
+ (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1326
+ adContainerEl = container;
1327
+ }
1328
+ if (!adVideoElement) {
1329
+ adVideoElement = createAdVideoElement();
1330
+ adContainerEl.appendChild(adVideoElement);
1331
+ setupAdEventListeners();
1332
+ } else {
1333
+ teardownCurrentPlayback();
1334
+ }
1261
1335
  }
1262
- contentVolume = contentVideo.volume;
1263
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1264
1336
  if (!continueLiveStreamDuringAds) {
1265
1337
  contentVideo.pause();
1266
1338
  }
@@ -1271,7 +1343,7 @@ function createVastAdLayer(contentVideo, options) {
1271
1343
  adVolume = originalMutedState ? 1 : originalVolume;
1272
1344
  adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1273
1345
  adVideoElement.muted = false;
1274
- if (adContainerEl) {
1346
+ if (!singleElementMode && adContainerEl) {
1275
1347
  adContainerEl.style.display = "flex";
1276
1348
  adContainerEl.style.pointerEvents = "auto";
1277
1349
  }
@@ -1308,7 +1380,7 @@ function createVastAdLayer(contentVideo, options) {
1308
1380
  }
1309
1381
  function preloadAd(bids, token) {
1310
1382
  return _async_to_generator(function() {
1311
- var winner, ad, mediaFile, videoEl, container, hls, slot, slot1;
1383
+ var winner, ad, mediaFile, slot, videoEl, container, hls, slot1, slot2;
1312
1384
  return _ts_generator(this, function(_state) {
1313
1385
  switch(_state.label){
1314
1386
  case 0:
@@ -1333,6 +1405,20 @@ function createVastAdLayer(contentVideo, options) {
1333
1405
  if (!mediaFile) return [
1334
1406
  2
1335
1407
  ];
1408
+ if (smartTVMode || singleElementMode) {
1409
+ slot = {
1410
+ bids: bids,
1411
+ ad: ad,
1412
+ mediaFile: mediaFile,
1413
+ videoEl: null,
1414
+ ready: true
1415
+ };
1416
+ preloadSlots.set(token, slot);
1417
+ if (debug) console.log("".concat(LOG, " [preload] Metadata-only preload (smartTV/singleElement), token=").concat(token, ", url=").concat(mediaFile.url));
1418
+ return [
1419
+ 2
1420
+ ];
1421
+ }
1336
1422
  videoEl = createAdVideoElement();
1337
1423
  videoEl.style.visibility = "hidden";
1338
1424
  videoEl.style.pointerEvents = "none";
@@ -1346,7 +1432,7 @@ function createVastAdLayer(contentVideo, options) {
1346
1432
  });
1347
1433
  hls.loadSource(mediaFile.url);
1348
1434
  hls.attachMedia(videoEl);
1349
- slot = {
1435
+ slot1 = {
1350
1436
  bids: bids,
1351
1437
  ad: ad,
1352
1438
  mediaFile: mediaFile,
@@ -1354,7 +1440,7 @@ function createVastAdLayer(contentVideo, options) {
1354
1440
  hlsInstance: hls,
1355
1441
  ready: false
1356
1442
  };
1357
- preloadSlots.set(token, slot);
1443
+ preloadSlots.set(token, slot1);
1358
1444
  hls.on(import_hls.default.Events.MANIFEST_PARSED, function() {
1359
1445
  var s = preloadSlots.get(token);
1360
1446
  if (s) s.ready = true;
@@ -1371,14 +1457,14 @@ function createVastAdLayer(contentVideo, options) {
1371
1457
  } else {
1372
1458
  videoEl.src = mediaFile.url;
1373
1459
  videoEl.load();
1374
- slot1 = {
1460
+ slot2 = {
1375
1461
  bids: bids,
1376
1462
  ad: ad,
1377
1463
  mediaFile: mediaFile,
1378
1464
  videoEl: videoEl,
1379
1465
  ready: false
1380
1466
  };
1381
- preloadSlots.set(token, slot1);
1467
+ preloadSlots.set(token, slot2);
1382
1468
  videoEl.addEventListener("canplay", function() {
1383
1469
  var s = preloadSlots.get(token);
1384
1470
  if (s) s.ready = true;
@@ -1397,7 +1483,7 @@ function createVastAdLayer(contentVideo, options) {
1397
1483
  }
1398
1484
  function playPreloaded(token) {
1399
1485
  return _async_to_generator(function() {
1400
- var slot, contentVolume, adVolume, container;
1486
+ var slot, contentVolume, adVolume2, videoEl, container2, adVolume21, adVolume, container;
1401
1487
  return _ts_generator(this, function(_state) {
1402
1488
  if (destroyed) return [
1403
1489
  2,
@@ -1412,6 +1498,68 @@ function createVastAdLayer(contentVideo, options) {
1412
1498
  }
1413
1499
  preloadSlots.delete(token);
1414
1500
  if (debug) console.log("".concat(LOG, " [preload] Playing preloaded ad, token=").concat(token, ", ready=").concat(slot.ready));
1501
+ contentVolume = contentVideo.volume;
1502
+ originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1503
+ sessionId = generateSessionId();
1504
+ currentAd = slot.ad;
1505
+ trackingFired = _object_spread({}, createEmptyTrackingState());
1506
+ fireTrackingPixels2(slot.ad.trackingUrls.impression);
1507
+ trackingFired.impression = true;
1508
+ if (singleElementMode) {
1509
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1510
+ teardownCurrentPlayback();
1511
+ adVideoElement = contentVideo;
1512
+ adHls = void 0;
1513
+ setupAdEventListeners();
1514
+ if (!continueLiveStreamDuringAds) {
1515
+ contentVideo.pause();
1516
+ }
1517
+ contentVideo.muted = true;
1518
+ contentVideo.volume = 0;
1519
+ adPlaying = true;
1520
+ setAdPlayingFlag(true);
1521
+ adVolume2 = originalMutedState ? 1 : originalVolume;
1522
+ contentVideo.volume = Math.max(0, Math.min(1, adVolume2));
1523
+ contentVideo.muted = false;
1524
+ emit("content_pause");
1525
+ if (debug) console.log("".concat(LOG, " [preload] singleElementMode: attaching ad to contentVideo, url=").concat(slot.mediaFile.url));
1526
+ startPlayback(slot.mediaFile);
1527
+ return [
1528
+ 2
1529
+ ];
1530
+ }
1531
+ if (smartTVMode && !slot.videoEl) {
1532
+ teardownCurrentPlayback();
1533
+ if (adVideoElement) {
1534
+ adVideoElement.remove();
1535
+ adVideoElement = void 0;
1536
+ }
1537
+ videoEl = createAdVideoElement();
1538
+ videoEl.style.visibility = "visible";
1539
+ videoEl.style.pointerEvents = "none";
1540
+ container2 = ensureAdContainer();
1541
+ container2.appendChild(videoEl);
1542
+ adVideoElement = videoEl;
1543
+ setupAdEventListeners();
1544
+ if (!continueLiveStreamDuringAds) {
1545
+ contentVideo.pause();
1546
+ }
1547
+ contentVideo.muted = true;
1548
+ contentVideo.volume = 0;
1549
+ adPlaying = true;
1550
+ setAdPlayingFlag(true);
1551
+ adVolume21 = originalMutedState ? 1 : originalVolume;
1552
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume21));
1553
+ adVideoElement.muted = false;
1554
+ container2.style.display = "flex";
1555
+ container2.style.pointerEvents = "auto";
1556
+ emit("content_pause");
1557
+ if (debug) console.log("".concat(LOG, " [preload] smartTVMode deferred: creating video element and loading, url=").concat(slot.mediaFile.url));
1558
+ startPlayback(slot.mediaFile);
1559
+ return [
1560
+ 2
1561
+ ];
1562
+ }
1415
1563
  teardownCurrentPlayback();
1416
1564
  if (adVideoElement && adVideoElement !== slot.videoEl) {
1417
1565
  adVideoElement.remove();
@@ -1421,13 +1569,6 @@ function createVastAdLayer(contentVideo, options) {
1421
1569
  adVideoElement = slot.videoEl;
1422
1570
  adHls = slot.hlsInstance;
1423
1571
  setupAdEventListeners();
1424
- sessionId = generateSessionId();
1425
- currentAd = slot.ad;
1426
- trackingFired = _object_spread({}, createEmptyTrackingState());
1427
- fireTrackingPixels2(slot.ad.trackingUrls.impression);
1428
- trackingFired.impression = true;
1429
- contentVolume = contentVideo.volume;
1430
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1431
1572
  if (!continueLiveStreamDuringAds) {
1432
1573
  contentVideo.pause();
1433
1574
  }
@@ -1442,17 +1583,10 @@ function createVastAdLayer(contentVideo, options) {
1442
1583
  container.style.display = "flex";
1443
1584
  container.style.pointerEvents = "auto";
1444
1585
  emit("content_pause");
1445
- if (slot.hlsInstance) {
1446
- adVideoElement.play().catch(function(error) {
1447
- console.error("".concat(LOG, " [preload] Error playing preloaded HLS ad:"), error);
1448
- handleAdError();
1449
- });
1450
- } else {
1451
- adVideoElement.play().catch(function(error) {
1452
- console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1453
- handleAdError();
1454
- });
1455
- }
1586
+ adVideoElement.play().catch(function(error) {
1587
+ console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1588
+ handleAdError();
1589
+ });
1456
1590
  return [
1457
1591
  2
1458
1592
  ];
@@ -1466,10 +1600,12 @@ function createVastAdLayer(contentVideo, options) {
1466
1600
  if (slot.hlsInstance) {
1467
1601
  slot.hlsInstance.destroy();
1468
1602
  }
1469
- slot.videoEl.pause();
1470
- slot.videoEl.removeAttribute("src");
1471
- slot.videoEl.load();
1472
- slot.videoEl.remove();
1603
+ if (slot.videoEl) {
1604
+ slot.videoEl.pause();
1605
+ slot.videoEl.removeAttribute("src");
1606
+ slot.videoEl.load();
1607
+ slot.videoEl.remove();
1608
+ }
1473
1609
  if (debug) console.log("".concat(LOG, " [preload] Cancelled and cleaned up token=").concat(token));
1474
1610
  }
1475
1611
  return {
@@ -1517,20 +1653,27 @@ function createVastAdLayer(contentVideo, options) {
1517
1653
  setAdPlayingFlag(false);
1518
1654
  contentVideo.muted = originalMutedState;
1519
1655
  contentVideo.volume = originalMutedState ? 0 : originalVolume;
1520
- if (adContainerEl) {
1521
- adContainerEl.style.display = "none";
1522
- adContainerEl.style.pointerEvents = "none";
1523
- }
1524
1656
  contentVideo.style.visibility = "visible";
1525
1657
  contentVideo.style.opacity = "1";
1526
- if (continueLiveStreamDuringAds) {
1527
- contentVideo.play().catch(function() {});
1528
- }
1529
- teardownCurrentPlayback();
1530
- if (adVideoElement) {
1531
- adVideoElement.pause();
1532
- adVideoElement.removeAttribute("src");
1533
- adVideoElement.load();
1658
+ if (singleElementMode) {
1659
+ teardownCurrentPlayback();
1660
+ contentVideo.removeAttribute("src");
1661
+ contentVideo.load();
1662
+ adVideoElement = void 0;
1663
+ } else {
1664
+ if (adContainerEl) {
1665
+ adContainerEl.style.display = "none";
1666
+ adContainerEl.style.pointerEvents = "none";
1667
+ }
1668
+ if (continueLiveStreamDuringAds) {
1669
+ contentVideo.play().catch(function() {});
1670
+ }
1671
+ teardownCurrentPlayback();
1672
+ if (adVideoElement) {
1673
+ adVideoElement.pause();
1674
+ adVideoElement.removeAttribute("src");
1675
+ adVideoElement.load();
1676
+ }
1534
1677
  }
1535
1678
  currentAd = void 0;
1536
1679
  tornDown = false;
@@ -1570,9 +1713,14 @@ function createVastAdLayer(contentVideo, options) {
1570
1713
  }
1571
1714
  teardownCurrentPlayback();
1572
1715
  if (adVideoElement) {
1573
- adVideoElement.pause();
1574
- adVideoElement.removeAttribute("src");
1575
- adVideoElement.remove();
1716
+ if (singleElementMode && adVideoElement === contentVideo) {
1717
+ contentVideo.removeAttribute("src");
1718
+ contentVideo.load();
1719
+ } else {
1720
+ adVideoElement.pause();
1721
+ adVideoElement.removeAttribute("src");
1722
+ adVideoElement.remove();
1723
+ }
1576
1724
  adVideoElement = void 0;
1577
1725
  }
1578
1726
  if (adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.parentElement) {
@@ -2770,8 +2918,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2770
2918
  this.vastManager = createVastManager(config.debugAdTiming !== void 0 ? {
2771
2919
  debug: !!config.debugAdTiming
2772
2920
  } : {});
2921
+ var browserForAdLayer = detectBrowser();
2922
+ var isSinglePipeline = browserForAdLayer.isSmartTV || !!this.config.singlePipelineMode;
2773
2923
  this.adLayer = createVastAdLayer(this.video, {
2774
2924
  continueLiveStreamDuringAds: false,
2925
+ smartTVMode: isSinglePipeline,
2926
+ singleElementMode: isSinglePipeline,
2927
+ forceMP4Ads: isSinglePipeline,
2775
2928
  debug: !!config.debugAdTiming
2776
2929
  });
2777
2930
  }
@@ -3417,6 +3570,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3417
3570
  if (_this.fillerVideo) {
3418
3571
  _this.fillerVideo.style.display = "none";
3419
3572
  }
3573
+ if (!_this.adLayer.isAdPlaying()) {
3574
+ if (_this.config.debugAdTiming) {
3575
+ console.log("[StormcloudVideoPlayer] Filler video failed \u2014 restoring main video");
3576
+ }
3577
+ _this.adLayer.hidePlaceholder();
3578
+ if (_this.video.paused && _this.video.readyState >= 2) {
3579
+ var _this_video_play;
3580
+ (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
3581
+ }
3582
+ }
3420
3583
  });
3421
3584
  if (this.config.debugAdTiming) {
3422
3585
  console.log("[StormcloudVideoPlayer] Showing filler video layer");
@@ -4133,6 +4296,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4133
4296
  if (!this.isLiveStream) {
4134
4297
  return false;
4135
4298
  }
4299
+ if (this.config.singlePipelineMode) {
4300
+ return false;
4301
+ }
4302
+ var browser = detectBrowser();
4303
+ if (browser.isSmartTV) {
4304
+ return false;
4305
+ }
4136
4306
  return true;
4137
4307
  }
4138
4308
  },
@@ -4340,7 +4510,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4340
4510
  switch(_state.label){
4341
4511
  case 0:
4342
4512
  _loop = function() {
4343
- var remaining, prefetchContext, bids, err, bids1, remainingNow, delay, elapsed, remaining1, context, bids2, remainingNow1, err1;
4513
+ var remaining, prefetchContext, bids, err, bids1, remainingNow, urgentNeedAd, delay, elapsed, remaining1, context, bids2, remainingNow1, err1, sleepMs;
4344
4514
  return _ts_generator(this, function(_state) {
4345
4515
  switch(_state.label){
4346
4516
  case 0:
@@ -4476,7 +4646,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4476
4646
  "continue"
4477
4647
  ];
4478
4648
  case 12:
4479
- delay = _this.lastAdRequestTime ? _this.minAdRequestIntervalMs + (_this.consecutiveFailures > 0 ? backoffMs() : 0) : 0;
4649
+ urgentNeedAd = _this.inAdBreak && !_this.adLayer.isAdPlaying();
4650
+ delay = _this.lastAdRequestTime ? _this.minAdRequestIntervalMs + (!urgentNeedAd && _this.consecutiveFailures > 0 ? backoffMs() : 0) : 0;
4480
4651
  elapsed = Date.now() - _this.lastAdRequestTime;
4481
4652
  if (!(elapsed < delay && _this.lastAdRequestTime > 0)) return [
4482
4653
  3,
@@ -4595,10 +4766,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4595
4766
  24
4596
4767
  ];
4597
4768
  case 24:
4769
+ sleepMs = _this.inAdBreak && !_this.adLayer.isAdPlaying() ? 0 : backoffMs();
4598
4770
  return [
4599
4771
  4,
4600
4772
  new Promise(function(r) {
4601
- return setTimeout(r, backoffMs());
4773
+ return setTimeout(r, sleepMs);
4602
4774
  })
4603
4775
  ];
4604
4776
  case 25:
@@ -5277,42 +5449,71 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5277
5449
  this.video.volume = restoredVolume;
5278
5450
  }
5279
5451
  var browser = detectBrowser();
5280
- var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0;
5452
+ var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0 || !!this.config.singlePipelineMode;
5281
5453
  if (isSmartTV && this.hls) {
5282
- if (!restoredMuted) {
5283
- var hlsRef = this.hls;
5284
- var savedMuted = restoredMuted;
5285
- var savedVolume = restoredVolume;
5286
- var onManifestParsedRestore = function onManifestParsedRestore1() {
5287
- hlsRef.off(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5288
- if (!_this.inAdBreak && !_this.adLayer.isAdPlaying()) {
5289
- if (_this.video.muted !== savedMuted) _this.video.muted = savedMuted;
5290
- if (Math.abs(_this.video.volume - savedVolume) > 0.01) _this.video.volume = savedVolume;
5291
- if (_this.config.debugAdTiming) {
5292
- console.log("[StormcloudVideoPlayer] Smart TV: audio state restored on MANIFEST_PARSED after re-attach");
5454
+ var hlsRef = this.hls;
5455
+ var savedMuted = restoredMuted;
5456
+ var savedVolume = restoredVolume;
5457
+ var videoRef = this.video;
5458
+ var debugEnabled = this.config.debugAdTiming;
5459
+ var tryPlay = function tryPlay1(attempt) {
5460
+ var _videoRef_play;
5461
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5462
+ (_videoRef_play = videoRef.play()) === null || _videoRef_play === void 0 ? void 0 : _videoRef_play.catch(function() {
5463
+ if (attempt < 3) {
5464
+ if (debugEnabled) {
5465
+ console.log("[StormcloudVideoPlayer] Smart TV: play() retry ".concat(attempt + 1, "/3 in ").concat(500 * (attempt + 1), "ms"));
5293
5466
  }
5467
+ setTimeout(function() {
5468
+ return tryPlay(attempt + 1);
5469
+ }, 500 * (attempt + 1));
5294
5470
  }
5295
- };
5296
- hlsRef.on(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5297
- }
5298
- this.hls.attachMedia(this.video);
5299
- if (this.config.debugAdTiming) {
5300
- console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5471
+ });
5472
+ };
5473
+ var onManifestParsedRestore = function onManifestParsedRestore1() {
5474
+ hlsRef.off(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5475
+ if (!_this.inAdBreak && !_this.adLayer.isAdPlaying()) {
5476
+ if (videoRef.muted !== savedMuted) videoRef.muted = savedMuted;
5477
+ if (Math.abs(videoRef.volume - savedVolume) > 0.01) videoRef.volume = savedVolume;
5478
+ if (debugEnabled) {
5479
+ console.log("[StormcloudVideoPlayer] Smart TV: audio state restored on MANIFEST_PARSED after re-attach");
5480
+ }
5481
+ hlsRef.startLoad(-1);
5482
+ tryPlay(0);
5483
+ if (debugEnabled) {
5484
+ console.log("[StormcloudVideoPlayer] Smart TV: seeking to live edge and resuming playback after re-attach");
5485
+ }
5486
+ }
5487
+ };
5488
+ hlsRef.on(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5489
+ var pipelineDelayMs = 300;
5490
+ if (debugEnabled) {
5491
+ console.log("[StormcloudVideoPlayer] Smart TV: waiting ".concat(pipelineDelayMs, "ms for hardware pipeline release before re-attach"));
5301
5492
  }
5302
- }
5303
- if (this.shouldContinueLiveStreamDuringAds()) {
5304
- var _this_video_play;
5305
- if (this.config.debugAdTiming) {
5306
- if (this.video.paused) {
5307
- console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
5308
- } else {
5309
- console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
5493
+ setTimeout(function() {
5494
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5495
+ if (_this.hls) {
5496
+ _this.hls.attachMedia(_this.video);
5497
+ if (debugEnabled) {
5498
+ console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5499
+ }
5500
+ }
5501
+ }, pipelineDelayMs);
5502
+ } else {
5503
+ if (this.shouldContinueLiveStreamDuringAds()) {
5504
+ var _this_video_play;
5505
+ if (this.config.debugAdTiming) {
5506
+ if (this.video.paused) {
5507
+ console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
5508
+ } else {
5509
+ console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
5510
+ }
5310
5511
  }
5512
+ (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5513
+ } else if (this.video.paused) {
5514
+ var _this_video_play1;
5515
+ (_this_video_play1 = this.video.play()) === null || _this_video_play1 === void 0 ? void 0 : _this_video_play1.catch(function() {});
5311
5516
  }
5312
- (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5313
- } else if (this.video.paused) {
5314
- var _this_video_play1;
5315
- (_this_video_play1 = this.video.play()) === null || _this_video_play1 === void 0 ? void 0 : _this_video_play1.catch(function() {});
5316
5517
  }
5317
5518
  this.syncMainContentAudioWhenVisible();
5318
5519
  if (!restoredMuted) {
@@ -5363,6 +5564,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5363
5564
  console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Max consecutive failures reached (".concat(this.consecutiveFailures, "), ending ad break gracefully"));
5364
5565
  }
5365
5566
  this.handleAdPodComplete();
5567
+ return;
5568
+ }
5569
+ if (this.inAdBreak && !this.config.disableFiller) {
5570
+ if (this.config.debugAdTiming) {
5571
+ console.log("[CONTINUOUS-FETCH] Ad failure in active break \u2014 showing filler while awaiting next ad");
5572
+ }
5573
+ this.showPlaceholderLayer();
5574
+ this.adLayer.showPlaceholder();
5575
+ } else if (this.inAdBreak) {
5576
+ if (this.config.debugAdTiming) {
5577
+ console.log("[CONTINUOUS-FETCH] Ad failure with no filler \u2014 restoring main video temporarily");
5578
+ }
5579
+ this.adLayer.hidePlaceholder();
5580
+ if (!this.adLayer.isAdPlaying() && this.video.paused && this.video.readyState >= 2) {
5581
+ var _this_video_play;
5582
+ (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5583
+ }
5366
5584
  }
5367
5585
  }
5368
5586
  },