stormcloud-video-player 0.1.13 → 0.2.0

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
@@ -30,15 +30,35 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ IS_BROWSER: () => IS_BROWSER,
34
+ IS_GLOBAL: () => IS_GLOBAL,
35
+ IS_IOS: () => IS_IOS,
36
+ IS_SAFARI: () => IS_SAFARI,
37
+ SUPPORTS_DASH: () => SUPPORTS_DASH,
38
+ SUPPORTS_HLS: () => SUPPORTS_HLS,
33
39
  StormcloudVideoPlayer: () => StormcloudVideoPlayer,
34
40
  StormcloudVideoPlayerComponent: () => StormcloudVideoPlayerComponent,
41
+ canPlay: () => canPlay,
42
+ createStormcloudPlayer: () => createStormcloudPlayer,
43
+ default: () => StormcloudPlayer_default,
35
44
  getBrowserID: () => getBrowserID,
36
45
  getClientInfo: () => getClientInfo,
46
+ isMediaStream: () => isMediaStream,
47
+ lazy: () => lazy,
48
+ merge: () => merge,
49
+ omit: () => omit,
50
+ parseQuery: () => parseQuery,
51
+ players: () => players_default,
52
+ randomString: () => randomString,
37
53
  sendHeartbeat: () => sendHeartbeat,
38
- sendInitialTracking: () => sendInitialTracking
54
+ sendInitialTracking: () => sendInitialTracking,
55
+ supportsWebKitPresentationMode: () => supportsWebKitPresentationMode
39
56
  });
40
57
  module.exports = __toCommonJS(index_exports);
41
58
 
59
+ // src/StormcloudPlayer.tsx
60
+ var import_react5 = __toESM(require("react"), 1);
61
+
42
62
  // src/player/StormcloudVideoPlayer.ts
43
63
  var import_hls = __toESM(require("hls.js"), 1);
44
64
 
@@ -1133,7 +1153,6 @@ var StormcloudVideoPlayer = class {
1133
1153
  }
