stormcloud-video-player 0.8.3 → 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.
package/lib/index.cjs CHANGED
@@ -428,6 +428,9 @@ var __toCommonJS = function __toCommonJS(mod) {
428
428
  // src/index.ts
429
429
  var index_exports = {};
430
430
  __export(index_exports, {
431
+ DEFAULT_MQTT_CONFIG: function DEFAULT_MQTT_CONFIG1() {
432
+ return DEFAULT_MQTT_CONFIG;
433
+ },
431
434
  IS_BROWSER: function IS_BROWSER1() {
432
435
  return IS_BROWSER;
433
436
  },
@@ -440,6 +443,9 @@ __export(index_exports, {
440
443
  IS_SAFARI: function IS_SAFARI1() {
441
444
  return IS_SAFARI;
442
445
  },
446
+ MQTT_CA_CERT_FILE: function MQTT_CA_CERT_FILE1() {
447
+ return MQTT_CA_CERT_FILE;
448
+ },
443
449
  SUPPORTS_DASH: function SUPPORTS_DASH1() {
444
450
  return SUPPORTS_DASH;
445
451
  },
@@ -455,9 +461,21 @@ __export(index_exports, {
455
461
  StormcloudVideoPlayerComponent: function StormcloudVideoPlayerComponent1() {
456
462
  return StormcloudVideoPlayerComponent;
457
463
  },
464
+ applyMQTTConfig: function applyMQTTConfig1() {
465
+ return applyMQTTConfig;
466
+ },
467
+ buildMQTTBrokerUrl: function buildMQTTBrokerUrl1() {
468
+ return buildMQTTBrokerUrl;
469
+ },
470
+ buildPlayerTopic: function buildPlayerTopic1() {
471
+ return buildPlayerTopic;
472
+ },
458
473
  canPlay: function canPlay1() {
459
474
  return canPlay;
460
475
  },
476
+ configureMQTT: function configureMQTT1() {
477
+ return configureMQTT;
478
+ },
461
479
  createHlsAdPlayer: function createHlsAdPlayer1() {
462
480
  return createHlsAdPlayer;
463
481
  },
@@ -473,6 +491,12 @@ __export(index_exports, {
473
491
  detectBrowser: function detectBrowser1() {
474
492
  return detectBrowser;
475
493
  },
494
+ disconnectMQTT: function disconnectMQTT1() {
495
+ return disconnectMQTT;
496
+ },
497
+ ensureMQTTClient: function ensureMQTTClient1() {
498
+ return ensureMQTTClient;
499
+ },
476
500
  getBrowserConfigOverrides: function getBrowserConfigOverrides1() {
477
501
  return getBrowserConfigOverrides;
478
502
  },
@@ -482,9 +506,24 @@ __export(index_exports, {
482
506
  getClientInfo: function getClientInfo1() {
483
507
  return getClientInfo;
484
508
  },
509
+ getMQTTStatus: function getMQTTStatus1() {
510
+ return getMQTTStatus;
511
+ },
512
+ initMQTTClient: function initMQTTClient1() {
513
+ return initMQTTClient;
514
+ },
485
515
  initializePolyfills: function initializePolyfills1() {
486
516
  return initializePolyfills;
487
517
  },
518
+ isMQTTConfigured: function isMQTTConfigured1() {
519
+ return isMQTTConfigured;
520
+ },
521
+ isMQTTConnected: function isMQTTConnected1() {
522
+ return isMQTTConnected;
523
+ },
524
+ isMQTTEnabled: function isMQTTEnabled1() {
525
+ return isMQTTEnabled;
526
+ },
488
527
  isMediaStream: function isMediaStream1() {
489
528
  return isMediaStream;
490
529
  },
@@ -497,6 +536,9 @@ __export(index_exports, {
497
536
  merge: function merge1() {
498
537
  return merge;
499
538
  },
539
+ mqttConfig: function mqttConfig1() {
540
+ return mqttConfig;
541
+ },
500
542
  omit: function omit1() {
501
543
  return omit;
502
544
  },
@@ -506,6 +548,9 @@ __export(index_exports, {
506
548
  players: function players() {
507
549
  return players_default;
508
550
  },
551
+ publishMQTT: function publishMQTT1() {
552
+ return publishMQTT;
553
+ },
509
554
  randomString: function randomString1() {
510
555
  return randomString;
511
556
  },
@@ -1524,6 +1569,123 @@ function createPalNonceManager() {
1524
1569
  }
1525
1570
  };
1526
1571
  }
1572
+ // src/utils/mqttConfig.ts
1573
+ var MQTT_CA_CERT_FILE = "src/certs/emqxsl-ca.crt";
1574
+ var DEFAULT_MQTT_CONFIG = {
1575
+ enabled: true,
1576
+ brokerAddress: "vecbae77.ala.us-east-1.emqxsl.com",
1577
+ brokerPort: 8883,
1578
+ wsPort: 8084,
1579
+ username: "for-sonifi",
1580
+ password: "sonifi-mqtt",
1581
+ topicPrefix: "adstorm/players",
1582
+ qos: 1
1583
+ };
1584
+ var mqttConfig = _object_spread({}, DEFAULT_MQTT_CONFIG);
1585
+ function applyMQTTConfig(overrides) {
1586
+ Object.assign(mqttConfig, overrides);
1587
+ }
1588
+ function isMQTTEnabled() {
1589
+ return mqttConfig.enabled;
1590
+ }
1591
+ function buildMQTTBrokerUrl() {
1592
+ if (mqttConfig.brokerUrl) return mqttConfig.brokerUrl;
1593
+ return "wss://".concat(mqttConfig.brokerAddress, ":").concat(mqttConfig.wsPort, "/mqtt");
1594
+ }
1595
+ function buildPlayerTopic(licenseKey, channel) {
1596
+ return "".concat(mqttConfig.topicPrefix, "/").concat(licenseKey, "/").concat(channel);
1597
+ }
1598
+ // src/utils/mqttClient.ts
1599
+ var import_mqtt = __toESM(require("mqtt"), 1);
1600
+ var LOG = "[StormcloudVideoPlayer][MQTT]";
1601
+ var client = null;
1602
+ var status = "disconnected";
1603
+ function getMQTTStatus() {
1604
+ return status;
1605
+ }
1606
+ function isMQTTConnected() {
1607
+ return status === "connected" && client !== null && client.connected;
1608
+ }
1609
+ function isMQTTConfigured() {
1610
+ return isMQTTEnabled();
1611
+ }
1612
+ function configureMQTT(overrides) {
1613
+ applyMQTTConfig(overrides);
1614
+ disconnectMQTT();
1615
+ }
1616
+ function initMQTTClient() {
1617
+ if (client || !isMQTTEnabled()) return;
1618
+ var url = buildMQTTBrokerUrl();
1619
+ status = "connecting";
1620
+ var clientId = "stormcloud-vp-".concat(Math.random().toString(36).slice(2, 9));
1621
+ try {
1622
+ client = import_mqtt.default.connect(url, {
1623
+ clientId: clientId,
1624
+ username: mqttConfig.username,
1625
+ password: mqttConfig.password,
1626
+ keepalive: 60,
1627
+ clean: true,
1628
+ reconnectPeriod: 5e3,
1629
+ connectTimeout: 1e4,
1630
+ queueQoSZero: false
1631
+ });
1632
+ } catch (err) {
1633
+ status = "error";
1634
+ console.warn("".concat(LOG, " connect() threw:"), err);
1635
+ return;
1636
+ }
1637
+ client.on("connect", function() {
1638
+ status = "connected";
1639
+ console.info("".concat(LOG, " connected to ").concat(url));
1640
+ });
1641
+ client.on("reconnect", function() {
1642
+ status = "connecting";
1643
+ console.info("".concat(LOG, " reconnecting…"));
1644
+ });
1645
+ client.on("offline", function() {
1646
+ status = "disconnected";
1647
+ console.warn("".concat(LOG, " offline"));
1648
+ });
1649
+ client.on("error", function(err) {
1650
+ status = "error";
1651
+ console.warn("".concat(LOG, " error:"), err.message);
1652
+ });
1653
+ client.on("close", function() {
1654
+ if (status === "connected") {
1655
+ status = "disconnected";
1656
+ }
1657
+ });
1658
+ }
1659
+ function ensureMQTTClient() {
1660
+ if (isMQTTEnabled() && !client) {
1661
+ initMQTTClient();
1662
+ }
1663
+ }
1664
+ function publishMQTT(topic, payload) {
1665
+ if (!isMQTTEnabled()) {
1666
+ return false;
1667
+ }
1668
+ ensureMQTTClient();
1669
+ if (!client) {
1670
+ return false;
1671
+ }
1672
+ try {
1673
+ client.publish(topic, JSON.stringify(payload), {
1674
+ qos: mqttConfig.qos
1675
+ });
1676
+ return true;
1677
+ } catch (err) {
1678
+ console.warn("".concat(LOG, " publish failed on ").concat(topic, ":"), err);
1679
+ return false;
1680
+ }
1681
+ }
1682
+ function disconnectMQTT() {
1683
+ if (client) {
1684
+ client.end(true);
1685
+ client = null;
1686
+ status = "disconnected";
1687
+ }
1688
+ }
1527
1689
  // src/utils/tracking.ts
1528
1690
  var cachedBrowserId = null;
1529
1691
  function getClientInfo() {
@@ -1670,7 +1832,7 @@ function getClientInfo() {
1670
1832
  }
1671
1833
  function getBrowserID(clientInfo) {
1672
1834
  return _async_to_generator(function() {
1673
- var fingerprintString, encodedData, utf8, buffer, i, hashBuffer, hashArray, hashHex, error, hash, i1, char, fallbackHash, timestamp, random;
1835
+ var _crypto_subtle, fingerprintString, encodedData, utf8, buffer, i, hashBuffer, hashHex, unused, hash, i1, char, fallbackHash, timestamp, random;
1674
1836
  return _ts_generator(this, function(_state) {
1675
1837
  switch(_state.label){
1676
1838
  case 0:
@@ -1681,7 +1843,7 @@ function getBrowserID(clientInfo) {
1681
1843
  ];
1682
1844
  }
1683
1845
  fingerprintString = JSON.stringify(clientInfo);
1684
- if (!(typeof crypto !== "undefined" && crypto.subtle && crypto.subtle.digest)) return [
1846
+ if (!(typeof crypto !== "undefined" && ((_crypto_subtle = crypto.subtle) === null || _crypto_subtle === void 0 ? void 0 : _crypto_subtle.digest))) return [
1685
1847
  3,
1686
1848
  5
1687
1849
  ];
@@ -1719,8 +1881,7 @@ function getBrowserID(clientInfo) {
1719
1881
  ];
1720
1882
  case 3:
1721
1883
  hashBuffer = _state.sent();
1722
- hashArray = Array.from(new Uint8Array(hashBuffer));
1723
- hashHex = hashArray.map(function(b) {
1884
+ hashHex = Array.from(new Uint8Array(hashBuffer)).map(function(b) {
1724
1885
  return b.toString(16).padStart(2, "0");
1725
1886
  }).join("");
1726
1887
  cachedBrowserId = hashHex;
@@ -1729,8 +1890,8 @@ function getBrowserID(clientInfo) {
1729
1890
  hashHex
1730
1891
  ];
1731
1892
  case 4:
1732
- error = _state.sent();
1733
- console.warn("[StormcloudVideoPlayer] crypto.subtle.digest not supported, using fallback hash");
1893
+ unused = _state.sent();
1894
+ console.warn("[StormcloudVideoPlayer] crypto.subtle not supported, using fallback hash");
1734
1895
  return [
1735
1896
  3,
1736
1897
  5
@@ -1754,177 +1915,91 @@ function getBrowserID(clientInfo) {
1754
1915
  });
1755
1916
  })();
1756
1917
  }
1757
- var PLAYER_TRACKING_BASE_URL = "https://adstorm.co/api-adstorm-dev/adstorm/player-tracking";
1758
- var TRACK_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/metrics/ingest");
1759
- var HEARTBEAT_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/heartbeat");
1760
- var IMPRESSIONS_URL = "".concat(PLAYER_TRACKING_BASE_URL, "/impressions/ingest");
1761
- function buildHeaders(licenseKey) {
1762
- var headers = {
1763
- "Content-Type": "application/json"
1764
- };
1765
- if (licenseKey) {
1766
- headers["Authorization"] = "Bearer ".concat(licenseKey);
1767
- }
1768
- return headers;
1918
+ function canPublish(licenseKey) {
1919
+ return Boolean(isMQTTEnabled() && licenseKey);
1769
1920
  }
1770
- function sendTrackRequest(licenseKey, body) {
1921
+ function buildPlayerMetricEvent() {
1771
1922
  return _async_to_generator(function() {
1772
- var response;
1773
- return _ts_generator(this, function(_state) {
1774
- switch(_state.label){
1775
- case 0:
1776
- return [
1777
- 4,
1778
- fetch(TRACK_URL, {
1779
- method: "POST",
1780
- headers: buildHeaders(licenseKey),
1781
- body: JSON.stringify(body)
1782
- })
1783
- ];
1784
- case 1:
1785
- response = _state.sent();
1786
- if (!response.ok) {
1787
- throw new Error("HTTP error! status: ".concat(response.status));
1788
- }
1789
- return [
1790
- 4,
1791
- response.json()
1792
- ];
1793
- case 2:
1794
- _state.sent();
1795
- return [
1796
- 2
1797
- ];
1798
- }
1799
- });
1800
- })();
1801
- }
1802
- function postJson(url, licenseKey, body) {
1803
- return _async_to_generator(function() {
1804
- var response;
1805
- return _ts_generator(this, function(_state) {
1806
- switch(_state.label){
1807
- case 0:
1808
- return [
1809
- 4,
1810
- fetch(url, {
1811
- method: "POST",
1812
- headers: buildHeaders(licenseKey),
1813
- body: JSON.stringify(body)
1814
- })
1815
- ];
1816
- case 1:
1817
- response = _state.sent();
1818
- if (!response.ok) {
1819
- throw new Error("HTTP error! status: ".concat(response.status));
1820
- }
1821
- return [
1822
- 4,
1823
- response.json()
1824
- ];
1825
- case 2:
1826
- _state.sent();
1827
- return [
1828
- 2
1829
- ];
1830
- }
1831
- });
1832
- })();
1833
- }
1834
- function buildPlayerMetricEvent(_0) {
1835
- return _async_to_generator(function(licenseKey) {
1836
- var context, flags, _flags_captureAt, clientInfo, browserId, captureAt;
1923
+ var context, flags, _flags_captureAt, _flags_adLoaded, _flags_adDetect, clientInfo, playerId, captureAt;
1837
1924
  var _arguments = arguments;
1838
1925
  return _ts_generator(this, function(_state) {
1839
1926
  switch(_state.label){
1840
1927
  case 0:
1841
- context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
1928
+ context = _arguments.length > 0 && _arguments[0] !== void 0 ? _arguments[0] : {}, flags = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
1842
1929
  clientInfo = getClientInfo();
1843
1930
  return [
1844
1931
  4,
1845
1932
  getBrowserID(clientInfo)
1846
1933
  ];
1847
1934
  case 1:
1848
- browserId = _state.sent();
1935
+ playerId = _state.sent();
1849
1936
  captureAt = (_flags_captureAt = flags.captureAt) !== null && _flags_captureAt !== void 0 ? _flags_captureAt : /* @__PURE__ */ new Date().toISOString();
1850
1937
  return [
1851
1938
  2,
1852
- {
1853
- player_id: browserId,
1854
- browserId: browserId,
1939
+ _object_spread({
1940
+ player_id: playerId,
1855
1941
  device_type: clientInfo.deviceType,
1856
- deviceType: clientInfo.deviceType,
1857
- input_stream_type: context.inputStreamType,
1858
- os: clientInfo.os,
1859
- ad_loaded: flags.adLoaded,
1860
- ad_detect: flags.adDetect,
1861
- license_key: licenseKey,
1862
- capture_at: captureAt,
1863
- timestamp: captureAt
1864
- }
1942
+ os: clientInfo.os.toLowerCase(),
1943
+ ad_loaded: (_flags_adLoaded = flags.adLoaded) !== null && _flags_adLoaded !== void 0 ? _flags_adLoaded : false,
1944
+ ad_detect: (_flags_adDetect = flags.adDetect) !== null && _flags_adDetect !== void 0 ? _flags_adDetect : false,
1945
+ capture_at: captureAt
1946
+ }, context.inputStreamType ? {
1947
+ input_stream_type: context.inputStreamType
1948
+ } : {})
1865
1949
  ];
1866
1950
  }
1867
1951
  });
1868
1952
  }).apply(this, arguments);
1869
1953
  }
1954
+ function publishTracking(licenseKey, channel, body) {
1955
+ ensureMQTTClient();
1956
+ publishMQTT(buildPlayerTopic(licenseKey, channel), body);
1957
+ }
1870
1958
  function sendInitialTracking(_0) {
1871
1959
  return _async_to_generator(function(licenseKey) {
1872
- var context, clientInfo, browserId, trackingData, error;
1960
+ var context, metricEvent, error;
1873
1961
  var _arguments = arguments;
1874
1962
  return _ts_generator(this, function(_state) {
1875
1963
  switch(_state.label){
1876
1964
  case 0:
1877
1965
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {};
1966
+ if (!canPublish(licenseKey)) return [
1967
+ 2
1968
+ ];
1878
1969
  _state.label = 1;
1879
1970
  case 1:
1880
1971
  _state.trys.push([
1881
1972
  1,
1882
- 4,
1973
+ 3,
1883
1974
  ,
1884
- 5
1975
+ 4
1885
1976
  ]);
1886
- clientInfo = getClientInfo();
1887
1977
  return [
1888
1978
  4,
1889
- getBrowserID(clientInfo)
1890
- ];
1891
- case 2:
1892
- browserId = _state.sent();
1893
- trackingData = _object_spread({
1894
- browserId: browserId
1895
- }, clientInfo);
1896
- return [
1897
- 4,
1898
- sendTrackRequest(licenseKey, {
1899
- events: [
1900
- {
1901
- player_id: browserId,
1902
- device_type: clientInfo.deviceType,
1903
- input_stream_type: context.inputStreamType,
1904
- os: clientInfo.os,
1905
- ad_loaded: false,
1906
- ad_detect: false,
1907
- license_key: licenseKey,
1908
- capture_at: /* @__PURE__ */ new Date().toISOString()
1909
- }
1910
- ],
1911
- trackingData: trackingData
1979
+ buildPlayerMetricEvent(context, {
1980
+ adLoaded: false,
1981
+ adDetect: false
1912
1982
  })
1913
1983
  ];
1914
- case 3:
1915
- _state.sent();
1984
+ case 2:
1985
+ metricEvent = _state.sent();
1986
+ publishTracking(licenseKey, "metrics", {
1987
+ events: [
1988
+ metricEvent
1989
+ ]
1990
+ });
1916
1991
  return [
1917
1992
  3,
1918
- 5
1993
+ 4
1919
1994
  ];
1920
- case 4:
1995
+ case 3:
1921
1996
  error = _state.sent();
1922
1997
  console.error("[StormcloudVideoPlayer] Error sending initial tracking data:", error);
1923
1998
  return [
1924
1999
  3,
1925
- 5
2000
+ 4
1926
2001
  ];
1927
- case 5:
2002
+ case 4:
1928
2003
  return [
1929
2004
  2
1930
2005
  ];
@@ -2028,53 +2103,48 @@ function sendAdImpressionTracking(_0, _1) {
2028
2103
  switch(_state.label){
2029
2104
  case 0:
2030
2105
  context = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2106
+ if (!canPublish(licenseKey)) return [
2107
+ 2
2108
+ ];
2031
2109
  _state.label = 1;
2032
2110
  case 1:
2033
2111
  _state.trys.push([
2034
2112
  1,
2035
- 4,
2113
+ 3,
2036
2114
  ,
2037
- 5
2115
+ 4
2038
2116
  ]);
2039
2117
  return [
2040
2118
  4,
2041
- buildPlayerMetricEvent(licenseKey, context, {
2119
+ buildPlayerMetricEvent(context, {
2042
2120
  captureAt: adImpressionInfo.timestamp
2043
2121
  })
2044
2122
  ];
2045
2123
  case 2:
2046
2124
  metricEvent = _state.sent();
2047
- return [
2048
- 4,
2049
- Promise.all([
2050
- postJson(HEARTBEAT_URL, licenseKey, metricEvent),
2051
- postJson(IMPRESSIONS_URL, licenseKey, {
2052
- events: [
2053
- {
2054
- player_id: metricEvent.player_id,
2055
- ad_played_count: 1,
2056
- ad_url: adImpressionInfo.adUrl,
2057
- license_key: licenseKey,
2058
- capture_at: adImpressionInfo.timestamp
2059
- }
2060
- ]
2061
- })
2062
- ])
2063
- ];
2064
- case 3:
2065
- _state.sent();
2125
+ publishTracking(licenseKey, "heartbeat", metricEvent);
2126
+ publishTracking(licenseKey, "impressions", {
2127
+ events: [
2128
+ {
2129
+ player_id: metricEvent.player_id,
2130
+ ad_played_count: 1,
2131
+ ad_url: adImpressionInfo.adUrl,
2132
+ capture_at: adImpressionInfo.timestamp
2133
+ }
2134
+ ]
2135
+ });
2066
2136
  return [
2067
2137
  3,
2068
- 5
2138
+ 4
2069
2139
  ];
2070
- case 4:
2140
+ case 3:
2071
2141
  error = _state.sent();
2072
2142
  console.error("[StormcloudVideoPlayer] Error sending ad impression tracking:", error);
2073
2143
  return [
2074
2144
  3,
2075
- 5
2145
+ 4
2076
2146
  ];
2077
- case 5:
2147
+ case 4:
2078
2148
  return [
2079
2149
  2
2080
2150
  ];
@@ -2090,38 +2160,36 @@ function sendHeartbeat(_0) {
2090
2160
  switch(_state.label){
2091
2161
  case 0:
2092
2162
  context = _arguments.length > 1 && _arguments[1] !== void 0 ? _arguments[1] : {}, flags = _arguments.length > 2 && _arguments[2] !== void 0 ? _arguments[2] : {};
2163
+ if (!canPublish(licenseKey)) return [
2164
+ 2
2165
+ ];
2093
2166
  _state.label = 1;
2094
2167
  case 1:
2095
2168
  _state.trys.push([
2096
2169
  1,
2097
- 4,
2170
+ 3,
2098
2171
  ,
2099
- 5
2172
+ 4
2100
2173
  ]);
2101
2174
  return [
2102
2175
  4,
2103
- buildPlayerMetricEvent(licenseKey, context, flags)
2176
+ buildPlayerMetricEvent(context, flags)
2104
2177
  ];
2105
2178
  case 2:
2106
2179
  heartbeatData = _state.sent();
2107
- return [
2108
- 4,
2109
- postJson(HEARTBEAT_URL, licenseKey, heartbeatData)
2110
- ];
2111
- case 3:
2112
- _state.sent();
2180
+ publishTracking(licenseKey, "heartbeat", heartbeatData);
2113
2181
  return [
2114
2182
  3,
2115
- 5
2183
+ 4
2116
2184
  ];
2117
- case 4:
2185
+ case 3:
2118
2186
  error = _state.sent();
2119
2187
  console.error("[StormcloudVideoPlayer] Error sending heartbeat:", error);
2120
2188
  return [
2121
2189
  3,
2122
- 5
2190
+ 4
2123
2191
  ];
2124
- case 5:
2192
+ case 4:
2125
2193
  return [
2126
2194
  2
2127
2195
  ];
@@ -2767,7 +2835,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
2767
2835
  var _level_details, _level_details1;
2768
2836
  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";
2769
2837
  })) !== null && _ref !== void 0 ? _ref : false;
2770
- if (!this.isLiveStream && this.vmapBreaks.length === 0 && this.apiVastTagUrl) {
2838
+ if (!this.isVmapEnabled() && !this.isLiveStream && this.vmapBreaks.length === 0 && this.apiVastTagUrl) {
2771
2839
  prerollKey = "synthetic-vod-preroll";
2772
2840
  if (!this.consumedVmapBreakIds.has(prerollKey)) {
2773
2841
  this.vmapBreaks = [
@@ -3352,12 +3420,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
3352
3420
  _this.palNonce.sendPlaybackStart();
3353
3421
  }
3354
3422
  });
3355
- this.video.addEventListener("ended", function() {
3423
+ this.endedHandler = function() {
3356
3424
  if (_this.palPlaybackStarted) {
3357
3425
  _this.palPlaybackStarted = false;
3358
3426
  _this.palNonce.sendPlaybackEnd();
3359
3427
  }
3360
- });
3428
+ _this.onVideoEnded();
3429
+ };
3430
+ this.video.addEventListener("ended", this.endedHandler);
3361
3431
  this.video.addEventListener("mousedown", function(e) {
3362
3432
  _this.palNonce.sendAdTouch(e);
3363
3433
  });
@@ -4679,6 +4749,13 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4679
4749
  }
4680
4750
  }
4681
4751
  },
4752
+ {
4753
+ key: "isVmapEnabled",
4754
+ value: function isVmapEnabled() {
4755
+ var _this_config_vmapUrl;
4756
+ return !!(this.config.isVmap && ((_this_config_vmapUrl = this.config.vmapUrl) === null || _this_config_vmapUrl === void 0 ? void 0 : _this_config_vmapUrl.trim()));
4757
+ }
4758
+ },
4682
4759
  {
4683
4760
  key: "fetchAdConfiguration",
4684
4761
  value: function fetchAdConfiguration() {
@@ -4687,7 +4764,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4687
4764
  return _ts_generator(this, function(_state) {
4688
4765
  switch(_state.label){
4689
4766
  case 0:
4690
- if (!this.config.vmapUrl) return [
4767
+ if (!this.isVmapEnabled()) return [
4691
4768
  3,
4692
4769
  2
4693
4770
  ];
@@ -4697,7 +4774,12 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4697
4774
  ];
4698
4775
  case 1:
4699
4776
  _state.sent();
4700
- _state.label = 2;
4777
+ if (this.config.debugAdTiming) {
4778
+ console.log("[StormcloudVideoPlayer] VMAP mode enabled");
4779
+ }
4780
+ return [
4781
+ 2
4782
+ ];
4701
4783
  case 2:
4702
4784
  vastMode = this.config.vastMode || "default";
4703
4785
  if (this.config.debugAdTiming) {
@@ -4865,7 +4947,16 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4865
4947
  }
4866
4948
  return [];
4867
4949
  }
4868
- var adBreakNodes = Array.from(doc.querySelectorAll("AdBreak, vmap\\:AdBreak"));
4950
+ var VMAP_NS = "http://www.iab.net/videosuite/vmap";
4951
+ var adBreakNodes = Array.from(doc.getElementsByTagNameNS(VMAP_NS, "AdBreak"));
4952
+ if (adBreakNodes.length === 0) {
4953
+ adBreakNodes = Array.from(doc.querySelectorAll("AdBreak, vmap\\:AdBreak"));
4954
+ }
4955
+ if (adBreakNodes.length === 0) {
4956
+ adBreakNodes = Array.from(doc.getElementsByTagName("*")).filter(function(el) {
4957
+ return el.localName === "AdBreak";
4958
+ });
4959
+ }
4869
4960
  var parsed = [];
4870
4961
  adBreakNodes.forEach(function(node, index) {
4871
4962
  var timeOffsetRaw = (node.getAttribute("timeOffset") || "").trim();
@@ -4873,7 +4964,11 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
4873
4964
  if (startTimeMs == null) {
4874
4965
  return;
4875
4966
  }
4876
- var adTagNode = node.querySelector("AdTagURI, vmap\\:AdTagURI");
4967
+ var adTagNode = node.getElementsByTagNameNS(VMAP_NS, "AdTagURI")[0];
4968
+ if (!adTagNode) {
4969
+ var _node_querySelector;
4970
+ adTagNode = (_node_querySelector = node.querySelector("AdTagURI, vmap\\:AdTagURI")) !== null && _node_querySelector !== void 0 ? _node_querySelector : void 0;
4971
+ }
4877
4972
  var adTagUrl = ((adTagNode === null || adTagNode === void 0 ? void 0 : adTagNode.textContent) || "").trim();
4878
4973
  if (!adTagUrl) {
4879
4974
  return;
@@ -6430,23 +6525,49 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6430
6525
  key: "onTimeUpdate",
6431
6526
  value: function onTimeUpdate(currentTimeSec) {
6432
6527
  var _this = this;
6528
+ if (!this.isVmapEnabled() || this.vmapBreaks.length === 0) {
6529
+ return;
6530
+ }
6433
6531
  if (this.adPlayer.isAdPlaying() || this.inAdBreak) return;
6434
6532
  var nowMs = currentTimeSec * 1e3;
6435
6533
  var breakToPlay = this.findBreakForTime(nowMs);
6436
6534
  if (breakToPlay) {
6437
- void this.handleMidAdJoin(breakToPlay, nowMs).catch(function(error) {
6535
+ void this.handleVmapAdBreak(breakToPlay, nowMs).catch(function(error) {
6438
6536
  if (_this.config.debugAdTiming) {
6439
- console.warn("[StormcloudVideoPlayer] Mid-roll VMAP join failed gracefully:", error);
6537
+ console.warn("[StormcloudVideoPlayer] VMAP ad break failed gracefully:", error);
6440
6538
  }
6441
6539
  });
6442
6540
  }
6443
6541
  }
6444
6542
  },
6445
6543
  {
6446
- key: "handleMidAdJoin",
6447
- value: function handleMidAdJoin(adBreak, nowMs) {
6544
+ key: "onVideoEnded",
6545
+ value: function onVideoEnded() {
6546
+ var _this = this;
6547
+ if (!this.isVmapEnabled() || this.vmapBreaks.length === 0) {
6548
+ return;
6549
+ }
6550
+ if (this.adPlayer.isAdPlaying() || this.inAdBreak) {
6551
+ return;
6552
+ }
6553
+ var durationMs = Number.isFinite(this.video.duration) ? Math.floor(this.video.duration * 1e3) : 0;
6554
+ var postroll = this.vmapBreaks.find(function(b) {
6555
+ return b.startTimeMs === -1 && !_this.consumedVmapBreakIds.has(_this.getAdBreakKey(b));
6556
+ });
6557
+ if (postroll) {
6558
+ void this.handleVmapAdBreak(postroll, durationMs).catch(function(error) {
6559
+ if (_this.config.debugAdTiming) {
6560
+ console.warn("[StormcloudVideoPlayer] VMAP post-roll failed gracefully:", error);
6561
+ }
6562
+ });
6563
+ }
6564
+ }
6565
+ },
6566
+ {
6567
+ key: "handleVmapAdBreak",
6568
+ value: function handleVmapAdBreak(adBreak, nowMs) {
6448
6569
  return _async_to_generator(function() {
6449
- var _adBreak_durationMs, _this_config_driftToleranceMs, key, breakStartMs, durationMs, endMs, tol, inWindow, remainingMs, tags, first, rest, error;
6570
+ var _adBreak_durationMs, key, breakStartMs, durationMs, endMs, inWindow, tags, first, rest, error;
6450
6571
  return _ts_generator(this, function(_state) {
6451
6572
  switch(_state.label){
6452
6573
  case 0:
@@ -6464,25 +6585,28 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6464
6585
  }
6465
6586
  durationMs = (_adBreak_durationMs = adBreak.durationMs) !== null && _adBreak_durationMs !== void 0 ? _adBreak_durationMs : 0;
6466
6587
  endMs = breakStartMs + durationMs;
6467
- tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
6468
- inWindow = durationMs > 0 ? nowMs > breakStartMs && nowMs < endMs : nowMs + tol >= breakStartMs;
6588
+ inWindow = durationMs > 0 ? nowMs >= breakStartMs && nowMs < endMs : nowMs >= breakStartMs;
6469
6589
  if (!inWindow) return [
6470
6590
  3,
6471
6591
  4
6472
6592
  ];
6473
6593
  this.consumedVmapBreakIds.add(key);
6474
- remainingMs = durationMs > 0 ? Math.max(0, endMs - nowMs) : 0;
6475
- tags = this.selectVastTagsForBreak(adBreak) || (this.apiVastTagUrl ? [
6476
- this.apiVastTagUrl
6477
- ] : void 0);
6478
- if (!(tags && tags.length > 0)) return [
6479
- 3,
6480
- 4
6481
- ];
6594
+ tags = this.selectVastTagsForBreak(adBreak);
6595
+ if (!tags || tags.length === 0) {
6596
+ return [
6597
+ 2
6598
+ ];
6599
+ }
6482
6600
  first = tags[0];
6483
6601
  rest = tags.slice(1);
6484
6602
  this.adPodQueue = rest;
6485
6603
  this.adPlayer.updateOriginalMutedState(this.video.muted, this.video.volume);
6604
+ this.showAds = true;
6605
+ this.inAdBreak = true;
6606
+ this.currentAdBreakStartWallClockMs = Date.now();
6607
+ if (!this.video.paused) {
6608
+ this.video.pause();
6609
+ }
6486
6610
  _state.label = 1;
6487
6611
  case 1:
6488
6612
  _state.trys.push([
@@ -6497,10 +6621,6 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6497
6621
  ];
6498
6622
  case 2:
6499
6623
  _state.sent();
6500
- this.inAdBreak = true;
6501
- this.expectedAdBreakDurationMs = remainingMs;
6502
- this.currentAdBreakStartWallClockMs = Date.now();
6503
- this.scheduleAdStopCountdown(remainingMs);
6504
6624
  return [
6505
6625
  3,
6506
6626
  4
@@ -6508,8 +6628,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
6508
6628
  case 3:
6509
6629
  error = _state.sent();
6510
6630
  this.adPodQueue = [];
6631
+ this.inAdBreak = false;
6632
+ this.showAds = false;
6511
6633
  if (this.config.debugAdTiming) {
6512
- console.warn("[StormcloudVideoPlayer] Mid-roll VMAP ad request failed:", error);
6634
+ console.warn("[StormcloudVideoPlayer] VMAP ad request failed:", error);
6513
6635
  }
6514
6636
  return [
6515
6637
  3,
@@ -7221,9 +7343,7 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7221
7343
  {
7222
7344
  key: "findBreakForTime",
7223
7345
  value: function findBreakForTime(nowMs) {
7224
- var _this_config_driftToleranceMs;
7225
7346
  var schedule = this.vmapBreaks;
7226
- var tol = (_this_config_driftToleranceMs = this.config.driftToleranceMs) !== null && _this_config_driftToleranceMs !== void 0 ? _this_config_driftToleranceMs : 1e3;
7227
7347
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
7228
7348
  try {
7229
7349
  for(var _iterator = schedule[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
@@ -7235,9 +7355,14 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7235
7355
  if (breakStartMs == null) {
7236
7356
  continue;
7237
7357
  }
7238
- var end = breakStartMs + (b.durationMs || 0);
7239
- var effectiveTol = breakStartMs === 0 ? Math.max(tol, 3e4) : tol;
7240
- if (nowMs >= breakStartMs && (b.durationMs ? nowMs < end : nowMs <= breakStartMs + effectiveTol)) {
7358
+ if (b.durationMs) {
7359
+ var end = breakStartMs + b.durationMs;
7360
+ if (nowMs >= breakStartMs && nowMs < end) {
7361
+ return b;
7362
+ }
7363
+ continue;
7364
+ }
7365
+ if (nowMs >= breakStartMs) {
7241
7366
  return b;
7242
7367
  }
7243
7368
  }
@@ -7445,6 +7570,10 @@ var StormcloudVideoPlayer = /*#__PURE__*/ function() {
7445
7570
  this.video.removeEventListener("timeupdate", this.timeUpdateHandler);
7446
7571
  delete this.timeUpdateHandler;
7447
7572
  }
7573
+ if (this.endedHandler) {
7574
+ this.video.removeEventListener("ended", this.endedHandler);
7575
+ delete this.endedHandler;
7576
+ }
7448
7577
  if (this.emptiedHandler) {
7449
7578
  this.video.removeEventListener("emptied", this.emptiedHandler);
7450
7579
  delete this.emptiedHandler;
@@ -7475,6 +7604,7 @@ var CRITICAL_PROPS = [
7475
7604
  "allowNativeHls",
7476
7605
  "isLiveStream",
7477
7606
  "licenseKey",
7607
+ "isVmap",
7478
7608
  "vmapUrl",
7479
7609
  "lowLatencyMode",
7480
7610
  "driftToleranceMs",
@@ -7483,7 +7613,7 @@ var CRITICAL_PROPS = [
7483
7613
  var CONTROLS_HIDE_DELAY = 3e3;
7484
7614
  var DEFAULT_PLAYER_ASPECT_RATIO = 16 / 9;
7485
7615
  var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
7486
- var src = props.src, autoplay = props.autoplay, muted = props.muted, lowLatencyMode = props.lowLatencyMode, allowNativeHls = props.allowNativeHls, isLiveStream = props.isLiveStream, driftToleranceMs = props.driftToleranceMs, immediateManifestAds = props.immediateManifestAds, debugAdTiming = props.debugAdTiming, showCustomControls = props.showCustomControls, hideLoadingIndicator = props.hideLoadingIndicator, onVolumeToggle = props.onVolumeToggle, onFullscreenToggle = props.onFullscreenToggle, onControlClick = props.onControlClick, onReady = props.onReady, wrapperClassName = props.wrapperClassName, wrapperStyle = props.wrapperStyle, className = props.className, style = props.style, controls = props.controls, playsInline = props.playsInline, preload = props.preload, poster = props.poster, children = props.children, licenseKey = props.licenseKey, vastMode = props.vastMode, vastTagUrl = props.vastTagUrl, vmapUrl = props.vmapUrl, minSegmentsBeforePlay = props.minSegmentsBeforePlay, restVideoAttrs = _object_without_properties(props, [
7616
+ var src = props.src, autoplay = props.autoplay, muted = props.muted, lowLatencyMode = props.lowLatencyMode, allowNativeHls = props.allowNativeHls, isLiveStream = props.isLiveStream, driftToleranceMs = props.driftToleranceMs, immediateManifestAds = props.immediateManifestAds, debugAdTiming = props.debugAdTiming, showCustomControls = props.showCustomControls, hideLoadingIndicator = props.hideLoadingIndicator, onVolumeToggle = props.onVolumeToggle, onFullscreenToggle = props.onFullscreenToggle, onControlClick = props.onControlClick, onReady = props.onReady, wrapperClassName = props.wrapperClassName, wrapperStyle = props.wrapperStyle, className = props.className, style = props.style, controls = props.controls, playsInline = props.playsInline, preload = props.preload, poster = props.poster, children = props.children, licenseKey = props.licenseKey, vastMode = props.vastMode, vastTagUrl = props.vastTagUrl, isVmap = props.isVmap, vmapUrl = props.vmapUrl, minSegmentsBeforePlay = props.minSegmentsBeforePlay, restVideoAttrs = _object_without_properties(props, [
7487
7617
  "src",
7488
7618
  "autoplay",
7489
7619
  "muted",
@@ -7511,6 +7641,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
7511
7641
  "licenseKey",
7512
7642
  "vastMode",
7513
7643
  "vastTagUrl",
7644
+ "isVmap",
7514
7645
  "vmapUrl",
7515
7646
  "minSegmentsBeforePlay"
7516
7647
  ]);
@@ -7653,6 +7784,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
7653
7784
  allowNativeHls,
7654
7785
  isLiveStream,
7655
7786
  licenseKey,
7787
+ isVmap,
7656
7788
  vmapUrl,
7657
7789
  lowLatencyMode,
7658
7790
  driftToleranceMs,
@@ -7697,6 +7829,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(function(props) {
7697
7829
  if (licenseKey !== void 0) cfg.licenseKey = licenseKey;
7698
7830
  if (vastMode !== void 0) cfg.vastMode = vastMode;
7699
7831
  if (vastTagUrl !== void 0) cfg.vastTagUrl = vastTagUrl;
7832
+ if (isVmap !== void 0) cfg.isVmap = isVmap;
7700
7833
  if (vmapUrl !== void 0) cfg.vmapUrl = vmapUrl;
7701
7834
  if (minSegmentsBeforePlay !== void 0) cfg.minSegmentsBeforePlay = minSegmentsBeforePlay;
7702
7835
  var player = new StormcloudVideoPlayer(cfg);
@@ -8859,6 +8992,7 @@ var defaultProps = {
8859
8992
  showCustomControls: false,
8860
8993
  hideLoadingIndicator: false,
8861
8994
  licenseKey: "",
8995
+ isVmap: false,
8862
8996
  vmapUrl: "",
8863
8997
  adFailsafeTimeoutMs: 1e4,
8864
8998
  minSegmentsBeforePlay: 2,
@@ -9803,6 +9937,7 @@ var SUPPORTED_PROPS = [
9803
9937
  "debugAdTiming",
9804
9938
  "showCustomControls",
9805
9939
  "licenseKey",
9940
+ "isVmap",
9806
9941
  "vmapUrl",
9807
9942
  "adFailsafeTimeoutMs",
9808
9943
  "minSegmentsBeforePlay",
@@ -9992,31 +10127,46 @@ var StormcloudPlayer = createStormcloudPlayer(players_default, players_default[p
9992
10127
  var StormcloudPlayer_default = StormcloudPlayer;
9993
10128
  // Annotate the CommonJS export names for ESM import in node:
9994
10129
  0 && (module.exports = {
10130
+ DEFAULT_MQTT_CONFIG: DEFAULT_MQTT_CONFIG,
9995
10131
  IS_BROWSER: IS_BROWSER,
9996
10132
  IS_GLOBAL: IS_GLOBAL,
9997
10133
  IS_IOS: IS_IOS,
9998
10134
  IS_SAFARI: IS_SAFARI,
10135
+ MQTT_CA_CERT_FILE: MQTT_CA_CERT_FILE,
9999
10136
  SUPPORTS_DASH: SUPPORTS_DASH,
10000
10137
  SUPPORTS_HLS: SUPPORTS_HLS,
10001
10138
  StormcloudPlayer: StormcloudPlayer,
10002
10139
  StormcloudVideoPlayer: StormcloudVideoPlayer,
10003
10140
  StormcloudVideoPlayerComponent: StormcloudVideoPlayerComponent,
10141
+ applyMQTTConfig: applyMQTTConfig,
10142
+ buildMQTTBrokerUrl: buildMQTTBrokerUrl,
10143
+ buildPlayerTopic: buildPlayerTopic,
10004
10144
  canPlay: canPlay,
10145
+ configureMQTT: configureMQTT,
10005
10146
  createHlsAdPlayer: createHlsAdPlayer,
10006
10147
  createPalNonceManager: createPalNonceManager,
10007
10148
  createStormcloudPlayer: createStormcloudPlayer,
10008
10149
  detectBrowser: detectBrowser,
10150
+ disconnectMQTT: disconnectMQTT,
10151
+ ensureMQTTClient: ensureMQTTClient,
10009
10152
  getBrowserConfigOverrides: getBrowserConfigOverrides,
10010
10153
  getBrowserID: getBrowserID,
10011
10154
  getClientInfo: getClientInfo,
10155
+ getMQTTStatus: getMQTTStatus,
10156
+ initMQTTClient: initMQTTClient,
10012
10157
  initializePolyfills: initializePolyfills,
10158
+ isMQTTConfigured: isMQTTConfigured,
10159
+ isMQTTConnected: isMQTTConnected,
10160
+ isMQTTEnabled: isMQTTEnabled,
10013
10161
  isMediaStream: isMediaStream,
10014
10162
  lazy: lazy,
10015
10163
  logBrowserInfo: logBrowserInfo,
10016
10164
  merge: merge,
10165
+ mqttConfig: mqttConfig,
10017
10166
  omit: omit,
10018
10167
  parseQuery: parseQuery,
10019
10168
  players: players,
10169
+ publishMQTT: publishMQTT,
10020
10170
  randomString: randomString,
10021
10171
  sendAdDetectTracking: sendAdDetectTracking,
10022
10172
  sendAdImpressionTracking: sendAdImpressionTracking,