stormcloud-video-player 0.8.2 → 0.8.4

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.
Files changed (36) hide show
  1. package/dist/stormcloud-vp.min.js +3 -1
  2. package/lib/index.cjs +536 -341
  3. package/lib/index.cjs.map +1 -1
  4. package/lib/index.d.cts +37 -2
  5. package/lib/index.d.ts +37 -2
  6. package/lib/index.js +477 -342
  7. package/lib/index.js.map +1 -1
  8. package/lib/player/StormcloudVideoPlayer.cjs +266 -206
  9. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  10. package/lib/player/StormcloudVideoPlayer.d.cts +5 -2
  11. package/lib/players/FilePlayer.cjs.map +1 -1
  12. package/lib/players/HlsPlayer.cjs +266 -206
  13. package/lib/players/HlsPlayer.cjs.map +1 -1
  14. package/lib/players/HlsPlayer.d.cts +1 -1
  15. package/lib/players/index.cjs +266 -206
  16. package/lib/players/index.cjs.map +1 -1
  17. package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
  18. package/lib/sdk/hlsAdPlayer.d.cts +1 -1
  19. package/lib/sdk/pal.cjs.map +1 -1
  20. package/lib/{types-DDwAfBLt.d.cts → types-BmF_60m2.d.cts} +2 -0
  21. package/lib/ui/StormcloudVideoPlayer.cjs +450 -341
  22. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  23. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -1
  24. package/lib/utils/browserCompat.cjs.map +1 -1
  25. package/lib/utils/mqttClient.cjs +245 -0
  26. package/lib/utils/mqttClient.cjs.map +1 -0
  27. package/lib/utils/mqttClient.d.cts +13 -0
  28. package/lib/utils/mqttConfig.cjs +141 -0
  29. package/lib/utils/mqttConfig.cjs.map +1 -0
  30. package/lib/utils/mqttConfig.d.cts +20 -0
  31. package/lib/utils/polyfills.cjs.map +1 -1
  32. package/lib/utils/tracking.cjs +182 -170
  33. package/lib/utils/tracking.cjs.map +1 -1
  34. package/lib/utils/tracking.d.cts +1 -1
  35. package/package.json +3 -1
  36. package/src/certs/emqxsl-ca.crt +22 -0
@@ -1385,6 +1385,99 @@ function createPalNonceManager() {
1385
1385
  }
1386
1386
  };
1387
1387
  }
1388
+ // src/utils/mqttConfig.ts
1389
+ var DEFAULT_MQTT_CONFIG = {
1390
+ enabled: true,
1391
+ brokerAddress: "vecbae77.ala.us-east-1.emqxsl.com",
1392
+ brokerPort: 8883,
1393
+ wsPort: 8084,
1394
+ username: "for-sonifi",
1395
+ password: "sonifi-mqtt",
1396
+ topicPrefix: "adstorm/players",
1397
+ qos: 1
1398
+ };
1399
+ var mqttConfig = _object_spread({}, DEFAULT_MQTT_CONFIG);
1400
+ function isMQTTEnabled() {
1401
+ return mqttConfig.enabled;
1402
+ }
1403
+ function buildMQTTBrokerUrl() {
1404
+ if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;
1405
+ return "wss://".concat(mqttConfig.brokerAddress, ":").concat(mqttConfig.wsPort, "/mqtt");
1406
+ }
1407
+ function buildPlayerTopic(licenseKey, channel) {
1408
+ return "".concat(mqttConfig.topicPrefix, "/").concat(licenseKey, "/").concat(channel);
1409
+ }
1410
+ // src/utils/mqttClient.ts
1411
+ var import_mqtt = __toESM(require("mqtt"), 1);
1412
+ var LOG = "[StormcloudVideoPlayer][MQTT]";
1413
+ var client = null;
1414
+ var status = "disconnected";
1415
+ function initMQTTClient() {
1416
+ if (client || !isMQTTEnabled()) return;
1417
+ var url = buildMQTTBrokerUrl();
1418
+ status = "connecting";
1419
+ var clientId = "stormcloud-vp-".concat(Math.random().toString(36).slice(2, 9));
1420
+ try {
1421
+ client = import_mqtt.default.connect(url, {
1422
+ clientId: clientId,
1423
+ username: mqttConfig.username,
1424
+ password: mqttConfig.password,
1425
+ keepalive: 60,
1426
+ clean: true,
1427
+ reconnectPeriod: 5e3,
1428
+ connectTimeout: 1e4,
1429
+ queueQoSZero: false
1430
+ });
1431
+ } catch (err) {
1432
+ status = "error";
1433
+ console.warn("".concat(LOG, " connect() threw:"), err);
1434
+ return;
1435
+ }
1436
+ client.on("connect", function() {
1437
+ status = "connected";
1438
+ console.info("".concat(LOG, " connected to ").concat(url));
1439
+ });
1440
+ client.on("reconnect", function() {
1441
+ status = "connecting";
1442
+ console.info("".concat(LOG, " reconnecting…"));
1443
+ });
1444
+ client.on("offline", function() {
1445
+ status = "disconnected";
1446
+ console.warn("".concat(LOG, " offline"));
1447
+ });
1448
+ client.on("error", function(err) {
1449
+ status = "error";
1450
+ console.warn("".concat(LOG, " error:"), err.message);
1451
+ });
1452
+ client.on("close", function() {
1453
+ if (status === "connected") {
1454
+ status = "disconnected";
1455
+ }
1456
+ });
1457
+ }
1458
+ function ensureMQTTClient() {
1459
+ if (isMQTTEnabled() && !client) {
1460
+ initMQTTClient();
1461
+ }
1462
+ }
1463
+ function publishMQTT(topic, payload) {
1464
+ if (!isMQTTEnabled()) {
1465
+ return false;
1466
+ }
1467
+ ensureMQTTClient();
1468
+ if (!client) {
1469
+ return false;
1470
+ }
1471
+ try {
1472
+ client.publish(topic, JSON.stringify(payload), {
1473
+ qos: mqttConfig.qos
1474
+ });
1475
+ return true;
1476
+ } catch (err) {
1477
+ console.warn("".concat(LOG, " publish failed on ").concat(topic, ":"), err);
1478
+ return false;
1479
+ }
1480
+ }
1388
1481
  // src/utils/tracking.ts
