stormcloud-video-player 0.1.12 → 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) {
@@ -1854,10 +2761,10 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
1854
2761
  });
1855
2762
  }
1856
2763
  };
1857
- const interval = setInterval(checkAdStatus, 500);
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) {
@@ -1932,7 +2839,11 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
1932
2839
  setShowCenterPlay(false);
1933
2840
  };
1934
2841
  const handlePause = () => {
1935
- setShowCenterPlay(true);
2842
+ if (playerRef.current && !playerRef.current.isShowingAds()) {
2843
+ setShowCenterPlay(true);
2844
+ } else {
2845
+ setShowCenterPlay(false);
2846
+ }
1936
2847
  };
1937
2848
  const handleEnded = () => {
1938
2849
  setShowCenterPlay(true);
@@ -2097,7 +3008,7 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2097
3008
  )
2098
3009
  }
2099
3010
  ),
2100
- showCenterPlay && !isLoading && !isBuffering && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3011
+ showCenterPlay && !isLoading && !isBuffering && !adStatus.showAds && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2101
3012
  "div",
2102
3013
  {
2103
3014
  onClick: handleCenterPlayClick,
@@ -2148,29 +3059,6 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2148
3059
  )
2149
3060
  }
2150
3061
  ),
2151
- adStatus.showAds && adStatus.totalAds > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2152
- "div",
2153
- {
2154
- style: {
2155
- position: "absolute",
2156
- top: "10px",
2157
- right: "10px",
2158
- backgroundColor: "rgba(0, 0, 0, 0.7)",
2159
- color: "white",
2160
- padding: "4px 8px",
2161
- borderRadius: "4px",
2162
- fontSize: "12px",
2163
- fontFamily: "Arial, sans-serif",
2164
- zIndex: 10
2165
- },
2166
- children: [
2167
- "Ad ",
2168
- adStatus.currentIndex,
2169
- "/",
2170
- adStatus.totalAds
2171
- ]
2172
- }
2173
- ),
2174
3062
  shouldShowEnhancedControls ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2175
3063
  "div",
2176
3064
  {
@@ -2925,11 +3813,27 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2925
3813
  );
2926
3814
  // Annotate the CommonJS export names for ESM import in node:
2927
3815
  0 && (module.exports = {
3816
+ IS_BROWSER,
3817
+ IS_GLOBAL,
3818
+ IS_IOS,
3819
+ IS_SAFARI,
3820
+ SUPPORTS_DASH,
3821
+ SUPPORTS_HLS,
2928
3822
  StormcloudVideoPlayer,
2929
3823
  StormcloudVideoPlayerComponent,
3824
+ canPlay,
3825
+ createStormcloudPlayer,
2930
3826
  getBrowserID,
2931
3827
  getClientInfo,
3828
+ isMediaStream,
3829
+ lazy,
3830
+ merge,
3831
+ omit,
3832
+ parseQuery,
3833
+ players,
3834
+ randomString,
2932
3835
  sendHeartbeat,
2933
- sendInitialTracking
3836
+ sendInitialTracking,
3837
+ supportsWebKitPresentationMode
2934
3838
  });
2935
3839
  //# sourceMappingURL=index.cjs.map