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.
@@ -1010,14 +1010,17 @@ function resolveBidToVastAd(winner, logPrefix) {
1010
1010
  return Promise.resolve(null);
1011
1011
  }
1012
1012
  function createVastAdLayer(contentVideo, options) {
1013
- var _ref, _ref1;
1013
+ var _ref, _ref1, _ref2, _ref3, _ref4;
1014
1014
  var adPlaying = false;
1015
1015
  var originalMutedState = false;
1016
1016
  var originalVolume = Math.max(0, Math.min(1, contentVideo.volume || 1));
1017
1017
  var listeners = /* @__PURE__ */ new Map();
1018
1018
  var mainHlsInstance = options === null || options === void 0 ? void 0 : options.mainHlsInstance;
1019
1019
  var continueLiveStreamDuringAds = (_ref = options === null || options === void 0 ? void 0 : options.continueLiveStreamDuringAds) !== null && _ref !== void 0 ? _ref : false;
1020
- var debug = (_ref1 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref1 !== void 0 ? _ref1 : false;
1020
+ var smartTVMode = (_ref1 = options === null || options === void 0 ? void 0 : options.smartTVMode) !== null && _ref1 !== void 0 ? _ref1 : false;
1021
+ var singleElementMode = (_ref2 = options === null || options === void 0 ? void 0 : options.singleElementMode) !== null && _ref2 !== void 0 ? _ref2 : false;
1022
+ var forceMP4Ads = (_ref3 = options === null || options === void 0 ? void 0 : options.forceMP4Ads) !== null && _ref3 !== void 0 ? _ref3 : smartTVMode || singleElementMode;
1023
+ var debug = (_ref4 = options === null || options === void 0 ? void 0 : options.debug) !== null && _ref4 !== void 0 ? _ref4 : false;
1021
1024
  var adVideoElement;
1022
1025
  var adHls;
1023
1026
  var adContainerEl;
@@ -1026,6 +1029,7 @@ function createVastAdLayer(contentVideo, options) {
1026
1029
  var destroyed = false;
1027
1030
  var tornDown = false;
1028
1031
  var trackingFired = createEmptyTrackingState();
1032
+ var currentAdEventHandlers;
1029
1033
  var preloadSlots = /* @__PURE__ */ new Map();
1030
1034
  function emit(event, payload) {
1031
1035
  var set = listeners.get(event);
@@ -1087,14 +1091,26 @@ function createVastAdLayer(contentVideo, options) {
1087
1091
  var _ref;
1088
1092
  var _scoredFiles_;
1089
1093
  if (mediaFiles.length === 0) throw new Error("No media files available");
1090
- var firstFile = mediaFiles[0];
1091
- if (mediaFiles.length === 1) return firstFile;
1094
+ var candidates = mediaFiles;
1095
+ if (forceMP4Ads) {
1096
+ var mp4Only = candidates.filter(function(f) {
1097
+ return !isHlsMediaFile(f);
1098
+ });
1099
+ if (mp4Only.length > 0) {
1100
+ candidates = mp4Only;
1101
+ if (debug) console.log("".concat(LOG, " forceMP4Ads: filtered to ").concat(mp4Only.length, " MP4-only file(s)"));
1102
+ } else if (debug) {
1103
+ console.warn("".concat(LOG, " forceMP4Ads: no MP4 files available, falling back to all media files"));
1104
+ }
1105
+ }
1106
+ var firstFile = candidates[0];
1107
+ if (candidates.length === 1) return firstFile;
1092
1108
  var mainQuality = getMainStreamQuality();
1093
1109
  if (!mainQuality) {
1094
1110
  if (debug) console.log("".concat(LOG, " No main stream quality info, using first media file"));
1095
1111
  return firstFile;
1096
1112
  }
1097
- var scoredFiles = mediaFiles.map(function(file) {
1113
+ var scoredFiles = candidates.map(function(file) {
1098
1114
  var widthDiff = Math.abs(file.width - mainQuality.width);
1099
1115
  var heightDiff = Math.abs(file.height - mainQuality.height);
1100
1116
  var resolutionDiff = widthDiff + heightDiff;
@@ -1128,63 +1144,86 @@ function createVastAdLayer(contentVideo, options) {
1128
1144
  video.volume = 1;
1129
1145
  return video;
1130
1146
  }
1147
+ function removeAdEventListeners() {
1148
+ if (!currentAdEventHandlers || !adVideoElement) return;
1149
+ var el = adVideoElement;
1150
+ el.removeEventListener("timeupdate", currentAdEventHandlers.timeupdate);
1151
+ el.removeEventListener("playing", currentAdEventHandlers.playing);
1152
+ el.removeEventListener("ended", currentAdEventHandlers.ended);
1153
+ el.removeEventListener("error", currentAdEventHandlers.error);
1154
+ el.removeEventListener("volumechange", currentAdEventHandlers.volumechange);
1155
+ el.removeEventListener("pause", currentAdEventHandlers.pause);
1156
+ el.removeEventListener("play", currentAdEventHandlers.play);
1157
+ currentAdEventHandlers = void 0;
1158
+ }
1131
1159
  function setupAdEventListeners() {
1132
1160
  if (!adVideoElement) return;
1133
- adVideoElement.addEventListener("timeupdate", function() {
1134
- var ad = currentAd;
1135
- if (!ad || !adVideoElement) return;
1136
- var progress = adVideoElement.currentTime / ad.duration;
1137
- if (progress >= 0.25 && !trackingFired.firstQuartile) {
1138
- trackingFired.firstQuartile = true;
1139
- fireTrackingPixels2(ad.trackingUrls.firstQuartile);
1140
- }
1141
- if (progress >= 0.5 && !trackingFired.midpoint) {
1142
- trackingFired.midpoint = true;
1143
- fireTrackingPixels2(ad.trackingUrls.midpoint);
1144
- }
1145
- if (progress >= 0.75 && !trackingFired.thirdQuartile) {
1146
- trackingFired.thirdQuartile = true;
1147
- fireTrackingPixels2(ad.trackingUrls.thirdQuartile);
1148
- }
1149
- });
1150
- adVideoElement.addEventListener("playing", function() {
1151
- var ad = currentAd;
1152
- if (!ad || trackingFired.start) return;
1153
- trackingFired.start = true;
1154
- fireTrackingPixels2(ad.trackingUrls.start);
1155
- if (debug) console.log("".concat(LOG, " Ad started playing"));
1156
- });
1157
- adVideoElement.addEventListener("ended", function() {
1158
- if (tornDown || !currentAd || trackingFired.complete) return;
1159
- trackingFired.complete = true;
1160
- fireTrackingPixels2(currentAd.trackingUrls.complete);
1161
- if (debug) console.log("".concat(LOG, " Ad completed"));
1162
- handleAdComplete();
1163
- });
1164
- adVideoElement.addEventListener("error", function(e) {
1165
- if (tornDown) return;
1166
- console.error("".concat(LOG, " Ad video error:"), e);
1167
- if (currentAd) fireTrackingPixels2(currentAd.trackingUrls.error);
1168
- handleAdError();
1169
- });
1170
- adVideoElement.addEventListener("volumechange", function() {
1171
- if (!currentAd || !adVideoElement) return;
1172
- if (adVideoElement.muted) {
1173
- fireTrackingPixels2(currentAd.trackingUrls.mute);
1174
- } else {
1175
- fireTrackingPixels2(currentAd.trackingUrls.unmute);
1176
- }
1177
- });
1178
- adVideoElement.addEventListener("pause", function() {
1179
- if (currentAd && adVideoElement && !adVideoElement.ended) {
1180
- fireTrackingPixels2(currentAd.trackingUrls.pause);
1181
- }
1182
- });
1183
- adVideoElement.addEventListener("play", function() {
1184
- if (currentAd && adVideoElement && adVideoElement.currentTime > 0) {
1185
- fireTrackingPixels2(currentAd.trackingUrls.resume);
1161
+ removeAdEventListeners();
1162
+ var handlers = {
1163
+ timeupdate: function timeupdate() {
1164
+ var ad = currentAd;
1165
+ if (!ad || !adVideoElement) return;
1166
+ var progress = adVideoElement.currentTime / ad.duration;
1167
+ if (progress >= 0.25 && !trackingFired.firstQuartile) {
1168
+ trackingFired.firstQuartile = true;
1169
+ fireTrackingPixels2(ad.trackingUrls.firstQuartile);
1170
+ }
1171
+ if (progress >= 0.5 && !trackingFired.midpoint) {
1172
+ trackingFired.midpoint = true;
1173
+ fireTrackingPixels2(ad.trackingUrls.midpoint);
1174
+ }
1175
+ if (progress >= 0.75 && !trackingFired.thirdQuartile) {
1176
+ trackingFired.thirdQuartile = true;
1177
+ fireTrackingPixels2(ad.trackingUrls.thirdQuartile);
1178
+ }
1179
+ },
1180
+ playing: function playing() {
1181
+ var ad = currentAd;
1182
+ if (!ad || trackingFired.start) return;
1183
+ trackingFired.start = true;
1184
+ fireTrackingPixels2(ad.trackingUrls.start);
1185
+ if (debug) console.log("".concat(LOG, " Ad started playing"));
1186
+ },
1187
+ ended: function ended() {
1188
+ if (tornDown || !currentAd || trackingFired.complete) return;
1189
+ trackingFired.complete = true;
1190
+ fireTrackingPixels2(currentAd.trackingUrls.complete);
1191
+ if (debug) console.log("".concat(LOG, " Ad completed"));
1192
+ handleAdComplete();
1193
+ },
1194
+ error: function error(e) {
1195
+ if (tornDown) return;
1196
+ console.error("".concat(LOG, " Ad video error:"), e);
1197
+ if (currentAd) fireTrackingPixels2(currentAd.trackingUrls.error);
1198
+ handleAdError();
1199
+ },
1200
+ volumechange: function volumechange() {
1201
+ if (!currentAd || !adVideoElement) return;
1202
+ if (adVideoElement.muted) {
1203
+ fireTrackingPixels2(currentAd.trackingUrls.mute);
1204
+ } else {
1205
+ fireTrackingPixels2(currentAd.trackingUrls.unmute);
1206
+ }
1207
+ },
1208
+ pause: function pause() {
1209
+ if (currentAd && adVideoElement && !adVideoElement.ended) {
1210
+ fireTrackingPixels2(currentAd.trackingUrls.pause);
1211
+ }
1212
+ },
1213
+ play: function play() {
1214
+ if (currentAd && adVideoElement && adVideoElement.currentTime > 0) {
1215
+ fireTrackingPixels2(currentAd.trackingUrls.resume);
1216
+ }
1186
1217
  }
1187
- });
1218
+ };
1219
+ adVideoElement.addEventListener("timeupdate", handlers.timeupdate);
1220
+ adVideoElement.addEventListener("playing", handlers.playing);
1221
+ adVideoElement.addEventListener("ended", handlers.ended);
1222
+ adVideoElement.addEventListener("error", handlers.error);
1223
+ adVideoElement.addEventListener("volumechange", handlers.volumechange);
1224
+ adVideoElement.addEventListener("pause", handlers.pause);
1225
+ adVideoElement.addEventListener("play", handlers.play);
1226
+ currentAdEventHandlers = handlers;
1188
1227
  }
1189
1228
  function setAdPlayingFlag(isPlaying) {
1190
1229
  if (isPlaying) {
@@ -1207,6 +1246,7 @@ function createVastAdLayer(contentVideo, options) {
1207
1246
  }
1208
1247
  function handleAdError() {
1209
1248
  if (tornDown) return;
1249
+ if (!adPlaying) return;
1210
1250
  if (debug) console.log("".concat(LOG, " Handling ad error"));
1211
1251
  adPlaying = false;
1212
1252
  setAdPlayingFlag(false);
@@ -1217,14 +1257,19 @@ function createVastAdLayer(contentVideo, options) {
1217
1257
  emit("ad_error");
1218
1258
  }
1219
1259
  function teardownCurrentPlayback() {
1260
+ removeAdEventListeners();
1220
1261
  if (adHls) {
1221
1262
  adHls.destroy();
1222
1263
  adHls = void 0;
1223
1264
  }
1224
1265
  if (adVideoElement) {
1225
- adVideoElement.pause();
1226
- adVideoElement.removeAttribute("src");
1227
- adVideoElement.load();
1266
+ if (singleElementMode && adVideoElement === contentVideo) {
1267
+ contentVideo.pause();
1268
+ } else {
1269
+ adVideoElement.pause();
1270
+ adVideoElement.removeAttribute("src");
1271
+ adVideoElement.load();
1272
+ }
1228
1273
  }
1229
1274
  }
1230
1275
  function startNativePlayback(mediaFile) {
@@ -1257,8 +1302,17 @@ function createVastAdLayer(contentVideo, options) {
1257
1302
  handleAdError();
1258
1303
  });
1259
1304
  });
1305
+ var nonFatalNetworkErrors = 0;
1260
1306
  adHls.on(import_hls.default.Events.ERROR, function(_event, data) {
1261
- if (data.fatal) handleAdError();
1307
+ if (data.fatal) {
1308
+ handleAdError();
1309
+ } else if (data.type === import_hls.default.ErrorTypes.NETWORK_ERROR) {
1310
+ nonFatalNetworkErrors++;
1311
+ if (nonFatalNetworkErrors >= 3) {
1312
+ if (debug) console.warn("".concat(LOG, " Too many non-fatal HLS network errors (").concat(nonFatalNetworkErrors, "), treating as fatal"));
1313
+ handleAdError();
1314
+ }
1315
+ }
1262
1316
  });
1263
1317
  } else if (adVideoElement.canPlayType("application/vnd.apple.mpegurl")) {
1264
1318
  adVideoElement.src = mediaFile.url;
@@ -1273,6 +1327,16 @@ function createVastAdLayer(contentVideo, options) {
1273
1327
  }
1274
1328
  function startPlayback(mediaFile) {
1275
1329
  if (!adVideoElement) return;
1330
+ if (singleElementMode && isHlsMediaFile(mediaFile)) {
1331
+ var mp4Fallback = currentAd === null || currentAd === void 0 ? void 0 : currentAd.mediaFiles.find(function(f) {
1332
+ return !isHlsMediaFile(f);
1333
+ });
1334
+ if (mp4Fallback) {
1335
+ if (debug) console.log("".concat(LOG, " singleElementMode: HLS ad blocked, using MP4 fallback"));
1336
+ startNativePlayback(mp4Fallback);
1337
+ return;
1338
+ }
1339
+ }
1276
1340
  if (isHlsMediaFile(mediaFile)) {
1277
1341
  startHlsPlayback(mediaFile);
1278
1342
  } else {
@@ -1281,7 +1345,7 @@ function createVastAdLayer(contentVideo, options) {
1281
1345
  }
1282
1346
  function playAd(bids) {
1283
1347
  return _async_to_generator(function() {
1284
- var winner, ad, _contentVideo_parentElement, container, contentVolume, adVolume, mediaFile;
1348
+ var winner, ad, contentVolume, _contentVideo_parentElement, container, adVolume, mediaFile;
1285
1349
  return _ts_generator(this, function(_state) {
1286
1350
  switch(_state.label){
1287
1351
  case 0:
@@ -1323,32 +1387,40 @@ function createVastAdLayer(contentVideo, options) {
1323
1387
  trackingFired = _object_spread({}, createEmptyTrackingState());
1324
1388
  fireTrackingPixels2(ad.trackingUrls.impression);
1325
1389
  trackingFired.impression = true;
1326
- if (!adContainerEl) {
1327
- ;
1328
- container = document.createElement("div");
1329
- container.style.position = "absolute";
1330
- container.style.left = "0";
1331
- container.style.top = "0";
1332
- container.style.right = "0";
1333
- container.style.bottom = "0";
1334
- container.style.display = "none";
1335
- container.style.alignItems = "center";
1336
- container.style.justifyContent = "center";
1337
- container.style.pointerEvents = "none";
1338
- container.style.zIndex = "10";
1339
- container.style.backgroundColor = "#000";
1340
- (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1341
- adContainerEl = container;
1342
- }
1343
- if (!adVideoElement) {
1344
- adVideoElement = createAdVideoElement();
1345
- adContainerEl.appendChild(adVideoElement);
1390
+ contentVolume = contentVideo.volume;
1391
+ originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1392
+ if (singleElementMode) {
1393
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1394
+ teardownCurrentPlayback();
1395
+ adVideoElement = contentVideo;
1396
+ adHls = void 0;
1346
1397
  setupAdEventListeners();
1347
1398
  } else {
1348
- teardownCurrentPlayback();
1399
+ if (!adContainerEl) {
1400
+ ;
1401
+ container = document.createElement("div");
1402
+ container.style.position = "absolute";
1403
+ container.style.left = "0";
1404
+ container.style.top = "0";
1405
+ container.style.right = "0";
1406
+ container.style.bottom = "0";
1407
+ container.style.display = "none";
1408
+ container.style.alignItems = "center";
1409
+ container.style.justifyContent = "center";
1410
+ container.style.pointerEvents = "none";
1411
+ container.style.zIndex = "10";
1412
+ container.style.backgroundColor = "#000";
1413
+ (_contentVideo_parentElement = contentVideo.parentElement) === null || _contentVideo_parentElement === void 0 ? void 0 : _contentVideo_parentElement.appendChild(container);
1414
+ adContainerEl = container;
1415
+ }
1416
+ if (!adVideoElement) {
1417
+ adVideoElement = createAdVideoElement();
1418
+ adContainerEl.appendChild(adVideoElement);
1419
+ setupAdEventListeners();
1420
+ } else {
1421
+ teardownCurrentPlayback();
1422
+ }
1349
1423
  }
1350
- contentVolume = contentVideo.volume;
1351
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1352
1424
  if (!continueLiveStreamDuringAds) {
1353
1425
  contentVideo.pause();
1354
1426
  }
@@ -1359,7 +1431,7 @@ function createVastAdLayer(contentVideo, options) {
1359
1431
  adVolume = originalMutedState ? 1 : originalVolume;
1360
1432
  adVideoElement.volume = Math.max(0, Math.min(1, adVolume));
1361
1433
  adVideoElement.muted = false;
1362
- if (adContainerEl) {
1434
+ if (!singleElementMode && adContainerEl) {
1363
1435
  adContainerEl.style.display = "flex";
1364
1436
  adContainerEl.style.pointerEvents = "auto";
1365
1437
  }
@@ -1396,7 +1468,7 @@ function createVastAdLayer(contentVideo, options) {
1396
1468
  }
1397
1469
  function preloadAd(bids, token) {
1398
1470
  return _async_to_generator(function() {
1399
- var winner, ad, mediaFile, videoEl, container, hls, slot, slot1;
1471
+ var winner, ad, mediaFile, slot, videoEl, container, hls, slot1, slot2;
1400
1472
  return _ts_generator(this, function(_state) {
1401
1473
  switch(_state.label){
1402
1474
  case 0:
@@ -1421,6 +1493,20 @@ function createVastAdLayer(contentVideo, options) {
1421
1493
  if (!mediaFile) return [
1422
1494
  2
1423
1495
  ];
1496
+ if (smartTVMode || singleElementMode) {
1497
+ slot = {
1498
+ bids: bids,
1499
+ ad: ad,
1500
+ mediaFile: mediaFile,
1501
+ videoEl: null,
1502
+ ready: true
1503
+ };
1504
+ preloadSlots.set(token, slot);
1505
+ if (debug) console.log("".concat(LOG, " [preload] Metadata-only preload (smartTV/singleElement), token=").concat(token, ", url=").concat(mediaFile.url));
1506
+ return [
1507
+ 2
1508
+ ];
1509
+ }
1424
1510
  videoEl = createAdVideoElement();
1425
1511
  videoEl.style.visibility = "hidden";
1426
1512
  videoEl.style.pointerEvents = "none";
@@ -1434,7 +1520,7 @@ function createVastAdLayer(contentVideo, options) {
1434
1520
  });
1435
1521
  hls.loadSource(mediaFile.url);
1436
1522
  hls.attachMedia(videoEl);
1437
- slot = {
1523
+ slot1 = {
1438
1524
  bids: bids,
1439
1525
  ad: ad,
1440
1526
  mediaFile: mediaFile,
@@ -1442,7 +1528,7 @@ function createVastAdLayer(contentVideo, options) {
1442
1528
  hlsInstance: hls,
1443
1529
  ready: false
1444
1530
  };
1445
- preloadSlots.set(token, slot);
1531
+ preloadSlots.set(token, slot1);
1446
1532
  hls.on(import_hls.default.Events.MANIFEST_PARSED, function() {
1447
1533
  var s = preloadSlots.get(token);
1448
1534
  if (s) s.ready = true;
@@ -1459,14 +1545,14 @@ function createVastAdLayer(contentVideo, options) {
1459
1545
  } else {
1460
1546
  videoEl.src = mediaFile.url;
1461
1547
  videoEl.load();
1462
- slot1 = {
1548
+ slot2 = {
1463
1549
  bids: bids,
1464
1550
  ad: ad,
1465
1551
  mediaFile: mediaFile,
1466
1552
  videoEl: videoEl,
1467
1553
  ready: false
1468
1554
  };
1469
- preloadSlots.set(token, slot1);
1555
+ preloadSlots.set(token, slot2);
1470
1556
  videoEl.addEventListener("canplay", function() {
1471
1557
  var s = preloadSlots.get(token);
1472
1558
  if (s) s.ready = true;
@@ -1485,7 +1571,7 @@ function createVastAdLayer(contentVideo, options) {
1485
1571
  }
1486
1572
  function playPreloaded(token) {
1487
1573
  return _async_to_generator(function() {
1488
- var slot, contentVolume, adVolume, container;
1574
+ var slot, contentVolume, adVolume2, videoEl, container2, adVolume21, adVolume, container;
1489
1575
  return _ts_generator(this, function(_state) {
1490
1576
  if (destroyed) return [
1491
1577
  2,
@@ -1500,6 +1586,68 @@ function createVastAdLayer(contentVideo, options) {
1500
1586
  }
1501
1587
  preloadSlots.delete(token);
1502
1588
  if (debug) console.log("".concat(LOG, " [preload] Playing preloaded ad, token=").concat(token, ", ready=").concat(slot.ready));
1589
+ contentVolume = contentVideo.volume;
1590
+ originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1591
+ sessionId = generateSessionId();
1592
+ currentAd = slot.ad;
1593
+ trackingFired = _object_spread({}, createEmptyTrackingState());
1594
+ fireTrackingPixels2(slot.ad.trackingUrls.impression);
1595
+ trackingFired.impression = true;
1596
+ if (singleElementMode) {
1597
+ mainHlsInstance === null || mainHlsInstance === void 0 ? void 0 : mainHlsInstance.detachMedia();
1598
+ teardownCurrentPlayback();
1599
+ adVideoElement = contentVideo;
1600
+ adHls = void 0;
1601
+ setupAdEventListeners();
1602
+ if (!continueLiveStreamDuringAds) {
1603
+ contentVideo.pause();
1604
+ }
1605
+ contentVideo.muted = true;
1606
+ contentVideo.volume = 0;
1607
+ adPlaying = true;
1608
+ setAdPlayingFlag(true);
1609
+ adVolume2 = originalMutedState ? 1 : originalVolume;
1610
+ contentVideo.volume = Math.max(0, Math.min(1, adVolume2));
1611
+ contentVideo.muted = false;
1612
+ emit("content_pause");
1613
+ if (debug) console.log("".concat(LOG, " [preload] singleElementMode: attaching ad to contentVideo, url=").concat(slot.mediaFile.url));
1614
+ startPlayback(slot.mediaFile);
1615
+ return [
1616
+ 2
1617
+ ];
1618
+ }
1619
+ if (smartTVMode && !slot.videoEl) {
1620
+ teardownCurrentPlayback();
1621
+ if (adVideoElement) {
1622
+ adVideoElement.remove();
1623
+ adVideoElement = void 0;
1624
+ }
1625
+ videoEl = createAdVideoElement();
1626
+ videoEl.style.visibility = "visible";
1627
+ videoEl.style.pointerEvents = "none";
1628
+ container2 = ensureAdContainer();
1629
+ container2.appendChild(videoEl);
1630
+ adVideoElement = videoEl;
1631
+ setupAdEventListeners();
1632
+ if (!continueLiveStreamDuringAds) {
1633
+ contentVideo.pause();
1634
+ }
1635
+ contentVideo.muted = true;
1636
+ contentVideo.volume = 0;
1637
+ adPlaying = true;
1638
+ setAdPlayingFlag(true);
1639
+ adVolume21 = originalMutedState ? 1 : originalVolume;
1640
+ adVideoElement.volume = Math.max(0, Math.min(1, adVolume21));
1641
+ adVideoElement.muted = false;
1642
+ container2.style.display = "flex";
1643
+ container2.style.pointerEvents = "auto";
1644
+ emit("content_pause");
1645
+ if (debug) console.log("".concat(LOG, " [preload] smartTVMode deferred: creating video element and loading, url=").concat(slot.mediaFile.url));
1646
+ startPlayback(slot.mediaFile);
1647
+ return [
1648
+ 2
1649
+ ];
1650
+ }
1503
1651
  teardownCurrentPlayback();
1504
1652
  if (adVideoElement && adVideoElement !== slot.videoEl) {
1505
1653
  adVideoElement.remove();
@@ -1509,13 +1657,6 @@ function createVastAdLayer(contentVideo, options) {
1509
1657
  adVideoElement = slot.videoEl;
1510
1658
  adHls = slot.hlsInstance;
1511
1659
  setupAdEventListeners();
1512
- sessionId = generateSessionId();
1513
- currentAd = slot.ad;
1514
- trackingFired = _object_spread({}, createEmptyTrackingState());
1515
- fireTrackingPixels2(slot.ad.trackingUrls.impression);
1516
- trackingFired.impression = true;
1517
- contentVolume = contentVideo.volume;
1518
- originalVolume = Math.max(0, Math.min(1, contentVolume || originalVolume));
1519
1660
  if (!continueLiveStreamDuringAds) {
1520
1661
  contentVideo.pause();
1521
1662
  }
@@ -1530,17 +1671,10 @@ function createVastAdLayer(contentVideo, options) {
1530
1671
  container.style.display = "flex";
1531
1672
  container.style.pointerEvents = "auto";
1532
1673
  emit("content_pause");
1533
- if (slot.hlsInstance) {
1534
- adVideoElement.play().catch(function(error) {
1535
- console.error("".concat(LOG, " [preload] Error playing preloaded HLS ad:"), error);
1536
- handleAdError();
1537
- });
1538
- } else {
1539
- adVideoElement.play().catch(function(error) {
1540
- console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1541
- handleAdError();
1542
- });
1543
- }
1674
+ adVideoElement.play().catch(function(error) {
1675
+ console.error("".concat(LOG, " [preload] Error playing preloaded ad:"), error);
1676
+ handleAdError();
1677
+ });
1544
1678
  return [
1545
1679
  2
1546
1680
  ];
@@ -1554,10 +1688,12 @@ function createVastAdLayer(contentVideo, options) {
1554
1688
  if (slot.hlsInstance) {
1555
1689
  slot.hlsInstance.destroy();
1556
1690
  }
1557
- slot.videoEl.pause();
1558
- slot.videoEl.removeAttribute("src");
1559
- slot.videoEl.load();
1560
- slot.videoEl.remove();
1691
+ if (slot.videoEl) {
1692
+ slot.videoEl.pause();
1693
+ slot.videoEl.removeAttribute("src");
1694
+ slot.videoEl.load();
1695
+ slot.videoEl.remove();
1696
+ }
1561
1697
  if (debug) console.log("".concat(LOG, " [preload] Cancelled and cleaned up token=").concat(token));
1562
1698
  }
1563
1699
  return {
@@ -1605,20 +1741,27 @@ function createVastAdLayer(contentVideo, options) {
1605
1741
  setAdPlayingFlag(false);
1606
1742
  contentVideo.muted = originalMutedState;
1607
1743
  contentVideo.volume = originalMutedState ? 0 : originalVolume;
1608
- if (adContainerEl) {
1609
- adContainerEl.style.display = "none";
1610
- adContainerEl.style.pointerEvents = "none";
1611
- }
1612
1744
  contentVideo.style.visibility = "visible";
1613
1745
  contentVideo.style.opacity = "1";
1614
- if (continueLiveStreamDuringAds) {
1615
- contentVideo.play().catch(function() {});
1616
- }
1617
- teardownCurrentPlayback();
1618
- if (adVideoElement) {
1619
- adVideoElement.pause();
1620
- adVideoElement.removeAttribute("src");
1621
- adVideoElement.load();
1746
+ if (singleElementMode) {
1747
+ teardownCurrentPlayback();
1748
+ contentVideo.removeAttribute("src");
1749
+ contentVideo.load();
1750
+ adVideoElement = void 0;
1751
+ } else {
1752
+ if (adContainerEl) {
1753
+ adContainerEl.style.display = "none";
1754
+ adContainerEl.style.pointerEvents = "none";
1755
+ }
1756
+ if (continueLiveStreamDuringAds) {
1757
+ contentVideo.play().catch(function() {});
1758
+ }
1759
+ teardownCurrentPlayback();
1760
+ if (adVideoElement) {
1761
+ adVideoElement.pause();
1762
+ adVideoElement.removeAttribute("src");
1763
+ adVideoElement.load();
1764
+ }
1622
1765
  }
1623
1766
  currentAd = void 0;
1624
1767
  tornDown = false;
@@ -1658,9 +1801,14 @@ function createVastAdLayer(contentVideo, options) {
1658
1801
  }
1659
1802
  teardownCurrentPlayback();
1660
1803
  if (adVideoElement) {
1661
- adVideoElement.pause();
1662
- adVideoElement.removeAttribute("src");
1663
- adVideoElement.remove();
1804
+ if (singleElementMode && adVideoElement === contentVideo) {
1805
+ contentVideo.removeAttribute("src");
1806
+ contentVideo.load();
1807
+ } else {
1808
+ adVideoElement.pause();
1809
+ adVideoElement.removeAttribute("src");
1810
+ adVideoElement.remove();
1811
+ }
1664
1812
  adVideoElement = void 0;
1665
1813
  }
1666
1814
  if (adContainerEl === null || adContainerEl === void 0 ? void 0 : adContainerEl.parentElement) {
@@ -2858,8 +3006,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2858
3006
  this.vastManager = createVastManager(config.debugAdTiming !== void 0 ? {
2859
3007
  debug: !!config.debugAdTiming
2860
3008
  } : {});
3009
+ var browserForAdLayer = detectBrowser();
3010
+ var isSinglePipeline = browserForAdLayer.isSmartTV || !!this.config.singlePipelineMode;
2861
3011
  this.adLayer = createVastAdLayer(this.video, {
2862
3012
  continueLiveStreamDuringAds: false,
3013
+ smartTVMode: isSinglePipeline,
3014
+ singleElementMode: isSinglePipeline,
3015
+ forceMP4Ads: isSinglePipeline,
2863
3016
  debug: !!config.debugAdTiming
2864
3017
  });
2865
3018
  }
@@ -3505,6 +3658,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3505
3658
  if (_this.fillerVideo) {
3506
3659
  _this.fillerVideo.style.display = "none";
3507
3660
  }
3661
+ if (!_this.adLayer.isAdPlaying()) {
3662
+ if (_this.config.debugAdTiming) {
3663
+ console.log("[StormcloudVideoPlayer] Filler video failed \u2014 restoring main video");
3664
+ }
3665
+ _this.adLayer.hidePlaceholder();
3666
+ if (_this.video.paused && _this.video.readyState >= 2) {
3667
+ var _this_video_play;
3668
+ (_this_video_play = _this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
3669
+ }
3670
+ }
3508
3671
  });
3509
3672
  if (this.config.debugAdTiming) {
3510
3673
  console.log("[StormcloudVideoPlayer] Showing filler video layer");
@@ -4221,6 +4384,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4221
4384
  if (!this.isLiveStream) {
4222
4385
  return false;
4223
4386
  }
4387
+ if (this.config.singlePipelineMode) {
4388
+ return false;
4389
+ }
4390
+ var browser = detectBrowser();
4391
+ if (browser.isSmartTV) {
4392
+ return false;
4393
+ }
4224
4394
  return true;
4225
4395
  }
4226
4396
  },
@@ -4428,7 +4598,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4428
4598
  switch(_state.label){
4429
4599
  case 0:
4430
4600
  _loop = function() {
4431
- var remaining, prefetchContext, bids, err, bids1, remainingNow, delay, elapsed, remaining1, context, bids2, remainingNow1, err1;
4601
+ var remaining, prefetchContext, bids, err, bids1, remainingNow, urgentNeedAd, delay, elapsed, remaining1, context, bids2, remainingNow1, err1, sleepMs;
4432
4602
  return _ts_generator(this, function(_state) {
4433
4603
  switch(_state.label){
4434
4604
  case 0:
@@ -4564,7 +4734,8 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4564
4734
  "continue"
4565
4735
  ];
4566
4736
  case 12:
4567
- delay = _this.lastAdRequestTime ? _this.minAdRequestIntervalMs + (_this.consecutiveFailures > 0 ? backoffMs() : 0) : 0;
4737
+ urgentNeedAd = _this.inAdBreak && !_this.adLayer.isAdPlaying();
4738
+ delay = _this.lastAdRequestTime ? _this.minAdRequestIntervalMs + (!urgentNeedAd && _this.consecutiveFailures > 0 ? backoffMs() : 0) : 0;
4568
4739
  elapsed = Date.now() - _this.lastAdRequestTime;
4569
4740
  if (!(elapsed < delay && _this.lastAdRequestTime > 0)) return [
4570
4741
  3,
@@ -4683,10 +4854,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4683
4854
  24
4684
4855
  ];
4685
4856
  case 24:
4857
+ sleepMs = _this.inAdBreak && !_this.adLayer.isAdPlaying() ? 0 : backoffMs();
4686
4858
  return [
4687
4859
  4,
4688
4860
  new Promise(function(r) {
4689
- return setTimeout(r, backoffMs());
4861
+ return setTimeout(r, sleepMs);
4690
4862
  })
4691
4863
  ];
4692
4864
  case 25:
@@ -5365,42 +5537,71 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5365
5537
  this.video.volume = restoredVolume;
5366
5538
  }
5367
5539
  var browser = detectBrowser();
5368
- var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0;
5540
+ var isSmartTV = browser.tizenVersion !== void 0 || browser.webOSVersion !== void 0 || !!this.config.singlePipelineMode;
5369
5541
  if (isSmartTV && this.hls) {
5370
- if (!restoredMuted) {
5371
- var hlsRef = this.hls;
5372
- var savedMuted = restoredMuted;
5373
- var savedVolume = restoredVolume;
5374
- var onManifestParsedRestore = function onManifestParsedRestore1() {
5375
- hlsRef.off(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5376
- if (!_this.inAdBreak && !_this.adLayer.isAdPlaying()) {
5377
- if (_this.video.muted !== savedMuted) _this.video.muted = savedMuted;
5378
- if (Math.abs(_this.video.volume - savedVolume) > 0.01) _this.video.volume = savedVolume;
5379
- if (_this.config.debugAdTiming) {
5380
- console.log("[StormcloudVideoPlayer] Smart TV: audio state restored on MANIFEST_PARSED after re-attach");
5542
+ var hlsRef = this.hls;
5543
+ var savedMuted = restoredMuted;
5544
+ var savedVolume = restoredVolume;
5545
+ var videoRef = this.video;
5546
+ var debugEnabled = this.config.debugAdTiming;
5547
+ var tryPlay = function tryPlay1(attempt) {
5548
+ var _videoRef_play;
5549
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5550
+ (_videoRef_play = videoRef.play()) === null || _videoRef_play === void 0 ? void 0 : _videoRef_play.catch(function() {
5551
+ if (attempt < 3) {
5552
+ if (debugEnabled) {
5553
+ console.log("[StormcloudVideoPlayer] Smart TV: play() retry ".concat(attempt + 1, "/3 in ").concat(500 * (attempt + 1), "ms"));
5381
5554
  }
5555
+ setTimeout(function() {
5556
+ return tryPlay(attempt + 1);
5557
+ }, 500 * (attempt + 1));
5382
5558
  }
5383
- };
5384
- hlsRef.on(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5385
- }
5386
- this.hls.attachMedia(this.video);
5387
- if (this.config.debugAdTiming) {
5388
- console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5559
+ });
5560
+ };
5561
+ var onManifestParsedRestore = function onManifestParsedRestore1() {
5562
+ hlsRef.off(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5563
+ if (!_this.inAdBreak && !_this.adLayer.isAdPlaying()) {
5564
+ if (videoRef.muted !== savedMuted) videoRef.muted = savedMuted;
5565
+ if (Math.abs(videoRef.volume - savedVolume) > 0.01) videoRef.volume = savedVolume;
5566
+ if (debugEnabled) {
5567
+ console.log("[StormcloudVideoPlayer] Smart TV: audio state restored on MANIFEST_PARSED after re-attach");
5568
+ }
5569
+ hlsRef.startLoad(-1);
5570
+ tryPlay(0);
5571
+ if (debugEnabled) {
5572
+ console.log("[StormcloudVideoPlayer] Smart TV: seeking to live edge and resuming playback after re-attach");
5573
+ }
5574
+ }
5575
+ };
5576
+ hlsRef.on(import_hls2.default.Events.MANIFEST_PARSED, onManifestParsedRestore);
5577
+ var pipelineDelayMs = 300;
5578
+ if (debugEnabled) {
5579
+ console.log("[StormcloudVideoPlayer] Smart TV: waiting ".concat(pipelineDelayMs, "ms for hardware pipeline release before re-attach"));
5389
5580
  }
5390
- }
5391
- if (this.shouldContinueLiveStreamDuringAds()) {
5392
- var _this_video_play;
5393
- if (this.config.debugAdTiming) {
5394
- if (this.video.paused) {
5395
- console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
5396
- } else {
5397
- console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
5581
+ setTimeout(function() {
5582
+ if (_this.inAdBreak || _this.adLayer.isAdPlaying()) return;
5583
+ if (_this.hls) {
5584
+ _this.hls.attachMedia(_this.video);
5585
+ if (debugEnabled) {
5586
+ console.log("[StormcloudVideoPlayer] Smart TV: re-attached HLS to video element after ad break to restore media pipeline");
5587
+ }
5588
+ }
5589
+ }, pipelineDelayMs);
5590
+ } else {
5591
+ if (this.shouldContinueLiveStreamDuringAds()) {
5592
+ var _this_video_play;
5593
+ if (this.config.debugAdTiming) {
5594
+ if (this.video.paused) {
5595
+ console.log("[StormcloudVideoPlayer] Content video paused in live mode after ads, resuming playback");
5596
+ } else {
5597
+ console.log("[StormcloudVideoPlayer] Content video already playing in live mode after ads");
5598
+ }
5398
5599
  }
5600
+ (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5601
+ } else if (this.video.paused) {
5602
+ var _this_video_play1;
5603
+ (_this_video_play1 = this.video.play()) === null || _this_video_play1 === void 0 ? void 0 : _this_video_play1.catch(function() {});
5399
5604
  }
5400
- (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5401
- } else if (this.video.paused) {
5402
- var _this_video_play1;
5403
- (_this_video_play1 = this.video.play()) === null || _this_video_play1 === void 0 ? void 0 : _this_video_play1.catch(function() {});
5404
5605
  }
5405
5606
  this.syncMainContentAudioWhenVisible();
5406
5607
  if (!restoredMuted) {
@@ -5451,6 +5652,23 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
5451
5652
  console.log("[CONTINUOUS-FETCH] \uD83D\uDED1 Max consecutive failures reached (".concat(this.consecutiveFailures, "), ending ad break gracefully"));
5452
5653
  }
5453
5654
  this.handleAdPodComplete();
5655
+ return;
5656
+ }
5657
+ if (this.inAdBreak && !this.config.disableFiller) {
5658
+ if (this.config.debugAdTiming) {
5659
+ console.log("[CONTINUOUS-FETCH] Ad failure in active break \u2014 showing filler while awaiting next ad");
5660
+ }
5661
+ this.showPlaceholderLayer();
5662
+ this.adLayer.showPlaceholder();
5663
+ } else if (this.inAdBreak) {
5664
+ if (this.config.debugAdTiming) {
5665
+ console.log("[CONTINUOUS-FETCH] Ad failure with no filler \u2014 restoring main video temporarily");
5666
+ }
5667
+ this.adLayer.hidePlaceholder();
5668
+ if (!this.adLayer.isAdPlaying() && this.video.paused && this.video.readyState >= 2) {
5669
+ var _this_video_play;
5670
+ (_this_video_play = this.video.play()) === null || _this_video_play === void 0 ? void 0 : _this_video_play.catch(function() {});
5671
+ }
5454
5672
  }
5455
5673
  }
5456
5674
  },