1134
1154
  parseScte35Binary(data) {
1135
1155
  class BitReader {
1136
- // 0..7
1137
1156
  constructor(buf) {
1138
1157
  this.buf = buf;
1139
1158
  this.bytePos = 0;
@@ -1671,8 +1690,896 @@ var StormcloudVideoPlayer = class {
1671
1690
  }
1672
1691
  };
1673
1692
 
1693
+ // src/props.ts
1694
+ var noop = () => {
1695
+ };
1696
+ var defaultProps = {
1697
+ playing: false,
1698
+ loop: false,
1699
+ controls: true,
1700
+ volume: 1,
1701
+ muted: false,
1702
+ playbackRate: 1,
1703
+ width: "100%",
1704
+ height: "auto",
1705
+ style: {},
1706
+ progressInterval: 1e3,
1707
+ playsInline: false,
1708
+ autoplay: false,
1709
+ preload: "metadata",
1710
+ poster: "",
1711
+ className: "",
1712
+ wrapperClassName: "",
1713
+ wrapperStyle: {},
1714
+ allowNativeHls: false,
1715
+ lowLatencyMode: false,
1716
+ driftToleranceMs: 1e3,
1717
+ immediateManifestAds: true,
1718
+ debugAdTiming: false,
1719
+ showCustomControls: false,
1720
+ licenseKey: "",
1721
+ adFailsafeTimeoutMs: 1e4,
1722
+ onStart: noop,
1723
+ onPlay: noop,
1724
+ onPause: noop,
1725
+ onBuffer: noop,
1726
+ onBufferEnd: noop,
1727
+ onEnded: noop,
1728
+ onError: noop,
1729
+ onDuration: noop,
1730
+ onSeek: noop,
1731
+ onProgress: noop,
1732
+ onVolumeToggle: noop,
1733
+ onFullscreenToggle: noop,
1734
+ onControlClick: noop
1735
+ };
1736
+
1737
+ // src/utils.ts
1738
+ var import_react = require("react");
1739
+ var lazy = import_react.lazy;
1740
+ var omit = (object, keys) => {
1741
+ const result = { ...object };
1742
+ keys.forEach((key) => {
1743
+ delete result[key];
1744
+ });
1745
+ return result;
1746
+ };
1747
+ var isMediaStream = (url) => {
1748
+ return typeof window !== "undefined" && window.MediaStream && url instanceof window.MediaStream;
1749
+ };
1750
+ var supportsWebKitPresentationMode = () => {
1751
+ if (typeof window === "undefined") return false;
1752
+ const video = document.createElement("video");
1753
+ return "webkitSupportsPresentationMode" in video;
1754
+ };
1755
+ var randomString = () => {
1756
+ return Math.random().toString(36).substr(2, 9);
1757
+ };
1758
+ var parseQuery = (url) => {
1759
+ const query = {};
1760
+ const params = new URLSearchParams(url.split("?")[1] || "");
1761
+ params.forEach((value, key) => {
1762
+ query[key] = value;
1763
+ });
1764
+ return query;
1765
+ };
1766
+ var merge = (target, ...sources) => {
1767
+ if (!sources.length) return target;
1768
+ const source = sources.shift();
1769
+ if (isObject(target) && isObject(source)) {
1770
+ for (const key in source) {
1771
+ if (isObject(source[key])) {
1772
+ if (!target[key]) Object.assign(target, { [key]: {} });
1773
+ merge(target[key], source[key]);
1774
+ } else {
1775
+ Object.assign(target, { [key]: source[key] });
1776
+ }
1777
+ }
1778
+ }
1779
+ return merge(target, ...sources);
1780
+ };
1781
+ var isObject = (item) => {
1782
+ return item && typeof item === "object" && !Array.isArray(item);
1783
+ };
1784
+ var IS_BROWSER = typeof window !== "undefined" && window.document;
1785
+ var IS_GLOBAL = typeof globalThis !== "undefined" && globalThis.window && globalThis.window.document;
1786
+ var IS_IOS = IS_BROWSER && /iPad|iPhone|iPod/.test(navigator.userAgent);
1787
+ var IS_SAFARI = IS_BROWSER && /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
1788
+ var SUPPORTS_HLS = () => {
1789
+ if (!IS_BROWSER) return false;
1790
+ const video = document.createElement("video");
1791
+ return Boolean(video.canPlayType("application/vnd.apple.mpegurl"));
1792
+ };
1793
+ var SUPPORTS_DASH = () => {
1794
+ if (!IS_BROWSER) return false;
1795
+ const video = document.createElement("video");
1796
+ return Boolean(video.canPlayType("application/dash+xml"));
1797
+ };
1798
+
1799
+ // src/patterns.ts
1800
+ var HLS_EXTENSIONS = /\.(m3u8)($|\?)/i;
1801
+ var HLS_PATHS = /\/hls\//i;
1802
+ var DASH_EXTENSIONS = /\.(mpd)($|\?)/i;
1803
+ var VIDEO_EXTENSIONS = /\.(mp4|webm|ogg|avi|mov|wmv|flv|mkv)($|\?)/i;
1804
+ var AUDIO_EXTENSIONS = /\.(mp3|wav|ogg|aac|wma|flac|m4a)($|\?)/i;
1805
+ var canPlay = {
1806
+ hls: (url) => {
1807
+ if (!url || typeof url !== "string") return false;
1808
+ return HLS_EXTENSIONS.test(url) || HLS_PATHS.test(url);
1809
+ },
1810
+ dash: (url) => {
1811
+ if (!url || typeof url !== "string") return false;
1812
+ return DASH_EXTENSIONS.test(url);
1813
+ },
1814
+ video: (url) => {
1815
+ if (!url || typeof url !== "string") return false;
1816
+ return VIDEO_EXTENSIONS.test(url);
1817
+ },
1818
+ audio: (url) => {
1819
+ if (!url || typeof url !== "string") return false;
1820
+ return AUDIO_EXTENSIONS.test(url);
1821
+ },
1822
+ file: (url) => {
1823
+ if (!url || typeof url !== "string") return false;
1824
+ return VIDEO_EXTENSIONS.test(url) || AUDIO_EXTENSIONS.test(url);
1825
+ }
1826
+ };
1827
+
1828
+ // src/players/HlsPlayer.tsx
1829
+ var import_react2 = require("react");
1830
+ var HlsPlayer = class extends import_react2.Component {
1831
+ constructor() {
1832
+ super(...arguments);
1833
+ this.player = null;
1834
+ this.mounted = false;
1835
+ this.load = async () => {
1836
+ if (!this.props.videoElement || !this.props.src) return;
1837
+ try {
1838
+ if (this.player) {
1839
+ this.player.destroy();
1840
+ this.player = null;
1841
+ }
1842
+ const config = {
1843
+ src: this.props.src,
1844
+ videoElement: this.props.videoElement
1845
+ };
1846
+ if (this.props.autoplay !== void 0)
1847
+ config.autoplay = this.props.autoplay;
1848
+ if (this.props.muted !== void 0) config.muted = this.props.muted;
1849
+ if (this.props.lowLatencyMode !== void 0)
1850
+ config.lowLatencyMode = this.props.lowLatencyMode;
1851
+ if (this.props.allowNativeHls !== void 0)
1852
+ config.allowNativeHls = this.props.allowNativeHls;
1853
+ if (this.props.driftToleranceMs !== void 0)
1854
+ config.driftToleranceMs = this.props.driftToleranceMs;
1855
+ if (this.props.immediateManifestAds !== void 0)
1856
+ config.immediateManifestAds = this.props.immediateManifestAds;
1857
+ if (this.props.debugAdTiming !== void 0)
1858
+ config.debugAdTiming = this.props.debugAdTiming;
1859
+ if (this.props.showCustomControls !== void 0)
1860
+ config.showCustomControls = this.props.showCustomControls;
1861
+ if (this.props.onVolumeToggle !== void 0)
1862
+ config.onVolumeToggle = this.props.onVolumeToggle;
1863
+ if (this.props.onFullscreenToggle !== void 0)
1864
+ config.onFullscreenToggle = this.props.onFullscreenToggle;
1865
+ if (this.props.onControlClick !== void 0)
1866
+ config.onControlClick = this.props.onControlClick;
1867
+ if (this.props.licenseKey !== void 0)
1868
+ config.licenseKey = this.props.licenseKey;
1869
+ if (this.props.adFailsafeTimeoutMs !== void 0)
1870
+ config.adFailsafeTimeoutMs = this.props.adFailsafeTimeoutMs;
1871
+ this.player = new StormcloudVideoPlayer(config);
1872
+ this.props.onMount?.(this);
1873
+ await this.player.load();
1874
+ if (this.mounted) {
1875
+ this.props.onReady?.();
1876
+ }
1877
+ } catch (error) {
1878
+ if (this.mounted) {
1879
+ this.props.onError?.(error);
1880
+ }
1881
+ }
1882
+ };
1883
+ this.play = () => {
1884
+ if (this.props.videoElement) {
1885
+ this.props.videoElement.play();
1886
+ this.props.onPlay?.();
1887
+ }
1888
+ };
1889
+ this.pause = () => {
1890
+ if (this.props.videoElement) {
1891
+ this.props.videoElement.pause();
1892
+ this.props.onPause?.();
1893
+ }
1894
+ };
1895
+ this.stop = () => {
1896
+ this.pause();
1897
+ if (this.props.videoElement) {
1898
+ this.props.videoElement.currentTime = 0;
1899
+ }
1900
+ };
1901
+ this.seekTo = (seconds, keepPlaying) => {
1902
+ if (this.props.videoElement) {
1903
+ this.props.videoElement.currentTime = seconds;
1904
+ if (!keepPlaying) {
1905
+ this.pause();
1906
+ }
1907
+ }
1908
+ };
1909
+ this.setVolume = (volume) => {
1910
+ if (this.props.videoElement) {
1911
+ this.props.videoElement.volume = Math.max(0, Math.min(1, volume));
1912
+ }
1913
+ };
1914
+ this.mute = () => {
1915
+ if (this.props.videoElement) {
1916
+ this.props.videoElement.muted = true;
1917
+ }
1918
+ };
1919
+ this.unmute = () => {
1920
+ if (this.props.videoElement) {
1921
+ this.props.videoElement.muted = false;
1922
+ }
1923
+ };
1924
+ this.setPlaybackRate = (rate) => {
1925
+ if (this.props.videoElement && rate > 0) {
1926
+ this.props.videoElement.playbackRate = rate;
1927
+ }
1928
+ };
1929
+ this.getDuration = () => {
1930
+ if (this.props.videoElement && isFinite(this.props.videoElement.duration)) {
1931
+ return this.props.videoElement.duration;
1932
+ }
1933
+ return null;
1934
+ };
1935
+ this.getCurrentTime = () => {
1936
+ if (this.props.videoElement && isFinite(this.props.videoElement.currentTime)) {
1937
+ return this.props.videoElement.currentTime;
1938
+ }
1939
+ return null;
1940
+ };
1941
+ this.getSecondsLoaded = () => {
1942
+ if (this.props.videoElement && this.props.videoElement.buffered.length > 0) {
1943
+ return this.props.videoElement.buffered.end(
1944
+ this.props.videoElement.buffered.length - 1
1945
+ );
1946
+ }
1947
+ return null;
1948
+ };
1949
+ this.getInternalPlayer = (key = "player") => {
1950
+ if (key === "player") return this.player;
1951
+ if (key === "video") return this.props.videoElement;
1952
+ if (key === "hls" && this.player) return this.player.hls;
1953
+ return null;
1954
+ };
1955
+ }
1956
+ componentDidMount() {
1957
+ this.mounted = true;
1958
+ this.load();
1959
+ }
1960
+ componentWillUnmount() {
1961
+ this.mounted = false;
1962
+ if (this.player) {
1963
+ this.player.destroy();
1964
+ this.player = null;
1965
+ }
1966
+ }
1967
+ componentDidUpdate(prevProps) {
1968
+ if (prevProps.src !== this.props.src) {
1969
+ this.load();
1970
+ }
1971
+ }
1972
+ render() {
1973
+ return null;
1974
+ }
1975
+ };
1976
+ HlsPlayer.displayName = "HlsPlayer";
1977
+ HlsPlayer.canPlay = canPlay.hls;
1978
+
1979
+ // src/players/FilePlayer.tsx
1980
+ var import_react3 = require("react");
1981
+ var FilePlayer = class extends import_react3.Component {
1982
+ constructor() {
1983
+ super(...arguments);
1984
+ this.mounted = false;
1985
+ this.ready = false;
1986
+ this.load = () => {
1987
+ if (!this.props.videoElement || !this.props.src) return;
1988
+ const video = this.props.videoElement;
1989
+ const handleLoadedMetadata = () => {
1990
+ if (this.mounted && !this.ready) {
1991
+ this.ready = true;
1992
+ this.props.onReady?.();
1993
+ }
1994
+ };
1995
+ const handlePlay = () => {
1996
+ if (this.mounted) {
1997
+ this.props.onPlay?.();
1998
+ }
1999
+ };
2000
+ const handlePause = () => {
2001
+ if (this.mounted) {
2002
+ this.props.onPause?.();
2003
+ }
2004
+ };
2005
+ const handleEnded = () => {
2006
+ if (this.mounted) {
2007
+ this.props.onEnded?.();
2008
+ }
2009
+ };
2010
+ const handleError = (error) => {
2011
+ if (this.mounted) {
2012
+ this.props.onError?.(error);
2013
+ }
2014
+ };
2015
+ const handleLoadedData = () => {
2016
+ if (this.mounted) {
2017
+ this.props.onLoaded?.();
2018
+ }
2019
+ };
2020
+ video.addEventListener("loadedmetadata", handleLoadedMetadata);
2021
+ video.addEventListener("play", handlePlay);
2022
+ video.addEventListener("pause", handlePause);
2023
+ video.addEventListener("ended", handleEnded);
2024
+ video.addEventListener("error", handleError);
2025
+ video.addEventListener("loadeddata", handleLoadedData);
2026
+ video.src = this.props.src;
2027
+ if (this.props.autoplay !== void 0) video.autoplay = this.props.autoplay;
2028
+ if (this.props.muted !== void 0) video.muted = this.props.muted;
2029
+ if (this.props.loop !== void 0) video.loop = this.props.loop;
2030
+ if (this.props.controls !== void 0) video.controls = this.props.controls;
2031
+ if (this.props.playsInline !== void 0)
2032
+ video.playsInline = this.props.playsInline;
2033
+ if (this.props.preload !== void 0)
2034
+ video.preload = this.props.preload;
2035
+ if (this.props.poster !== void 0) video.poster = this.props.poster;
2036
+ this.props.onMount?.(this);
2037
+ return () => {
2038
+ video.removeEventListener("loadedmetadata", handleLoadedMetadata);
2039
+ video.removeEventListener("play", handlePlay);
2040
+ video.removeEventListener("pause", handlePause);
2041
+ video.removeEventListener("ended", handleEnded);
2042
+ video.removeEventListener("error", handleError);
2043
+ video.removeEventListener("loadeddata", handleLoadedData);
2044
+ };
2045
+ };
2046
+ this.play = () => {
2047
+ if (this.props.videoElement) {
2048
+ this.props.videoElement.play();
2049
+ }
2050
+ };
2051
+ this.pause = () => {
2052
+ if (this.props.videoElement) {
2053
+ this.props.videoElement.pause();
2054
+ }
2055
+ };
2056
+ this.stop = () => {
2057
+ this.pause();
2058
+ if (this.props.videoElement) {
2059
+ this.props.videoElement.currentTime = 0;
2060
+ }
2061
+ };
2062
+ this.seekTo = (seconds, keepPlaying) => {
2063
+ if (this.props.videoElement) {
2064
+ this.props.videoElement.currentTime = seconds;
2065
+ if (!keepPlaying) {
2066
+ this.pause();
2067
+ }
2068
+ }
2069
+ };
2070
+ this.setVolume = (volume) => {
2071
+ if (this.props.videoElement) {
2072
+ this.props.videoElement.volume = Math.max(0, Math.min(1, volume));
2073
+ }
2074
+ };
2075
+ this.mute = () => {
2076
+ if (this.props.videoElement) {
2077
+ this.props.videoElement.muted = true;
2078
+ }
2079
+ };
2080
+ this.unmute = () => {
2081
+ if (this.props.videoElement) {
2082
+ this.props.videoElement.muted = false;
2083
+ }
2084
+ };
2085
+ this.setPlaybackRate = (rate) => {
2086
+ if (this.props.videoElement && rate > 0) {
2087
+ this.props.videoElement.playbackRate = rate;
2088
+ }
2089
+ };
2090
+ this.setLoop = (loop) => {
2091
+ if (this.props.videoElement) {
2092
+ this.props.videoElement.loop = loop;
2093
+ }
2094
+ };
2095
+ this.getDuration = () => {
2096
+ if (this.props.videoElement && isFinite(this.props.videoElement.duration)) {
2097
+ return this.props.videoElement.duration;
2098
+ }
2099
+ return null;
2100
+ };
2101
+ this.getCurrentTime = () => {
2102
+ if (this.props.videoElement && isFinite(this.props.videoElement.currentTime)) {
2103
+ return this.props.videoElement.currentTime;
2104
+ }
2105
+ return null;
2106
+ };
2107
+ this.getSecondsLoaded = () => {
2108
+ if (this.props.videoElement && this.props.videoElement.buffered.length > 0) {
2109
+ return this.props.videoElement.buffered.end(
2110
+ this.props.videoElement.buffered.length - 1
2111
+ );
2112
+ }
2113
+ return null;
2114
+ };
2115
+ this.getInternalPlayer = (key = "player") => {
2116
+ if (key === "video") return this.props.videoElement;
2117
+ return null;
2118
+ };
2119
+ this.enablePIP = async () => {
2120
+ if (this.props.videoElement && "requestPictureInPicture" in this.props.videoElement) {
2121
+ try {
2122
+ await this.props.videoElement.requestPictureInPicture();
2123
+ } catch (error) {
2124
+ console.warn("Picture-in-Picture failed:", error);
2125
+ }
2126
+ }
2127
+ };
2128
+ this.disablePIP = async () => {
2129
+ if (document.pictureInPictureElement) {
2130
+ try {
2131
+ await document.exitPictureInPicture();
2132
+ } catch (error) {
2133
+ console.warn("Exit Picture-in-Picture failed:", error);
2134
+ }
2135
+ }
2136
+ };
2137
+ }
2138
+ componentDidMount() {
2139
+ this.mounted = true;
2140
+ this.load();
2141
+ }
2142
+ componentWillUnmount() {
2143
+ this.mounted = false;
2144
+ }
2145
+ componentDidUpdate(prevProps) {
2146
+ if (prevProps.src !== this.props.src) {
2147
+ this.load();
2148
+ }
2149
+ }
2150
+ render() {
2151
+ return null;
2152
+ }
2153
+ };
2154
+ FilePlayer.displayName = "FilePlayer";
2155
+ FilePlayer.canPlay = canPlay.file;
2156
+
2157
+ // src/players/index.ts
2158
+ var players = [
2159
+ {
2160
+ key: "hls",
2161
+ name: "HLS Player",
2162
+ canPlay: canPlay.hls,
2163
+ lazyPlayer: lazy(() => Promise.resolve({ default: HlsPlayer }))
2164
+ },
2165
+ {
2166
+ key: "file",
2167
+ name: "File Player",
2168
+ canPlay: canPlay.file,
2169
+ canEnablePIP: (url) => {
2170
+ return canPlay.file(url) && (document.pictureInPictureEnabled || typeof document.webkitSupportsPresentationMode === "function");
2171
+ },
2172
+ lazyPlayer: lazy(() => Promise.resolve({ default: FilePlayer }))
2173
+ }
2174
+ ];
2175
+ var players_default = players;
2176
+
2177
+ // src/Player.tsx
2178
+ var import_react4 = __toESM(require("react"), 1);
2179
+ var SEEK_ON_PLAY_EXPIRY = 5e3;
2180
+ var Player = class extends import_react4.Component {
2181
+ constructor() {
2182
+ super(...arguments);
2183
+ this.mounted = false;
2184
+ this.isReady = false;
2185
+ this.isPlaying = false;
2186
+ this.isLoading = true;
2187
+ this.loadOnReady = null;
2188
+ this.startOnPlay = true;
2189
+ this.seekOnPlay = null;
2190
+ this.onDurationCalled = false;
2191
+ this.handlePlayerMount = (player) => {
2192
+ if (this.player) {
2193
+ this.progress();
2194
+ return;
2195
+ }
2196
+ this.player = player;
2197
+ this.player.load(this.props.src);
2198
+ this.progress();
2199
+ };
2200
+ this.getInternalPlayer = (key) => {
2201
+ if (!this.player) return null;
2202
+ return this.player.getInternalPlayer(key);
2203
+ };
2204
+ this.progress = () => {
2205
+ if (this.props.src && this.player && this.isReady) {
2206
+ const playedSeconds = this.getCurrentTime() || 0;
2207
+ const loadedSeconds = this.getSecondsLoaded();
2208
+ const duration = this.getDuration();
2209
+ if (duration) {
2210
+ const progress = {
2211
+ playedSeconds,
2212
+ played: playedSeconds / duration,
2213
+ loaded: 0,
2214
+ loadedSeconds: 0
2215
+ };
2216
+ if (loadedSeconds !== null) {
2217
+ progress.loadedSeconds = loadedSeconds;
2218
+ progress.loaded = loadedSeconds / duration;
2219
+ }
2220
+ if (progress.playedSeconds !== this.prevPlayed || progress.loadedSeconds !== this.prevLoaded) {
2221
+ this.props.onProgress?.(progress);
2222
+ }
2223
+ this.prevPlayed = progress.playedSeconds;
2224
+ this.prevLoaded = progress.loadedSeconds;
2225
+ }
2226
+ }
2227
+ this.progressTimeout = window.setTimeout(
2228
+ this.progress,
2229
+ this.props.progressInterval || 1e3
2230
+ );
2231
+ };
2232
+ this.handleReady = () => {
2233
+ if (!this.mounted) return;
2234
+ this.isReady = true;
2235
+ this.isLoading = false;
2236
+ const { onReady, playing, volume, muted } = this.props;
2237
+ onReady();
2238
+ if (!muted && volume !== null) {
2239
+ this.player.setVolume(volume);
2240
+ }
2241
+ if (this.loadOnReady) {
2242
+ this.player.load(this.loadOnReady, true);
2243
+ this.loadOnReady = null;
2244
+ } else if (playing) {
2245
+ this.player.play();
2246
+ }
2247
+ this.handleDurationCheck();
2248
+ };
2249
+ this.handlePlay = () => {
2250
+ this.isPlaying = true;
2251
+ this.isLoading = false;
2252
+ const { onStart, onPlay, playbackRate } = this.props;
2253
+ if (this.startOnPlay) {
2254
+ if (this.player.setPlaybackRate && playbackRate !== 1) {
2255
+ this.player.setPlaybackRate(playbackRate);
2256
+ }
2257
+ onStart?.();
2258
+ this.startOnPlay = false;
2259
+ }
2260
+ onPlay?.();
2261
+ if (this.seekOnPlay) {
2262
+ this.seekTo(this.seekOnPlay);
2263
+ this.seekOnPlay = null;
2264
+ }
2265
+ this.handleDurationCheck();
2266
+ };
2267
+ this.handlePause = (e) => {
2268
+ this.isPlaying = false;
2269
+ if (!this.isLoading) {
2270
+ this.props.onPause?.(e);
2271
+ }
2272
+ };
2273
+ this.handleEnded = () => {
2274
+ const { activePlayer, loop, onEnded } = this.props;
2275
+ if (activePlayer.loopOnEnded && loop) {
2276
+ this.seekTo(0);
2277
+ }
2278
+ if (!loop) {
2279
+ this.isPlaying = false;
2280
+ onEnded?.();
2281
+ }
2282
+ };
2283
+ this.handleError = (...args) => {
2284
+ this.isLoading = false;
2285
+ this.props.onError?.(args[0], args[1], args[2], args[3]);
2286
+ };
2287
+ this.handleDurationCheck = () => {
2288
+ clearTimeout(this.durationCheckTimeout);
2289
+ const duration = this.getDuration();
2290
+ if (duration) {
2291
+ if (!this.onDurationCalled) {
2292
+ this.props.onDuration?.(duration);
2293
+ this.onDurationCalled = true;
2294
+ }
2295
+ } else {
2296
+ this.durationCheckTimeout = window.setTimeout(
2297
+ this.handleDurationCheck,
2298
+ 100
2299
+ );
2300
+ }
2301
+ };
2302
+ this.handleLoaded = () => {
2303
+ this.isLoading = false;
2304
+ };
2305
+ }
2306
+ componentDidMount() {
2307
+ this.mounted = true;
2308
+ }
2309
+ componentWillUnmount() {
2310
+ clearTimeout(this.progressTimeout);
2311
+ clearTimeout(this.durationCheckTimeout);
2312
+ this.mounted = false;
2313
+ }
2314
+ componentDidUpdate(prevProps) {
2315
+ if (!this.player) return;
2316
+ const { src, playing, volume, muted, playbackRate, loop, activePlayer } = this.props;
2317
+ if (prevProps.src !== src) {
2318
+ if (this.isLoading && !activePlayer.forceLoad && !isMediaStream(src)) {
2319
+ console.warn(
2320
+ `StormcloudPlayer: the attempt to load ${src} is being deferred until the player has loaded`
2321
+ );
2322
+ this.loadOnReady = src || null;
2323
+ return;
2324
+ }
2325
+ this.isLoading = true;
2326
+ this.startOnPlay = true;
2327
+ this.onDurationCalled = false;
2328
+ this.player.load(src, this.isReady);
2329
+ }
2330
+ if (!prevProps.playing && playing && !this.isPlaying) {
2331
+ this.player.play();
2332
+ }
2333
+ if (prevProps.playing && !playing && this.isPlaying) {
2334
+ this.player.pause();
2335
+ }
2336
+ if (prevProps.volume !== volume && volume !== null) {
2337
+ this.player.setVolume(volume);
2338
+ }
2339
+ if (prevProps.muted !== muted) {
2340
+ if (muted) {
2341
+ this.player.mute();
2342
+ } else {
2343
+ this.player.unmute();
2344
+ if (volume !== null) {
2345
+ setTimeout(() => this.player.setVolume(volume));
2346
+ }
2347
+ }
2348
+ }
2349
+ if (prevProps.playbackRate !== playbackRate && this.player.setPlaybackRate) {
2350
+ this.player.setPlaybackRate(playbackRate);
2351
+ }
2352
+ if (prevProps.loop !== loop && this.player.setLoop) {
2353
+ this.player.setLoop(loop);
2354
+ }
2355
+ }
2356
+ getDuration() {
2357
+ if (!this.isReady) return null;
2358
+ return this.player.getDuration();
2359
+ }
2360
+ getCurrentTime() {
2361
+ if (!this.isReady) return null;
2362
+ return this.player.getCurrentTime();
2363
+ }
2364
+ getSecondsLoaded() {
2365
+ if (!this.isReady) return null;
2366
+ return this.player.getSecondsLoaded();
2367
+ }
2368
+ seekTo(amount, type, keepPlaying) {
2369
+ if (!this.isReady) {
2370
+ if (amount !== 0) {
2371
+ this.seekOnPlay = amount;
2372
+ setTimeout(() => {
2373
+ this.seekOnPlay = null;
2374
+ }, SEEK_ON_PLAY_EXPIRY);
2375
+ }
2376
+ return;
2377
+ }
2378
+ const isFraction = !type ? amount > 0 && amount < 1 : type === "fraction";
2379
+ if (isFraction) {
2380
+ const duration = this.player.getDuration();
2381
+ if (!duration) {
2382
+ console.warn(
2383
+ "StormcloudPlayer: could not seek using fraction \u2013 duration not yet available"
2384
+ );
2385
+ return;
2386
+ }
2387
+ this.player.seekTo(duration * amount, keepPlaying);
2388
+ return;
2389
+ }
2390
+ this.player.seekTo(amount, keepPlaying);
2391
+ }
2392
+ render() {
2393
+ const Player2 = this.props.activePlayer;
2394
+ if (!Player2) {
2395
+ return null;
2396
+ }
2397
+ return import_react4.default.createElement(Player2, {
2398
+ ...this.props,
2399
+ onMount: this.handlePlayerMount,
2400
+ onReady: this.handleReady,
2401
+ onPlay: this.handlePlay,
2402
+ onPause: this.handlePause,
2403
+ onEnded: this.handleEnded,
2404
+ onLoaded: this.handleLoaded,
2405
+ onError: this.handleError
2406
+ });
2407
+ }
2408
+ };
2409
+ Player.displayName = "Player";
2410
+ Player.defaultProps = defaultProps;
2411
+
2412
+ // src/StormcloudPlayer.tsx
2413
+ var IS_BROWSER2 = typeof window !== "undefined" && window.document;
2414
+ var IS_GLOBAL2 = typeof globalThis !== "undefined" && globalThis.window && globalThis.window.document;
2415
+ var UniversalSuspense = IS_BROWSER2 || IS_GLOBAL2 ? import_react5.Suspense : () => null;
2416
+ var SUPPORTED_PROPS = [
2417
+ "src",
2418
+ "playing",
2419
+ "loop",
2420
+ "controls",
2421
+ "volume",
2422
+ "muted",
2423
+ "playbackRate",
2424
+ "width",
2425
+ "height",
2426
+ "style",
2427
+ "progressInterval",
2428
+ "playsInline",
2429
+ "autoplay",
2430
+ "preload",
2431
+ "poster",
2432
+ "className",
2433
+ "wrapperClassName",
2434
+ "wrapperStyle",
2435
+ "allowNativeHls",
2436
+ "lowLatencyMode",
2437
+ "driftToleranceMs",
2438
+ "immediateManifestAds",
2439
+ "debugAdTiming",
2440
+ "showCustomControls",
2441
+ "licenseKey",
2442
+ "adFailsafeTimeoutMs",
2443
+ "onReady",
2444
+ "onStart",
2445
+ "onPlay",
2446
+ "onPause",
2447
+ "onBuffer",
2448
+ "onBufferEnd",
2449
+ "onEnded",
2450
+ "onError",
2451
+ "onDuration",
2452
+ "onSeek",
2453
+ "onProgress",
2454
+ "onVolumeToggle",
2455
+ "onFullscreenToggle",
2456
+ "onControlClick"
2457
+ ];
2458
+ var customPlayers = [];
2459
+ var createStormcloudPlayer = (playerList, fallback) => {
2460
+ var _a;
2461
+ return _a = class extends import_react5.Component {
2462
+ constructor() {
2463
+ super(...arguments);
2464
+ this.state = {
2465
+ showPreview: false
2466
+ };
2467
+ this.references = {
2468
+ wrapper: (wrapper) => {
2469
+ this.wrapper = wrapper;
2470
+ },
2471
+ player: (player) => {
2472
+ this.player = player;
2473
+ }
2474
+ };
2475
+ this.getActivePlayer = (src) => {
2476
+ if (!src) return null;
2477
+ for (const player of [...customPlayers, ...playerList]) {
2478
+ if (player.canPlay(src)) {
2479
+ return player;
2480
+ }
2481
+ }
2482
+ if (fallback) {
2483
+ return fallback;
2484
+ }
2485
+ return null;
2486
+ };
2487
+ this.getAttributes = (src) => {
2488
+ return omit(this.props, SUPPORTED_PROPS);
2489
+ };
2490
+ this.handleReady = () => {
2491
+ this.props.onReady?.(this);
2492
+ };
2493
+ this.seekTo = (fraction, type, keepPlaying) => {
2494
+ if (!this.player) return null;
2495
+ this.player.seekTo(fraction, type, keepPlaying);
2496
+ };
2497
+ this.getCurrentTime = () => {
2498
+ if (!this.player) return null;
2499
+ return this.player.getCurrentTime();
2500
+ };
2501
+ this.getSecondsLoaded = () => {
2502
+ if (!this.player) return null;
2503
+ return this.player.getSecondsLoaded();
2504
+ };
2505
+ this.getDuration = () => {
2506
+ if (!this.player) return null;
2507
+ return this.player.getDuration();
2508
+ };
2509
+ this.getInternalPlayer = (key = "player") => {
2510
+ if (!this.player) return null;
2511
+ return this.player.getInternalPlayer(key);
2512
+ };
2513
+ this.renderActivePlayer = (src) => {
2514
+ if (!src) return null;
2515
+ const activePlayer = this.getActivePlayer(src);
2516
+ if (!activePlayer) return null;
2517
+ return import_react5.default.createElement(Player, {
2518
+ ...this.props,
2519
+ key: activePlayer.key,
2520
+ ref: this.references.player,
2521
+ activePlayer: activePlayer.lazyPlayer || activePlayer,
2522
+ onReady: this.handleReady
2523
+ });
2524
+ };
2525
+ }
2526
+ render() {
2527
+ const {
2528
+ src,
2529
+ style,
2530
+ width,
2531
+ height,
2532
+ fallback: fallbackElement,
2533
+ wrapper: Wrapper
2534
+ } = this.props;
2535
+ const attributes = this.getAttributes(src);
2536
+ const wrapperRef = typeof Wrapper === "string" ? this.references.wrapper : void 0;
2537
+ return import_react5.default.createElement(
2538
+ Wrapper,
2539
+ {
2540
+ ref: wrapperRef,
2541
+ style: { ...style, width, height },
2542
+ ...attributes
2543
+ },
2544
+ import_react5.default.createElement(
2545
+ UniversalSuspense,
2546
+ { fallback: fallbackElement },
2547
+ this.renderActivePlayer(src)
2548
+ )
2549
+ );
2550
+ }
2551
+ }, _a.displayName = "StormcloudPlayer", _a.defaultProps = {
2552
+ ...defaultProps,
2553
+ fallback: null,
2554
+ wrapper: "div"
2555
+ }, _a.addCustomPlayer = (player) => {
2556
+ customPlayers.push(player);
2557
+ }, _a.removeCustomPlayers = () => {
2558
+ customPlayers.length = 0;
2559
+ }, _a.canPlay = (src) => {
2560
+ for (const Player2 of [...customPlayers, ...playerList]) {
2561
+ if (Player2.canPlay(src)) {
2562
+ return true;
2563
+ }
2564
+ }
2565
+ return false;
2566
+ }, _a.canEnablePIP = (src) => {
2567
+ for (const Player2 of [...customPlayers, ...playerList]) {
2568
+ if (Player2.canEnablePIP && Player2.canEnablePIP(src)) {
2569
+ return true;
2570
+ }
2571
+ }
2572
+ return false;
2573
+ }, _a;
2574
+ };
2575
+ var StormcloudPlayer = createStormcloudPlayer(
2576
+ players_default,
2577
+ players_default[players_default.length - 1]
2578
+ );
2579
+ var StormcloudPlayer_default = StormcloudPlayer;
2580
+
1674
2581
  // src/ui/StormcloudVideoPlayer.tsx
1675
- var import_react = __toESM(require("react"), 1);
2582
+ var import_react6 = __toESM(require("react"), 1);
1676
2583
  var import_fa = require("react-icons/fa");
1677
2584
  var import_jsx_runtime = require("react/jsx-runtime");
1678
2585
  var CRITICAL_PROPS = [
@@ -1682,7 +2589,7 @@ var CRITICAL_PROPS = [
1682
2589
  "lowLatencyMode",
1683
2590
  "driftToleranceMs"
1684
2591
  ];
1685
- var StormcloudVideoPlayerComponent = import_react.default.memo(
2592
+ var StormcloudVideoPlayerComponent = import_react6.default.memo(
1686
2593
  (props) => {
1687
2594
  const {
1688
2595
  src,
@@ -1710,22 +2617,22 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
1710
2617
  licenseKey,
1711
2618
  ...restVideoAttrs
1712
2619
  } = props;
1713
- const videoRef = (0, import_react.useRef)(null);
1714
- const playerRef = (0, import_react.useRef)(null);
1715
- const [adStatus, setAdStatus] = import_react.default.useState({ showAds: false, currentIndex: 0, totalAds: 0 });
1716
- const [shouldShowNativeControls, setShouldShowNativeControls] = import_react.default.useState(true);
1717
- const [isMuted, setIsMuted] = import_react.default.useState(false);
1718
- const [isFullscreen, setIsFullscreen] = import_react.default.useState(false);
1719
- const [isPlaying, setIsPlaying] = import_react.default.useState(false);
1720
- const [currentTime, setCurrentTime] = import_react.default.useState(0);
1721
- const [duration, setDuration] = import_react.default.useState(0);
1722
- const [volume, setVolume] = import_react.default.useState(1);
1723
- const [playbackRate, setPlaybackRate] = import_react.default.useState(1);
1724
- const [showVolumeSlider, setShowVolumeSlider] = import_react.default.useState(false);
1725
- const [showSpeedMenu, setShowSpeedMenu] = import_react.default.useState(false);
1726
- const [isLoading, setIsLoading] = import_react.default.useState(true);
1727
- const [isBuffering, setIsBuffering] = import_react.default.useState(false);
1728
- const [showCenterPlay, setShowCenterPlay] = import_react.default.useState(false);
2620
+ const videoRef = (0, import_react6.useRef)(null);
2621
+ const playerRef = (0, import_react6.useRef)(null);
2622
+ const [adStatus, setAdStatus] = import_react6.default.useState({ showAds: false, currentIndex: 0, totalAds: 0 });
2623
+ const [shouldShowNativeControls, setShouldShowNativeControls] = import_react6.default.useState(true);
2624
+ const [isMuted, setIsMuted] = import_react6.default.useState(false);
2625
+ const [isFullscreen, setIsFullscreen] = import_react6.default.useState(false);
2626
+ const [isPlaying, setIsPlaying] = import_react6.default.useState(false);
2627
+ const [currentTime, setCurrentTime] = import_react6.default.useState(0);
2628
+ const [duration, setDuration] = import_react6.default.useState(0);
2629
+ const [volume, setVolume] = import_react6.default.useState(1);
2630
+ const [playbackRate, setPlaybackRate] = import_react6.default.useState(1);
2631
+ const [showVolumeSlider, setShowVolumeSlider] = import_react6.default.useState(false);
2632
+ const [showSpeedMenu, setShowSpeedMenu] = import_react6.default.useState(false);
2633
+ const [isLoading, setIsLoading] = import_react6.default.useState(true);
2634
+ const [isBuffering, setIsBuffering] = import_react6.default.useState(false);
2635
+ const [showCenterPlay, setShowCenterPlay] = import_react6.default.useState(false);
1729
2636
  const formatTime = (seconds) => {
1730
2637
  if (!isFinite(seconds)) return "0:00:00";
1731
2638
  const hours = Math.floor(seconds / 3600);
@@ -1776,10 +2683,10 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
1776
2683
  };
1777
2684
  const isHlsStream = src?.toLowerCase().includes(".m3u8") || src?.toLowerCase().includes("/hls/");
1778
2685
  const shouldShowEnhancedControls = showCustomControls && (isHlsStream ? allowNativeHls : true);
1779
- const criticalPropsKey = (0, import_react.useMemo)(() => {
2686
+ const criticalPropsKey = (0, import_react6.useMemo)(() => {
1780
2687
  return CRITICAL_PROPS.map((prop) => `${prop}:${props[prop]}`).join("|");
1781
2688
  }, [src, allowNativeHls, licenseKey, lowLatencyMode, driftToleranceMs]);
1782
- (0, import_react.useEffect)(() => {
2689
+ (0, import_react6.useEffect)(() => {
1783
2690
  if (typeof window === "undefined") return;
1784
2691
  const el = videoRef.current;
1785
2692
  if (!el || !src) return;
@@ -1826,7 +2733,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
1826
2733
  playerRef.current = null;
1827
2734
  };
1828
2735
  }, [criticalPropsKey]);
1829
- (0, import_react.useEffect)(() => {
2736
+ (0, import_react6.useEffect)(() => {
1830
2737
  if (!playerRef.current) return;
1831
2738
  try {
1832
2739
  if (autoplay !== void 0 && playerRef.current.videoElement) {
@@ -1839,7 +2746,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
1839
2746
  console.warn("Failed to update player properties:", error);
1840
2747
  }
1841
2748
  }, [autoplay, muted]);
1842
- (0, import_react.useEffect)(() => {
2749
+ (0, import_react6.useEffect)(() => {
1843
2750
  if (!playerRef.current) return;
1844
2751
  const checkAdStatus = () => {
1845
2752
  if (playerRef.current) {
@@ -1857,7 +2764,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
1857
2764
  const interval = setInterval(checkAdStatus, 100);
1858
2765
  return () => clearInterval(interval);
1859
2766
  }, []);
1860
- (0, import_react.useEffect)(() => {
2767
+ (0, import_react6.useEffect)(() => {
1861
2768
  if (typeof window === "undefined" || !playerRef.current) return;
1862
2769
  const handleResize = () => {
1863
2770
  if (playerRef.current && videoRef.current) {
@@ -1869,7 +2776,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
1869
2776
  window.addEventListener("resize", handleResize);
1870
2777
  return () => window.removeEventListener("resize", handleResize);
1871
2778
  }, []);
1872
- (0, import_react.useEffect)(() => {
2779
+ (0, import_react6.useEffect)(() => {
1873
2780
  if (!playerRef.current || !videoRef.current) return;
1874
2781
  const updateStates = () => {
1875
2782
  if (playerRef.current && videoRef.current) {
@@ -1907,7 +2814,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
1907
2814
  );
1908
2815
  };
1909
2816
  }, []);
1910
- (0, import_react.useEffect)(() => {
2817
+ (0, import_react6.useEffect)(() => {
1911
2818
  if (!videoRef.current) return;
1912
2819
  const handleLoadedMetadata = () => {
1913
2820
  if (videoRef.current) {
@@ -2906,11 +3813,27 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2906
3813
  );
2907
3814
  // Annotate the CommonJS export names for ESM import in node:
2908
3815
  0 && (module.exports = {
3816
+ IS_BROWSER,
3817
+ IS_GLOBAL,
3818
+ IS_IOS,
3819
+ IS_SAFARI,
3820
+ SUPPORTS_DASH,
3821
+ SUPPORTS_HLS,
2909
3822
  StormcloudVideoPlayer,
2910
3823
  StormcloudVideoPlayerComponent,
3824
+ canPlay,
3825
+ createStormcloudPlayer,
2911
3826
  getBrowserID,
2912
3827
  getClientInfo,
3828
+ isMediaStream,
3829
+ lazy,
3830
+ merge,
3831
+ omit,
3832
+ parseQuery,
3833
+ players,
3834
+ randomString,
2913
3835
  sendHeartbeat,
2914
- sendInitialTracking
3836
+ sendInitialTracking,
3837
+ supportsWebKitPresentationMode
2915
3838
  });
2916
3839
  //# sourceMappingURL=index.cjs.map