1389
1482
  var cachedBrowserId = null;
1390
1483
  function getClientInfo() {
@@ -1531,7 +1624,7 @@ function getClientInfo() {
1531
1624
  }
1532
1625
  function getBrowserID(clientInfo) {
1533
1626
  return _async_to_generator(function() {
1534
- var fingerprintString, encodedData, utf8, buffer, i, hashBuffer, hashArray, hashHex, error, hash, i1, char, fallbackHash, timestamp, random;
1627
+ var _crypto_subtle, fingerprintString, encodedData, utf8, buffer, i, hashBuffer, hashHex, unused, hash, i1, char, fallbackHash, timestamp, random;
1535
1628
  return _ts_generator(this, function(_state) {
1536
1629
  switch(_state.label){
1537
1630
  case 0:
@@ -1542,7 +1635,7 @@ function getBrowserID(clientInfo) {
1542
1635
  ];
1543
1636
  }
1544
1637
  fingerprintString = JSON.stringify(clientInfo);
1545
- if (!(typeof crypto !== "undefined" && crypto.subtle && crypto.subtle.digest)) return [
1638
+ if (!(typeof crypto !== "undefined" && ((_crypto_subtle = crypto.subtle) === null || _crypto_subtle === void 0 ? void 0 : _crypto_subtle.digest))) return [
1546
1639
  3,
1547
1640
  5
1548
1641
  ];
@@ -1580,8 +1673,7 @@ function getBrowserID(clientInfo) {
1580
1673
  ];
1581
1674
  case 3:
1582
1675
  hashBuffer = _state.sent();
1583
- hashArray = Array.from(new Uint8Array(hashBuffer));
1584
- hashHex = hashArray.map(function(b) {
1676
+ hashHex = Array.from(new Uint8Array(hashBuffer)).map(function(b) {
1585
1677
  return b.toString(16).padStart(2, "0");
1586
1678
  }).join("");
1587
1679
  cachedBrowserId = hashHex;
@@ -1590,8 +1682,8 @@ function getBrowserID(clientInfo) {
1590
1682
  hashHex
1591
1683
  ];
1592
1684
  case 4:
1593
- error = _state.sent();
1594
- console.warn("[StormcloudVideoPlayer] crypto.subtle.digest not supported, using fallback hash");
1685
+ unused = _state.sent();
1686
+ console.warn("[StormcloudVideoPlayer] crypto.subtle not supported, using fallback hash");
1595
1687
  return [
1596
1688
  3,
1597
1689
  5
@@ -1615,177 +1707,91 @@ function getBrowserID(clientInfo) {
1615
1707
  });
1616
1708
  })();
1617
1709
  }
1618
- var PLAYER_TRACKING_BASE_URL = "https://adstorm.co/api-adstorm-dev/adstorm/player-tracking";
1619
- var TRACK_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/metrics/ingest");
1620
- var HEARTBEAT_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/heartbeat");
1621
- var IMPRESSIONS_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/impressions/ingest");
1622
- function buildHeaders(licenseKey) {
1623
- var headers = {
1624
- "Content-Type": "application/json"
1625
- };
1626
- if (licenseKey) {
1627
- headers["Authorization"] = "Bearer ".concat(licenseKey);
1628
- }
1629
- return headers;
1710
+ function canPublish(licenseKey) {
1711
+ return Boolean(isMQTTEnabled() && licenseKey);
1630
1712
  }
1631
- function sendTrackRequest(licenseKey, body) {
1713
+ function buildPlayerMetricEvent() {
1632
1714
  return _async_to_generator(function() {
1633
- var response;
1634
- return _ts_generator(this, function(_state) {
1635
- switch(_state.label){
1636
- case 0:
1637
- return [
1638
- 4,
1639
- fetch(TRACK_URL, {
1640
- method: "POST",
1641
- headers: buildHeaders(licenseKey),
1642
- body: JSON.stringify(body)
1643
- })
1644
- ];
1645
- case 1:
1646
- response = _state.sent();
1647
- if (!response.ok) {
1648
- throw new Error("HTTP error! status: ".concat(response.status));
1649
- }
1650
- return [
1651
- 4,
1652
- response.json()
1653
- ];
1654
- case 2:
1655
- _state.sent();
1656
- return [
1657
- 2
1658
- ];
1659
- }
1660
- });
1661
- })();
1662
- }
1663
- function postJson(url, licenseKey, body) {
1664
- return _async_to_generator(function() {
1665
- var response;
1666
- return _ts_generator(this, function(_state) {
1667
- switch(_state.label){
1668
- case 0:
1669
- return [
1670
- 4,
1671
- fetch(url, {
1672
- method: "POST",
1673
- headers: buildHeaders(licenseKey),
1674
- body: JSON.stringify(body)
1675
- })
1676
- ];
1677
- case 1:
1678
- response = _state.sent();
1679
- if (!response.ok) {
1680
- throw new Error("HTTP error! status: ".concat(response.status));
1681
- }
1682
- return [
1683
- 4,
1684
- response.json()
1685
- ];
1686
- case 2:
1687
- _state.sent();
1688
- return [
1689
- 2
1690
- ];
1691
- }
1692
- });
1693
- })();
1694
- }
1695
- function buildPlayerMetricEvent(_0) {
1696
- return _async_to_generator(function(licenseKey) {
1697
- var context, flags, _flags_captureAt, clientInfo, browserId, captureAt;
1715
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
1698
1716
  var _arguments = arguments;
1699
1717
  return _ts_generator(this, function(_state) {
1700
1718
  switch(_state.label){
1701
1719
  case 0:
1702
- context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
1720
+ context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
1703
1721
  clientInfo = getClientInfo();
1704
1722
  return [
1705
1723
  4,
1706
1724
  getBrowserID(clientInfo)
1707
1725
  ];
1708
1726
  case 1:
1709
- browserId = _state.sent();
1727
+ playerId = _state.sent();
1710
1728
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
1711
1729
  return [
1712
1730
  2,
1713
- {
1714
- player_id: browserId,
1715
- browserId: browserId,
1731
+ _object_spread({
1732
+ player_id: playerId,
1716
1733
  device_type: clientInfo.deviceType,
1717
- deviceType: clientInfo.deviceType,
1718
- input_stream_type: context.inputStreamType,
1719
- os: clientInfo.os,
1720
- ad_loaded: flags.adLoaded,
1721
- ad_detect: flags.adDetect,
1722
- license_key: licenseKey,
1723
- capture_at: captureAt,
1724
- timestamp: captureAt
1725
- }
1734
+ os: clientInfo.os.toLowerCase(),
1735
+ ad_loaded: (_flags_adLoaded = flags.adLoaded) !== null && _flags_adLoaded !== void 0 ? _flags_adLoaded : false,
1736
+ ad_detect: (_flags_adDetect = flags.adDetect) !== null && _flags_adDetect !== void 0 ? _flags_adDetect : false,
1737
+ capture_at: captureAt
1738
+ }, context.inputStreamType ? {
1739
+ input_stream_type: context.inputStreamType
1740
+ } : {})
1726
1741
  ];
1727
1742
  }
1728
1743
  });
1729
1744
  }).apply(this, arguments);
1730
1745
  }
1746
+ function publishTracking(licenseKey, channel, body) {
1747
+ ensureMQTTClient();
1748
+ publishMQTT(buildPlayerTopic(licenseKey, channel), body);
1749
+ }
1731
1750
  function sendInitialTracking(_0) {
1732
1751
  return _async_to_generator(function(licenseKey) {
1733
- var context, clientInfo, browserId, trackingData, error;
1752
+ var context, metricEvent, error;
1734
1753
  var _arguments = arguments;
1735
1754
  return _ts_generator(this, function(_state) {
1736
1755
  switch(_state.label){
1737
1756
  case 0:
1738
1757
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
1758
+ if (!canPublish(licenseKey)) return [
1759
+ 2
1760
+ ];
1739
1761
  _state.label = 1;
1740
1762
  case 1:
1741
1763
  _state.trys.push([
1742
1764
  1,
1743
- 4,
1765
+ 3,
1744
1766
  ,
1745
- 5
1767
+ 4
1746
1768
  ]);
1747
- clientInfo = getClientInfo();
1748
- return [
1749
- 4,
1750
- getBrowserID(clientInfo)
1751
- ];
1752
- case 2:
1753
- browserId = _state.sent();
1754
- trackingData = _object_spread({
1755
- browserId: browserId
1756
- }, clientInfo);
1757
1769
  return [
1758
1770
  4,
1759
- sendTrackRequest(licenseKey, {
1760
- events: [
1761
- {
1762
- player_id: browserId,
1763
- device_type: clientInfo.deviceType,
1764
- input_stream_type: context.inputStreamType,
1765
- os: clientInfo.os,
1766
- ad_loaded: false,
1767
- ad_detect: false,
1768
- license_key: licenseKey,
1769
- capture_at: /* @__PURE__ */ new Date().toISOString()
1770
- }
1771
- ],
1772
- trackingData: trackingData
1771
+ buildPlayerMetricEvent(context, {
1772
+ adLoaded: false,
1773
+ adDetect: false
1773
1774
  })
1774
1775
  ];
1775
- case 3:
1776
- _state.sent();
1776
+ case 2:
1777
+ metricEvent = _state.sent();
1778
+ publishTracking(licenseKey, "metrics", {
1779
+ events: [
1780
+ metricEvent
1781
+ ]
1782
+ });
1777
1783
  return [
1778
1784
  3,
1779
- 5
1785
+ 4
1780
1786
  ];
1781
- case 4:
1787
+ case 3:
1782
1788
  error = _state.sent();
1783
1789
  console.error("[StormcloudVideoPlayer] Error sending initial tracking data:", error);
1784
1790
  return [
1785
1791
  3,
1786
- 5
1792
+ 4
1787
1793
  ];
1788
- case 5:
1794
+ case 4:
1789
1795
  return [
1790
1796
  2
1791
1797
  ];
@@ -1889,53 +1895,48 @@ function sendAdImpressionTracking(_0, _1) {
1889
1895
  switch(_state.label){
1890
1896
  case 0:
1891
1897
  context = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
1898
+ if (!canPublish(licenseKey)) return [
1899
+ 2
1900
+ ];
1892
1901
  _state.label = 1;
1893
1902
  case 1:
1894
1903
  _state.trys.push([
1895
1904
  1,
1896
- 4,
1905
+ 3,
1897
1906
  ,
1898
- 5
1907
+ 4
1899
1908
  ]);
1900
1909
  return [
1901
1910
  4,
1902
- buildPlayerMetricEvent(licenseKey, context, {
1911
+ buildPlayerMetricEvent(context, {
1903
1912
  captureAt: adImpressionInfo.timestamp
1904
1913
  })
1905
1914
  ];
1906
1915
  case 2:
1907
1916
  metricEvent = _state.sent();
1908
- return [
1909
- 4,
1910
- Promise.all([
1911
- postJson(HEARTBEAT_URL, licenseKey, metricEvent),
1912
- postJson(IMPRESSIONS_URL, licenseKey, {
1913
- events: [
1914
- {
1915
- player_id: metricEvent.player_id,
1916
- ad_played_count: 1,
1917
- ad_url: adImpressionInfo.adUrl,
1918
- license_key: licenseKey,
1919
- capture_at: adImpressionInfo.timestamp
1920
- }
1921
- ]
1922
- })
1923
- ])
1924
- ];
1925
- case 3:
1926
- _state.sent();
1917
+ publishTracking(licenseKey, "heartbeat", metricEvent);
1918
+ publishTracking(licenseKey, "impressions", {
1919
+ events: [
1920
+ {
1921
+ player_id: metricEvent.player_id,
1922
+ ad_played_count: 1,
1923
+ ad_url: adImpressionInfo.adUrl,
1924
+ capture_at: adImpressionInfo.timestamp
1925
+ }
1926
+ ]
1927
+ });
1927
1928
  return [
1928
1929
  3,
1929
- 5
1930
+ 4
1930
1931
  ];
1931
- case 4:
1932
+ case 3:
1932
1933
  error = _state.sent();
1933
1934
  console.error("[StormcloudVideoPlayer] Error sending ad impression tracking:", error);
1934
1935
  return [
1935
1936
  3,
1936
- 5
1937
+ 4
1937
1938
  ];
1938
- case 5:
1939
+ case 4:
1939
1940
  return [
1940
1941
  2
1941
1942
  ];
@@ -1951,38 +1952,36 @@ function sendHeartbeat(_0) {
1951
1952
  switch(_state.label){
1952
1953
  case 0:
1953
1954
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
1955
+ if (!canPublish(licenseKey)) return [
1956
+ 2
1957
+ ];
1954
1958
  _state.label = 1;
1955
1959
  case 1:
1956
1960
  _state.trys.push([
1957
1961
  1,
1958
- 4,
1962
+ 3,
1959
1963
  ,
1960
- 5
1964
+ 4
1961
1965
  ]);
1962
1966
  return [
1963
1967
  4,
1964
- buildPlayerMetricEvent(licenseKey, context, flags)
1968
+ buildPlayerMetricEvent(context, flags)
1965
1969
  ];
1966
1970
  case 2:
1967
1971
  heartbeatData = _state.sent();
1968
- return [
1969
- 4,
1970
- postJson(HEARTBEAT_URL, licenseKey, heartbeatData)
1971
- ];
1972
- case 3:
1973
- _state.sent();
1972
+ publishTracking(licenseKey, "heartbeat", heartbeatData);
1974
1973
  return [
1975
1974
  3,
1976
- 5
1975
+ 4
1977
1976
  ];
1978
- case 4:
1977
+ case 3:
1979
1978
  error = _state.sent();
1980
1979
  console.error("[StormcloudVideoPlayer] Error sending heartbeat:", error);
1981
1980
  return [
1982
1981
  3,
1983
- 5
1982
+ 4
1984
1983
  ];
1985
- case 5:
1984
+ case 4:
1986
1985
  return [
1987
1986
  2
1988
1987
  ];
@@ -2495,7 +2494,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2495
2494
  key: "load",
2496
2495
  value: function load() {
2497
2496
  return _async_to_generator(function() {
2498
- var _this, _this_config_lowLatencyMode, error, _this_config_lowLatencyMode1, _this_video_play;
2497
+ var _this, _this_config_lowLatencyMode, error, _this_config_isLiveStream, _this_config_lowLatencyMode1, _this_video_play;
2499
2498
  return _ts_generator(this, function(_state) {
2500
2499
  switch(_state.label){
2501
2500
  case 0:
@@ -2546,7 +2545,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2546
2545
  this.nativeHlsMode = true;
2547
2546
  this.videoSrcProtection = this.config.src;
2548
2547
  this.video.src = this.config.src;
2549
- this.isLiveStream = (_this_config_lowLatencyMode1 = this.config.lowLatencyMode) !== null && _this_config_lowLatencyMode1 !== void 0 ? _this_config_lowLatencyMode1 : false;
2548
+ this.isLiveStream = (_this_config_isLiveStream = this.config.isLiveStream) !== null && _this_config_isLiveStream !== void 0 ? _this_config_isLiveStream : (_this_config_lowLatencyMode1 = this.config.lowLatencyMode) !== null && _this_config_lowLatencyMode1 !== void 0 ? _this_config_lowLatencyMode1 : false;
2550
2549
  if (this.config.debugAdTiming) {
2551
2550
  console.log("[StormcloudVideoPlayer] Using native HLS playback - VOD mode:", {
2552
2551
  isLive: this.isLiveStream,
@@ -2597,15 +2596,15 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2597
2596
  });
2598
2597
  this.hls.on(import_hls2.default.Events.MANIFEST_PARSED, function(_, data) {
2599
2598
  return _async_to_generator(function() {
2600
- var _ref, _this_config_minSegmentsBeforePlay, _this_hls_levels, _this_hls, prerollKey, adBehavior, minSegments, _this_video_play;
2599
+ var _this_config_isLiveStream, _ref, _this_config_minSegmentsBeforePlay, _this_hls_levels, _this_hls, prerollKey, adBehavior, minSegments, _this_video_play;
2601
2600
  return _ts_generator(this, function(_state) {
2602
2601
  switch(_state.label){
2603
2602
  case 0:
2604
- this.isLiveStream = (_ref = (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : (_this_hls_levels = _this_hls.levels) === null || _this_hls_levels === void 0 ? void 0 : _this_hls_levels.some(function(level) {
2603
+ this.isLiveStream = (_this_config_isLiveStream = this.config.isLiveStream) !== null && _this_config_isLiveStream !== void 0 ? _this_config_isLiveStream : (_ref = (_this_hls = this.hls) === null || _this_hls === void 0 ? void 0 : (_this_hls_levels = _this_hls.levels) === null || _this_hls_levels === void 0 ? void 0 : _this_hls_levels.some(function(level) {
2605
2604
  var _level_details, _level_details1;
2606
2605
  return (level === null || level === void 0 ? void 0 : (_level_details = level.details) === null || _level_details === void 0 ? void 0 : _level_details.live) === true || (level === null || level === void 0 ? void 0 : (_level_details1 = level.details) === null || _level_details1 === void 0 ? void 0 : _level_details1.type) === "LIVE";
2607
2606
  })) !== null && _ref !== void 0 ? _ref : false;
2608
- if (!this.isLiveStream && this.vmapBreaks.length === 0 && this.apiVastTagUrl) {
2607
+ if (!this.isVmapEnabled() && !this.isLiveStream && this.vmapBreaks.length === 0 && this.apiVastTagUrl) {
2609
2608
  prerollKey = "synthetic-vod-preroll";
2610
2609
  if (!this.consumedVmapBreakIds.has(prerollKey)) {
2611
2610
  this.vmapBreaks = [
@@ -3190,12 +3189,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3190
3189
  _this.palNonce.sendPlaybackStart();
3191
3190
  }
3192
3191
  });
3193
- this.video.addEventListener("ended", function() {
3192
+ this.endedHandler = function() {
3194
3193
  if (_this.palPlaybackStarted) {
3195
3194
  _this.palPlaybackStarted = false;
3196
3195
  _this.palNonce.sendPlaybackEnd();
3197
3196
  }
3198
- });
3197
+ _this.onVideoEnded();
3198
+ };
3199
+ this.video.addEventListener("ended", this.endedHandler);
3199
3200
  this.video.addEventListener("mousedown", function(e) {
3200
3201
  _this.palNonce.sendAdTouch(e);
3201
3202
  });
@@ -4517,6 +4518,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4517
4518
  }
4518
4519
  }
4519
4520
  },
4521
+ {
4522
+ key: "isVmapEnabled",
4523
+ value: function isVmapEnabled() {
4524
+ var _this_config_vmapUrl;
4525
+ return !!(this.config.isVmap && ((_this_config_vmapUrl = this.config.vmapUrl) === null || _this_config_vmapUrl === void 0 ? void 0 : _this_config_vmapUrl.trim()));
4526
+ }
4527
+ },
4520
4528
  {
4521
4529
  key: "fetchAdConfiguration",
4522
4530
  value: function fetchAdConfiguration() {
@@ -4525,7 +4533,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4525
4533
  return _ts_generator(this, function(_state) {
4526
4534
  switch(_state.label){
4527
4535
  case 0:
4528
- if (!this.config.vmapUrl) return [
4536
+ if (!this.isVmapEnabled()) return [
4529
4537
  3,
4530
4538
  2
4531
4539
  ];
@@ -4535,7 +4543,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4535
4543
  ];
4536
4544
  case 1:
4537
4545
  _state.sent();
4538
- _state.label = 2;
4546
+ if (this.config.debugAdTiming) {
4547
+ console.log("[StormcloudVideoPlayer] VMAP mode enabled");
4548
+ }
4549
+ return [
4550
+ 2
4551
+ ];
4539
4552
  case 2:
4540
4553
  vastMode = this.config.vastMode || "default";
4541
4554
  if (this.config.debugAdTiming) {
@@ -4703,7 +4716,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4703
4716
  }
4704
4717
  return [];
4705
4718
  }
4706
- var adBreakNodes = Array.from(doc.querySelectorAll("AdBreak, vmap\\:AdBreak"));
4719
+ var VMAP_NS = "http://www.iab.net/videosuite/vmap";
4720
+ var adBreakNodes = Array.from(doc.getElementsByTagNameNS(VMAP_NS, "AdBreak"));
4721
+ if (adBreakNodes.length === 0) {
4722
+ adBreakNodes = Array.from(doc.querySelectorAll("AdBreak, vmap\\:AdBreak"));
4723
+ }
4724
+ if (adBreakNodes.length === 0) {
4725
+ adBreakNodes = Array.from(doc.getElementsByTagName("*")).filter(function(el) {
4726
+ return el.localName === "AdBreak";
4727
+ });
4728
+ }
4707
4729
  var parsed = [];
4708
4730
  adBreakNodes.forEach(function(node, index) {
4709
4731
  var timeOffsetRaw = (node.getAttribute("timeOffset") || "").trim();
@@ -4711,7 +4733,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4711
4733
  if (startTimeMs == null) {
4712
4734
  return;
4713
4735
  }
4714
- var adTagNode = node.querySelector("AdTagURI, vmap\\:AdTagURI");
4736
+ var adTagNode = node.getElementsByTagNameNS(VMAP_NS, "AdTagURI")[0];
4737
+ if (!adTagNode) {
4738
+ var _node_querySelector;
4739
+ adTagNode = (_node_querySelector = node.querySelector("AdTagURI, vmap\\:AdTagURI")) !== null && _node_querySelector !== void 0 ? _node_querySelector : void 0;
4740
+ }
4715
4741
  var adTagUrl = ((adTagNode === null || adTagNode === void 0 ? void 0 : adTagNode.textContent) || "").trim();
4716
4742
  if (!adTagUrl) {
4717
4743
  return;
@@ -6268,23 +6294,49 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6268
6294
  key: "onTimeUpdate",
6269
6295
  value: function onTimeUpdate(currentTimeSec) {
6270
6296
  var _this = this;
6297
+ if (!this.isVmapEnabled() || this.vmapBreaks.length === 0) {
6298
+ return;
6299
+ }
6271
6300
  if (this.adPlayer.isAdPlaying() || this.inAdBreak) return;
6272
6301
  var nowMs = currentTimeSec * 1e3;
6273
6302
  var breakToPlay = this.findBreakForTime(nowMs);
6274
6303
  if (breakToPlay) {
6275
- void this.handleMidAdJoin(breakToPlay, nowMs).catch(function(error) {
6304
+ void this.handleVmapAdBreak(breakToPlay, nowMs).catch(function(error) {
6305
+ if (_this.config.debugAdTiming) {
6306
+ console.warn("[StormcloudVideoPlayer] VMAP ad break failed gracefully:", error);
6307
+ }
6308
+ });
6309
+ }
6310
+ }
6311
+ },
6312
+ {
6313
+ key: "onVideoEnded",
6314
+ value: function onVideoEnded() {
6315
+ var _this = this;
6316
+ if (!this.isVmapEnabled() || this.vmapBreaks.length === 0) {
6317
+ return;
6318
+ }
6319
+ if (this.adPlayer.isAdPlaying() || this.inAdBreak) {
6320
+ return;
6321
+ }
6322
+ var durationMs = Number.isFinite(this.video.duration) ? Math.floor(this.video.duration * 1e3) : 0;
6323
+ var postroll = this.vmapBreaks.find(function(b) {
6324
+ return b.startTimeMs === -1 && !_this.consumedVmapBreakIds.has(_this.getAdBreakKey(b));
6325
+ });
6326
+ if (postroll) {
6327
+ void this.handleVmapAdBreak(postroll, durationMs).catch(function(error) {
6276
6328
  if (_this.config.debugAdTiming) {
6277
- console.warn("[StormcloudVideoPlayer] Mid-roll VMAP join failed gracefully:", error);
6329
+ console.warn("[StormcloudVideoPlayer] VMAP post-roll failed gracefully:", error);
6278
6330
  }
6279
6331
  });
6280
6332
  }
6281
6333
  }
6282
6334
  },
6283
6335
  {
6284
- key: "handleMidAdJoin",
6285
- value: function handleMidAdJoin(adBreak, nowMs) {
6336
+ key: "handleVmapAdBreak",
6337
+ value: function handleVmapAdBreak(adBreak, nowMs) {
6286
6338
  return _async_to_generator(function() {
6287
- var _adBreak_durationMs, _this_config_driftToleranceMs, key, breakStartMs, durationMs, endMs, tol, inWindow, remainingMs, tags, first, rest, error;
6339
+ var _adBreak_durationMs, key, breakStartMs, durationMs, endMs, inWindow, tags, first, rest, error;
6288
6340
  return _ts_generator(this, function(_state) {
6289
6341
  switch(_state.label){
6290
6342
  case 0:
@@ -6302,25 +6354,28 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6302
6354
  }
6303
6355
  durationMs = (_adBreak_durationMs = adBreak.durationMs) !== null && _adBreak_durationMs !== void 0 ? _adBreak_durationMs : 0;
6304
6356
  endMs = breakStartMs + durationMs;
6305
- tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
6306
- inWindow = durationMs > 0 ? nowMs > breakStartMs && nowMs < endMs : nowMs + tol >= breakStartMs;
6357
+ inWindow = durationMs > 0 ? nowMs >= breakStartMs && nowMs < endMs : nowMs >= breakStartMs;
6307
6358
  if (!inWindow) return [
6308
6359
  3,
6309
6360
  4
6310
6361
  ];
6311
6362
  this.consumedVmapBreakIds.add(key);
6312
- remainingMs = durationMs > 0 ? Math.max(0, endMs - nowMs) : 0;
6313
- tags = this.selectVastTagsForBreak(adBreak) || (this.apiVastTagUrl ? [
6314
- this.apiVastTagUrl
6315
- ] : void 0);
6316
- if (!(tags && tags.length > 0)) return [
6317
- 3,
6318
- 4
6319
- ];
6363
+ tags = this.selectVastTagsForBreak(adBreak);
6364
+ if (!tags || tags.length === 0) {
6365
+ return [
6366
+ 2
6367
+ ];
6368
+ }
6320
6369
  first = tags[0];
6321
6370
  rest = tags.slice(1);
6322
6371
  this.adPodQueue = rest;
6323
6372
  this.adPlayer.updateOriginalMutedState(this.video.muted, this.video.volume);
6373
+ this.showAds = true;
6374
+ this.inAdBreak = true;
6375
+ this.currentAdBreakStartWallClockMs = Date.now();
6376
+ if (!this.video.paused) {
6377
+ this.video.pause();
6378
+ }
6324
6379
  _state.label = 1;
6325
6380
  case 1:
6326
6381
  _state.trys.push([
@@ -6335,10 +6390,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6335
6390
  ];
6336
6391
  case 2:
6337
6392
  _state.sent();
6338
- this.inAdBreak = true;
6339
- this.expectedAdBreakDurationMs = remainingMs;
6340
- this.currentAdBreakStartWallClockMs = Date.now();
6341
- this.scheduleAdStopCountdown(remainingMs);
6342
6393
  return [
6343
6394
  3,
6344
6395
  4
@@ -6346,8 +6397,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6346
6397
  case 3:
6347
6398
  error = _state.sent();
6348
6399
  this.adPodQueue = [];
6400
+ this.inAdBreak = false;
6401
+ this.showAds = false;
6349
6402
  if (this.config.debugAdTiming) {
6350
- console.warn("[StormcloudVideoPlayer] Mid-roll VMAP ad request failed:", error);
6403
+ console.warn("[StormcloudVideoPlayer] VMAP ad request failed:", error);
6351
6404
  }
6352
6405
  return [
6353
6406
  3,
@@ -7059,9 +7112,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7059
7112
  {
7060
7113
  key: "findBreakForTime",
7061
7114
  value: function findBreakForTime(nowMs) {
7062
- var _this_config_driftToleranceMs;
7063
7115
  var schedule = this.vmapBreaks;
7064
- var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
7065
7116
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
7066
7117
  try {
7067
7118
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
@@ -7073,9 +7124,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7073
7124
  if (breakStartMs == null) {
7074
7125
  continue;
7075
7126
  }
7076
- var end = breakStartMs + (b.durationMs || 0);
7077
- var effectiveTol = breakStartMs === 0 ? Math.max(tol, 3e4) : tol;
7078
- if (nowMs >= breakStartMs && (b.durationMs ? nowMs < end : nowMs <= breakStartMs + effectiveTol)) {
7127
+ if (b.durationMs) {
7128
+ var end = breakStartMs + b.durationMs;
7129
+ if (nowMs >= breakStartMs && nowMs < end) {
7130
+ return b;
7131
+ }
7132
+ continue;
7133
+ }
7134
+ if (nowMs >= breakStartMs) {
7079
7135
  return b;
7080
7136
  }
7081
7137
  }
@@ -7283,6 +7339,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7283
7339
  this.video.removeEventListener("timeupdate", this.timeUpdateHandler);
7284
7340
  delete this.timeUpdateHandler;
7285
7341
  }
7342
+ if (this.endedHandler) {
7343
+ this.video.removeEventListener("ended", this.endedHandler);
7344
+ delete this.endedHandler;
7345
+ }
7286
7346
  if (this.emptiedHandler) {
7287
7347
  this.video.removeEventListener("emptied", this.emptiedHandler);
7288
7348
  delete this.emptiedHandler;