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.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/StormcloudPlayer.tsx
2
+ import React2, { Component as Component4, Suspense } from "react";
3
+
1
4
  // src/player/StormcloudVideoPlayer.ts
2
5
  import Hls from "hls.js";
3
6
 
@@ -1092,7 +1095,6 @@ var StormcloudVideoPlayer = class {
1092
1095
  }
1093
1096
  parseScte35Binary(data) {
1094
1097
  class BitReader {
1095
- // 0..7
1096
1098
  constructor(buf) {
1097
1099
  this.buf = buf;
1098
1100
  this.bytePos = 0;
@@ -1630,8 +1632,896 @@ var StormcloudVideoPlayer = class {
1630
1632
  }
1631
1633
  };
1632
1634
 
1635
+ // src/props.ts
1636
+ var noop = () => {
1637
+ };
1638
+ var defaultProps = {
1639
+ playing: false,
1640
+ loop: false,
1641
+ controls: true,
1642
+ volume: 1,
1643
+ muted: false,
1644
+ playbackRate: 1,
1645
+ width: "100%",
1646
+ height: "auto",
1647
+ style: {},
1648
+ progressInterval: 1e3,
1649
+ playsInline: false,
1650
+ autoplay: false,
1651
+ preload: "metadata",
1652
+ poster: "",
1653
+ className: "",
1654
+ wrapperClassName: "",
1655
+ wrapperStyle: {},
1656
+ allowNativeHls: false,
1657
+ lowLatencyMode: false,
1658
+ driftToleranceMs: 1e3,
1659
+ immediateManifestAds: true,
1660
+ debugAdTiming: false,
1661
+ showCustomControls: false,
1662
+ licenseKey: "",
1663
+ adFailsafeTimeoutMs: 1e4,
1664
+ onStart: noop,
1665
+ onPlay: noop,
1666
+ onPause: noop,
1667
+ onBuffer: noop,
1668
+ onBufferEnd: noop,
1669
+ onEnded: noop,
1670
+ onError: noop,
1671
+ onDuration: noop,
1672
+ onSeek: noop,
1673
+ onProgress: noop,
1674
+ onVolumeToggle: noop,
1675
+ onFullscreenToggle: noop,
1676
+ onControlClick: noop
1677
+ };
1678
+
1679
+ // src/utils.ts
1680
+ import { lazy as reactLazy } from "react";
1681
+ var lazy = reactLazy;
1682
+ var omit = (object, keys) => {
1683
+ const result = { ...object };
1684
+ keys.forEach((key) => {
1685
+ delete result[key];
1686
+ });
1687
+ return result;
1688
+ };
1689
+ var isMediaStream = (url) => {
1690
+ return typeof window !== "undefined" && window.MediaStream && url instanceof window.MediaStream;
1691
+ };
1692
+ var supportsWebKitPresentationMode = () => {
1693
+ if (typeof window === "undefined") return false;
1694
+ const video = document.createElement("video");
1695
+ return "webkitSupportsPresentationMode" in video;
1696
+ };
1697
+ var randomString = () => {
1698
+ return Math.random().toString(36).substr(2, 9);
1699
+ };
1700
+ var parseQuery = (url) => {
1701
+ const query = {};
1702
+ const params = new URLSearchParams(url.split("?")[1] || "");
1703
+ params.forEach((value, key) => {
1704
+ query[key] = value;
1705
+ });
1706
+ return query;
1707
+ };
1708
+ var merge = (target, ...sources) => {
1709
+ if (!sources.length) return target;
1710
+ const source = sources.shift();
1711
+ if (isObject(target) && isObject(source)) {
1712
+ for (const key in source) {
1713
+ if (isObject(source[key])) {
1714
+ if (!target[key]) Object.assign(target, { [key]: {} });
1715
+ merge(target[key], source[key]);
1716
+ } else {
1717
+ Object.assign(target, { [key]: source[key] });
1718
+ }
1719
+ }
1720
+ }
1721
+ return merge(target, ...sources);
1722
+ };
1723
+ var isObject = (item) => {
1724
+ return item && typeof item === "object" && !Array.isArray(item);
1725
+ };
1726
+ var IS_BROWSER = typeof window !== "undefined" && window.document;
1727
+ var IS_GLOBAL = typeof globalThis !== "undefined" && globalThis.window && globalThis.window.document;
1728
+ var IS_IOS = IS_BROWSER && /iPad|iPhone|iPod/.test(navigator.userAgent);
1729
+ var IS_SAFARI = IS_BROWSER && /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
1730
+ var SUPPORTS_HLS = () => {
1731
+ if (!IS_BROWSER) return false;
1732
+ const video = document.createElement("video");
1733
+ return Boolean(video.canPlayType("application/vnd.apple.mpegurl"));
1734
+ };
1735
+ var SUPPORTS_DASH = () => {
1736
+ if (!IS_BROWSER) return false;
1737
+ const video = document.createElement("video");
1738
+ return Boolean(video.canPlayType("application/dash+xml"));
1739
+ };
1740
+
1741
+ // src/patterns.ts
1742
+ var HLS_EXTENSIONS = /\.(m3u8)($|\?)/i;
1743
+ var HLS_PATHS = /\/hls\//i;
1744
+ var DASH_EXTENSIONS = /\.(mpd)($|\?)/i;
1745
+ var VIDEO_EXTENSIONS = /\.(mp4|webm|ogg|avi|mov|wmv|flv|mkv)($|\?)/i;
1746
+ var AUDIO_EXTENSIONS = /\.(mp3|wav|ogg|aac|wma|flac|m4a)($|\?)/i;
1747
+ var canPlay = {
1748
+ hls: (url) => {
1749
+ if (!url || typeof url !== "string") return false;
1750
+ return HLS_EXTENSIONS.test(url) || HLS_PATHS.test(url);
1751
+ },
1752
+ dash: (url) => {
1753
+ if (!url || typeof url !== "string") return false;
1754
+ return DASH_EXTENSIONS.test(url);
1755
+ },
1756
+ video: (url) => {
1757
+ if (!url || typeof url !== "string") return false;
1758
+ return VIDEO_EXTENSIONS.test(url);
1759
+ },
1760
+ audio: (url) => {
1761
+ if (!url || typeof url !== "string") return false;
1762
+ return AUDIO_EXTENSIONS.test(url);
1763
+ },
1764
+ file: (url) => {
1765
+ if (!url || typeof url !== "string") return false;
1766
+ return VIDEO_EXTENSIONS.test(url) || AUDIO_EXTENSIONS.test(url);
1767
+ }
1768
+ };
1769
+
1770
+ // src/players/HlsPlayer.tsx
1771
+ import { Component } from "react";
1772
+ var HlsPlayer = class extends Component {
1773
+ constructor() {
1774
+ super(...arguments);
1775
+ this.player = null;
1776
+ this.mounted = false;
1777
+ this.load = async () => {
1778
+ if (!this.props.videoElement || !this.props.src) return;
1779
+ try {
1780
+ if (this.player) {
1781
+ this.player.destroy();
1782
+ this.player = null;
1783
+ }
1784
+ const config = {
1785
+ src: this.props.src,
1786
+ videoElement: this.props.videoElement
1787
+ };
1788
+ if (this.props.autoplay !== void 0)
1789
+ config.autoplay = this.props.autoplay;
1790
+ if (this.props.muted !== void 0) config.muted = this.props.muted;
1791
+ if (this.props.lowLatencyMode !== void 0)
1792
+ config.lowLatencyMode = this.props.lowLatencyMode;
1793
+ if (this.props.allowNativeHls !== void 0)
1794
+ config.allowNativeHls = this.props.allowNativeHls;
1795
+ if (this.props.driftToleranceMs !== void 0)
1796
+ config.driftToleranceMs = this.props.driftToleranceMs;
1797
+ if (this.props.immediateManifestAds !== void 0)
1798
+ config.immediateManifestAds = this.props.immediateManifestAds;
1799
+ if (this.props.debugAdTiming !== void 0)
1800
+ config.debugAdTiming = this.props.debugAdTiming;
1801
+ if (this.props.showCustomControls !== void 0)
1802
+ config.showCustomControls = this.props.showCustomControls;
1803
+ if (this.props.onVolumeToggle !== void 0)
1804
+ config.onVolumeToggle = this.props.onVolumeToggle;
1805
+ if (this.props.onFullscreenToggle !== void 0)
1806
+ config.onFullscreenToggle = this.props.onFullscreenToggle;
1807
+ if (this.props.onControlClick !== void 0)
1808
+ config.onControlClick = this.props.onControlClick;
1809
+ if (this.props.licenseKey !== void 0)
1810
+ config.licenseKey = this.props.licenseKey;
1811
+ if (this.props.adFailsafeTimeoutMs !== void 0)
1812
+ config.adFailsafeTimeoutMs = this.props.adFailsafeTimeoutMs;
1813
+ this.player = new StormcloudVideoPlayer(config);
1814
+ this.props.onMount?.(this);
1815
+ await this.player.load();
1816
+ if (this.mounted) {
1817
+ this.props.onReady?.();
1818
+ }
1819
+ } catch (error) {
1820
+ if (this.mounted) {
1821
+ this.props.onError?.(error);
1822
+ }
1823
+ }
1824
+ };
1825
+ this.play = () => {
1826
+ if (this.props.videoElement) {
1827
+ this.props.videoElement.play();
1828
+ this.props.onPlay?.();
1829
+ }
1830
+ };
1831
+ this.pause = () => {
1832
+ if (this.props.videoElement) {
1833
+ this.props.videoElement.pause();
1834
+ this.props.onPause?.();
1835
+ }
1836
+ };
1837
+ this.stop = () => {
1838
+ this.pause();
1839
+ if (this.props.videoElement) {
1840
+ this.props.videoElement.currentTime = 0;
1841
+ }
1842
+ };
1843
+ this.seekTo = (seconds, keepPlaying) => {
1844
+ if (this.props.videoElement) {
1845
+ this.props.videoElement.currentTime = seconds;
1846
+ if (!keepPlaying) {
1847
+ this.pause();
1848
+ }
1849
+ }
1850
+ };
1851
+ this.setVolume = (volume) => {
1852
+ if (this.props.videoElement) {
1853
+ this.props.videoElement.volume = Math.max(0, Math.min(1, volume));
1854
+ }
1855
+ };
1856
+ this.mute = () => {
1857
+ if (this.props.videoElement) {
1858
+ this.props.videoElement.muted = true;
1859
+ }
1860
+ };
1861
+ this.unmute = () => {
1862
+ if (this.props.videoElement) {
1863
+ this.props.videoElement.muted = false;
1864
+ }
1865
+ };
1866
+ this.setPlaybackRate = (rate) => {
1867
+ if (this.props.videoElement && rate > 0) {
1868
+ this.props.videoElement.playbackRate = rate;
1869
+ }
1870
+ };
1871
+ this.getDuration = () => {
1872
+ if (this.props.videoElement && isFinite(this.props.videoElement.duration)) {
1873
+ return this.props.videoElement.duration;
1874
+ }
1875
+ return null;
1876
+ };
1877
+ this.getCurrentTime = () => {
1878
+ if (this.props.videoElement && isFinite(this.props.videoElement.currentTime)) {
1879
+ return this.props.videoElement.currentTime;
1880
+ }
1881
+ return null;
1882
+ };
1883
+ this.getSecondsLoaded = () => {
1884
+ if (this.props.videoElement && this.props.videoElement.buffered.length > 0) {
1885
+ return this.props.videoElement.buffered.end(
1886
+ this.props.videoElement.buffered.length - 1
1887
+ );
1888
+ }
1889
+ return null;
1890
+ };
1891
+ this.getInternalPlayer = (key = "player") => {
1892
+ if (key === "player") return this.player;
1893
+ if (key === "video") return this.props.videoElement;
1894
+ if (key === "hls" && this.player) return this.player.hls;
1895
+ return null;
1896
+ };
1897
+ }
1898
+ componentDidMount() {
1899
+ this.mounted = true;
1900
+ this.load();
1901
+ }
1902
+ componentWillUnmount() {
1903
+ this.mounted = false;
1904
+ if (this.player) {
1905
+ this.player.destroy();
1906
+ this.player = null;
1907
+ }
1908
+ }
1909
+ componentDidUpdate(prevProps) {
1910
+ if (prevProps.src !== this.props.src) {
1911
+ this.load();
1912
+ }
1913
+ }
1914
+ render() {
1915
+ return null;
1916
+ }
1917
+ };
1918
+ HlsPlayer.displayName = "HlsPlayer";
1919
+ HlsPlayer.canPlay = canPlay.hls;
1920
+
1921
+ // src/players/FilePlayer.tsx
1922
+ import { Component as Component2 } from "react";
1923
+ var FilePlayer = class extends Component2 {
1924
+ constructor() {
1925
+ super(...arguments);
1926
+ this.mounted = false;
1927
+ this.ready = false;
1928
+ this.load = () => {
1929
+ if (!this.props.videoElement || !this.props.src) return;
1930
+ const video = this.props.videoElement;
1931
+ const handleLoadedMetadata = () => {
1932
+ if (this.mounted && !this.ready) {
1933
+ this.ready = true;
1934
+ this.props.onReady?.();
1935
+ }
1936
+ };
1937
+ const handlePlay = () => {
1938
+ if (this.mounted) {
1939
+ this.props.onPlay?.();
1940
+ }
1941
+ };
1942
+ const handlePause = () => {
1943
+ if (this.mounted) {
1944
+ this.props.onPause?.();
1945
+ }
1946
+ };
1947
+ const handleEnded = () => {
1948
+ if (this.mounted) {
1949
+ this.props.onEnded?.();
1950
+ }
1951
+ };
1952
+ const handleError = (error) => {
1953
+ if (this.mounted) {
1954
+ this.props.onError?.(error);
1955
+ }
1956
+ };
1957
+ const handleLoadedData = () => {
1958
+ if (this.mounted) {
1959
+ this.props.onLoaded?.();
1960
+ }
1961
+ };
1962
+ video.addEventListener("loadedmetadata", handleLoadedMetadata);
1963
+ video.addEventListener("play", handlePlay);
1964
+ video.addEventListener("pause", handlePause);
1965
+ video.addEventListener("ended", handleEnded);
1966
+ video.addEventListener("error", handleError);
1967
+ video.addEventListener("loadeddata", handleLoadedData);
1968
+ video.src = this.props.src;
1969
+ if (this.props.autoplay !== void 0) video.autoplay = this.props.autoplay;
1970
+ if (this.props.muted !== void 0) video.muted = this.props.muted;
1971
+ if (this.props.loop !== void 0) video.loop = this.props.loop;
1972
+ if (this.props.controls !== void 0) video.controls = this.props.controls;
1973
+ if (this.props.playsInline !== void 0)
1974
+ video.playsInline = this.props.playsInline;
1975
+ if (this.props.preload !== void 0)
1976
+ video.preload = this.props.preload;
1977
+ if (this.props.poster !== void 0) video.poster = this.props.poster;
1978
+ this.props.onMount?.(this);
1979
+ return () => {
1980
+ video.removeEventListener("loadedmetadata", handleLoadedMetadata);
1981
+ video.removeEventListener("play", handlePlay);
1982
+ video.removeEventListener("pause", handlePause);
1983
+ video.removeEventListener("ended", handleEnded);
1984
+ video.removeEventListener("error", handleError);
1985
+ video.removeEventListener("loadeddata", handleLoadedData);
1986
+ };
1987
+ };
1988
+ this.play = () => {
1989
+ if (this.props.videoElement) {
1990
+ this.props.videoElement.play();
1991
+ }
1992
+ };
1993
+ this.pause = () => {
1994
+ if (this.props.videoElement) {
1995
+ this.props.videoElement.pause();
1996
+ }
1997
+ };
1998
+ this.stop = () => {
1999
+ this.pause();
2000
+ if (this.props.videoElement) {
2001
+ this.props.videoElement.currentTime = 0;
2002
+ }
2003
+ };
2004
+ this.seekTo = (seconds, keepPlaying) => {
2005
+ if (this.props.videoElement) {
2006
+ this.props.videoElement.currentTime = seconds;
2007
+ if (!keepPlaying) {
2008
+ this.pause();
2009
+ }
2010
+ }
2011
+ };
2012
+ this.setVolume = (volume) => {
2013
+ if (this.props.videoElement) {
2014
+ this.props.videoElement.volume = Math.max(0, Math.min(1, volume));
2015
+ }
2016
+ };
2017
+ this.mute = () => {
2018
+ if (this.props.videoElement) {
2019
+ this.props.videoElement.muted = true;
2020
+ }
2021
+ };
2022
+ this.unmute = () => {
2023
+ if (this.props.videoElement) {
2024
+ this.props.videoElement.muted = false;
2025
+ }
2026
+ };
2027
+ this.setPlaybackRate = (rate) => {
2028
+ if (this.props.videoElement && rate > 0) {
2029
+ this.props.videoElement.playbackRate = rate;
2030
+ }
2031
+ };
2032
+ this.setLoop = (loop) => {
2033
+ if (this.props.videoElement) {
2034
+ this.props.videoElement.loop = loop;
2035
+ }
2036
+ };
2037
+ this.getDuration = () => {
2038
+ if (this.props.videoElement && isFinite(this.props.videoElement.duration)) {
2039
+ return this.props.videoElement.duration;
2040
+ }
2041
+ return null;
2042
+ };
2043
+ this.getCurrentTime = () => {
2044
+ if (this.props.videoElement && isFinite(this.props.videoElement.currentTime)) {
2045
+ return this.props.videoElement.currentTime;
2046
+ }
2047
+ return null;
2048
+ };
2049
+ this.getSecondsLoaded = () => {
2050
+ if (this.props.videoElement && this.props.videoElement.buffered.length > 0) {
2051
+ return this.props.videoElement.buffered.end(
2052
+ this.props.videoElement.buffered.length - 1
2053
+ );
2054
+ }
2055
+ return null;
2056
+ };
2057
+ this.getInternalPlayer = (key = "player") => {
2058
+ if (key === "video") return this.props.videoElement;
2059
+ return null;
2060
+ };
2061
+ this.enablePIP = async () => {
2062
+ if (this.props.videoElement && "requestPictureInPicture" in this.props.videoElement) {
2063
+ try {
2064
+ await this.props.videoElement.requestPictureInPicture();
2065
+ } catch (error) {
2066
+ console.warn("Picture-in-Picture failed:", error);
2067
+ }
2068
+ }
2069
+ };
2070
+ this.disablePIP = async () => {
2071
+ if (document.pictureInPictureElement) {
2072
+ try {
2073
+ await document.exitPictureInPicture();
2074
+ } catch (error) {
2075
+ console.warn("Exit Picture-in-Picture failed:", error);
2076
+ }
2077
+ }
2078
+ };
2079
+ }
2080
+ componentDidMount() {
2081
+ this.mounted = true;
2082
+ this.load();
2083
+ }
2084
+ componentWillUnmount() {
2085
+ this.mounted = false;
2086
+ }
2087
+ componentDidUpdate(prevProps) {
2088
+ if (prevProps.src !== this.props.src) {
2089
+ this.load();
2090
+ }
2091
+ }
2092
+ render() {
2093
+ return null;
2094
+ }
2095
+ };
2096
+ FilePlayer.displayName = "FilePlayer";
2097
+ FilePlayer.canPlay = canPlay.file;
2098
+
2099
+ // src/players/index.ts
2100
+ var players = [
2101
+ {
2102
+ key: "hls",
2103
+ name: "HLS Player",
2104
+ canPlay: canPlay.hls,
2105
+ lazyPlayer: lazy(() => Promise.resolve({ default: HlsPlayer }))
2106
+ },
2107
+ {
2108
+ key: "file",
2109
+ name: "File Player",
2110
+ canPlay: canPlay.file,
2111
+ canEnablePIP: (url) => {
2112
+ return canPlay.file(url) && (document.pictureInPictureEnabled || typeof document.webkitSupportsPresentationMode === "function");
2113
+ },
2114
+ lazyPlayer: lazy(() => Promise.resolve({ default: FilePlayer }))
2115
+ }
2116
+ ];
2117
+ var players_default = players;
2118
+
2119
+ // src/Player.tsx
2120
+ import React, { Component as Component3 } from "react";
2121
+ var SEEK_ON_PLAY_EXPIRY = 5e3;
2122
+ var Player = class extends Component3 {
2123
+ constructor() {
2124
+ super(...arguments);
2125
+ this.mounted = false;
2126
+ this.isReady = false;
2127
+ this.isPlaying = false;
2128
+ this.isLoading = true;
2129
+ this.loadOnReady = null;
2130
+ this.startOnPlay = true;
2131
+ this.seekOnPlay = null;
2132
+ this.onDurationCalled = false;
2133
+ this.handlePlayerMount = (player) => {
2134
+ if (this.player) {
2135
+ this.progress();
2136
+ return;
2137
+ }
2138
+ this.player = player;
2139
+ this.player.load(this.props.src);
2140
+ this.progress();
2141
+ };
2142
+ this.getInternalPlayer = (key) => {
2143
+ if (!this.player) return null;
2144
+ return this.player.getInternalPlayer(key);
2145
+ };
2146
+ this.progress = () => {
2147
+ if (this.props.src && this.player && this.isReady) {
2148
+ const playedSeconds = this.getCurrentTime() || 0;
2149
+ const loadedSeconds = this.getSecondsLoaded();
2150
+ const duration = this.getDuration();
2151
+ if (duration) {
2152
+ const progress = {
2153
+ playedSeconds,
2154
+ played: playedSeconds / duration,
2155
+ loaded: 0,
2156
+ loadedSeconds: 0
2157
+ };
2158
+ if (loadedSeconds !== null) {
2159
+ progress.loadedSeconds = loadedSeconds;
2160
+ progress.loaded = loadedSeconds / duration;
2161
+ }
2162
+ if (progress.playedSeconds !== this.prevPlayed || progress.loadedSeconds !== this.prevLoaded) {
2163
+ this.props.onProgress?.(progress);
2164
+ }
2165
+ this.prevPlayed = progress.playedSeconds;
2166
+ this.prevLoaded = progress.loadedSeconds;
2167
+ }
2168
+ }
2169
+ this.progressTimeout = window.setTimeout(
2170
+ this.progress,
2171
+ this.props.progressInterval || 1e3
2172
+ );
2173
+ };
2174
+ this.handleReady = () => {
2175
+ if (!this.mounted) return;
2176
+ this.isReady = true;
2177
+ this.isLoading = false;
2178
+ const { onReady, playing, volume, muted } = this.props;
2179
+ onReady();
2180
+ if (!muted && volume !== null) {
2181
+ this.player.setVolume(volume);
2182
+ }
2183
+ if (this.loadOnReady) {
2184
+ this.player.load(this.loadOnReady, true);
2185
+ this.loadOnReady = null;
2186
+ } else if (playing) {
2187
+ this.player.play();
2188
+ }
2189
+ this.handleDurationCheck();
2190
+ };
2191
+ this.handlePlay = () => {
2192
+ this.isPlaying = true;
2193
+ this.isLoading = false;
2194
+ const { onStart, onPlay, playbackRate } = this.props;
2195
+ if (this.startOnPlay) {
2196
+ if (this.player.setPlaybackRate && playbackRate !== 1) {
2197
+ this.player.setPlaybackRate(playbackRate);
2198
+ }
2199
+ onStart?.();
2200
+ this.startOnPlay = false;
2201
+ }
2202
+ onPlay?.();
2203
+ if (this.seekOnPlay) {
2204
+ this.seekTo(this.seekOnPlay);
2205
+ this.seekOnPlay = null;
2206
+ }
2207
+ this.handleDurationCheck();
2208
+ };
2209
+ this.handlePause = (e) => {
2210
+ this.isPlaying = false;
2211
+ if (!this.isLoading) {
2212
+ this.props.onPause?.(e);
2213
+ }
2214
+ };
2215
+ this.handleEnded = () => {
2216
+ const { activePlayer, loop, onEnded } = this.props;
2217
+ if (activePlayer.loopOnEnded && loop) {
2218
+ this.seekTo(0);
2219
+ }
2220
+ if (!loop) {
2221
+ this.isPlaying = false;
2222
+ onEnded?.();
2223
+ }
2224
+ };
2225
+ this.handleError = (...args) => {
2226
+ this.isLoading = false;
2227
+ this.props.onError?.(args[0], args[1], args[2], args[3]);
2228
+ };
2229
+ this.handleDurationCheck = () => {
2230
+ clearTimeout(this.durationCheckTimeout);
2231
+ const duration = this.getDuration();
2232
+ if (duration) {
2233
+ if (!this.onDurationCalled) {
2234
+ this.props.onDuration?.(duration);
2235
+ this.onDurationCalled = true;
2236
+ }
2237
+ } else {
2238
+ this.durationCheckTimeout = window.setTimeout(
2239
+ this.handleDurationCheck,
2240
+ 100
2241
+ );
2242
+ }
2243
+ };
2244
+ this.handleLoaded = () => {
2245
+ this.isLoading = false;
2246
+ };
2247
+ }
2248
+ componentDidMount() {
2249
+ this.mounted = true;
2250
+ }
2251
+ componentWillUnmount() {
2252
+ clearTimeout(this.progressTimeout);
2253
+ clearTimeout(this.durationCheckTimeout);
2254
+ this.mounted = false;
2255
+ }
2256
+ componentDidUpdate(prevProps) {
2257
+ if (!this.player) return;
2258
+ const { src, playing, volume, muted, playbackRate, loop, activePlayer } = this.props;
2259
+ if (prevProps.src !== src) {
2260
+ if (this.isLoading && !activePlayer.forceLoad && !isMediaStream(src)) {
2261
+ console.warn(
2262
+ `StormcloudPlayer: the attempt to load ${src} is being deferred until the player has loaded`
2263
+ );
2264
+ this.loadOnReady = src || null;
2265
+ return;
2266
+ }
2267
+ this.isLoading = true;
2268
+ this.startOnPlay = true;
2269
+ this.onDurationCalled = false;
2270
+ this.player.load(src, this.isReady);
2271
+ }
2272
+ if (!prevProps.playing && playing && !this.isPlaying) {
2273
+ this.player.play();
2274
+ }
2275
+ if (prevProps.playing && !playing && this.isPlaying) {
2276
+ this.player.pause();
2277
+ }
2278
+ if (prevProps.volume !== volume && volume !== null) {
2279
+ this.player.setVolume(volume);
2280
+ }
2281
+ if (prevProps.muted !== muted) {
2282
+ if (muted) {
2283
+ this.player.mute();
2284
+ } else {
2285
+ this.player.unmute();
2286
+ if (volume !== null) {
2287
+ setTimeout(() => this.player.setVolume(volume));
2288
+ }
2289
+ }
2290
+ }
2291
+ if (prevProps.playbackRate !== playbackRate && this.player.setPlaybackRate) {
2292
+ this.player.setPlaybackRate(playbackRate);
2293
+ }
2294
+ if (prevProps.loop !== loop && this.player.setLoop) {
2295
+ this.player.setLoop(loop);
2296
+ }
2297
+ }
2298
+ getDuration() {
2299
+ if (!this.isReady) return null;
2300
+ return this.player.getDuration();
2301
+ }
2302
+ getCurrentTime() {
2303
+ if (!this.isReady) return null;
2304
+ return this.player.getCurrentTime();
2305
+ }
2306
+ getSecondsLoaded() {
2307
+ if (!this.isReady) return null;
2308
+ return this.player.getSecondsLoaded();
2309
+ }
2310
+ seekTo(amount, type, keepPlaying) {
2311
+ if (!this.isReady) {
2312
+ if (amount !== 0) {
2313
+ this.seekOnPlay = amount;
2314
+ setTimeout(() => {
2315
+ this.seekOnPlay = null;
2316
+ }, SEEK_ON_PLAY_EXPIRY);
2317
+ }
2318
+ return;
2319
+ }
2320
+ const isFraction = !type ? amount > 0 && amount < 1 : type === "fraction";
2321
+ if (isFraction) {
2322
+ const duration = this.player.getDuration();
2323
+ if (!duration) {
2324
+ console.warn(
2325
+ "StormcloudPlayer: could not seek using fraction \u2013 duration not yet available"
2326
+ );
2327
+ return;
2328
+ }
2329
+ this.player.seekTo(duration * amount, keepPlaying);
2330
+ return;
2331
+ }
2332
+ this.player.seekTo(amount, keepPlaying);
2333
+ }
2334
+ render() {
2335
+ const Player2 = this.props.activePlayer;
2336
+ if (!Player2) {
2337
+ return null;
2338
+ }
2339
+ return React.createElement(Player2, {
2340
+ ...this.props,
2341
+ onMount: this.handlePlayerMount,
2342
+ onReady: this.handleReady,
2343
+ onPlay: this.handlePlay,
2344
+ onPause: this.handlePause,
2345
+ onEnded: this.handleEnded,
2346
+ onLoaded: this.handleLoaded,
2347
+ onError: this.handleError
2348
+ });
2349
+ }
2350
+ };
2351
+ Player.displayName = "Player";
2352
+ Player.defaultProps = defaultProps;
2353
+
2354
+ // src/StormcloudPlayer.tsx
2355
+ var IS_BROWSER2 = typeof window !== "undefined" && window.document;
2356
+ var IS_GLOBAL2 = typeof globalThis !== "undefined" && globalThis.window && globalThis.window.document;
2357
+ var UniversalSuspense = IS_BROWSER2 || IS_GLOBAL2 ? Suspense : () => null;
2358
+ var SUPPORTED_PROPS = [
2359
+ "src",
2360
+ "playing",
2361
+ "loop",
2362
+ "controls",
2363
+ "volume",
2364
+ "muted",
2365
+ "playbackRate",
2366
+ "width",
2367
+ "height",
2368
+ "style",
2369
+ "progressInterval",
2370
+ "playsInline",
2371
+ "autoplay",
2372
+ "preload",
2373
+ "poster",
2374
+ "className",
2375
+ "wrapperClassName",
2376
+ "wrapperStyle",
2377
+ "allowNativeHls",
2378
+ "lowLatencyMode",
2379
+ "driftToleranceMs",
2380
+ "immediateManifestAds",
2381
+ "debugAdTiming",
2382
+ "showCustomControls",
2383
+ "licenseKey",
2384
+ "adFailsafeTimeoutMs",
2385
+ "onReady",
2386
+ "onStart",
2387
+ "onPlay",
2388
+ "onPause",
2389
+ "onBuffer",
2390
+ "onBufferEnd",
2391
+ "onEnded",
2392
+ "onError",
2393
+ "onDuration",
2394
+ "onSeek",
2395
+ "onProgress",
2396
+ "onVolumeToggle",
2397
+ "onFullscreenToggle",
2398
+ "onControlClick"
2399
+ ];
2400
+ var customPlayers = [];
2401
+ var createStormcloudPlayer = (playerList, fallback) => {
2402
+ var _a;
2403
+ return _a = class extends Component4 {
2404
+ constructor() {
2405
+ super(...arguments);
2406
+ this.state = {
2407
+ showPreview: false
2408
+ };
2409
+ this.references = {
2410
+ wrapper: (wrapper) => {
2411
+ this.wrapper = wrapper;
2412
+ },
2413
+ player: (player) => {
2414
+ this.player = player;
2415
+ }
2416
+ };
2417
+ this.getActivePlayer = (src) => {
2418
+ if (!src) return null;
2419
+ for (const player of [...customPlayers, ...playerList]) {
2420
+ if (player.canPlay(src)) {
2421
+ return player;
2422
+ }
2423
+ }
2424
+ if (fallback) {
2425
+ return fallback;
2426
+ }
2427
+ return null;
2428
+ };
2429
+ this.getAttributes = (src) => {
2430
+ return omit(this.props, SUPPORTED_PROPS);
2431
+ };
2432
+ this.handleReady = () => {
2433
+ this.props.onReady?.(this);
2434
+ };
2435
+ this.seekTo = (fraction, type, keepPlaying) => {
2436
+ if (!this.player) return null;
2437
+ this.player.seekTo(fraction, type, keepPlaying);
2438
+ };
2439
+ this.getCurrentTime = () => {
2440
+ if (!this.player) return null;
2441
+ return this.player.getCurrentTime();
2442
+ };
2443
+ this.getSecondsLoaded = () => {
2444
+ if (!this.player) return null;
2445
+ return this.player.getSecondsLoaded();
2446
+ };
2447
+ this.getDuration = () => {
2448
+ if (!this.player) return null;
2449
+ return this.player.getDuration();
2450
+ };
2451
+ this.getInternalPlayer = (key = "player") => {
2452
+ if (!this.player) return null;
2453
+ return this.player.getInternalPlayer(key);
2454
+ };
2455
+ this.renderActivePlayer = (src) => {
2456
+ if (!src) return null;
2457
+ const activePlayer = this.getActivePlayer(src);
2458
+ if (!activePlayer) return null;
2459
+ return React2.createElement(Player, {
2460
+ ...this.props,
2461
+ key: activePlayer.key,
2462
+ ref: this.references.player,
2463
+ activePlayer: activePlayer.lazyPlayer || activePlayer,
2464
+ onReady: this.handleReady
2465
+ });
2466
+ };
2467
+ }
2468
+ render() {
2469
+ const {
2470
+ src,
2471
+ style,
2472
+ width,
2473
+ height,
2474
+ fallback: fallbackElement,
2475
+ wrapper: Wrapper
2476
+ } = this.props;
2477
+ const attributes = this.getAttributes(src);
2478
+ const wrapperRef = typeof Wrapper === "string" ? this.references.wrapper : void 0;
2479
+ return React2.createElement(
2480
+ Wrapper,
2481
+ {
2482
+ ref: wrapperRef,
2483
+ style: { ...style, width, height },
2484
+ ...attributes
2485
+ },
2486
+ React2.createElement(
2487
+ UniversalSuspense,
2488
+ { fallback: fallbackElement },
2489
+ this.renderActivePlayer(src)
2490
+ )
2491
+ );
2492
+ }
2493
+ }, _a.displayName = "StormcloudPlayer", _a.defaultProps = {
2494
+ ...defaultProps,
2495
+ fallback: null,
2496
+ wrapper: "div"
2497
+ }, _a.addCustomPlayer = (player) => {
2498
+ customPlayers.push(player);
2499
+ }, _a.removeCustomPlayers = () => {
2500
+ customPlayers.length = 0;
2501
+ }, _a.canPlay = (src) => {
2502
+ for (const Player2 of [...customPlayers, ...playerList]) {
2503
+ if (Player2.canPlay(src)) {
2504
+ return true;
2505
+ }
2506
+ }
2507
+ return false;
2508
+ }, _a.canEnablePIP = (src) => {
2509
+ for (const Player2 of [...customPlayers, ...playerList]) {
2510
+ if (Player2.canEnablePIP && Player2.canEnablePIP(src)) {
2511
+ return true;
2512
+ }
2513
+ }
2514
+ return false;
2515
+ }, _a;
2516
+ };
2517
+ var StormcloudPlayer = createStormcloudPlayer(
2518
+ players_default,
2519
+ players_default[players_default.length - 1]
2520
+ );
2521
+ var StormcloudPlayer_default = StormcloudPlayer;
2522
+
1633
2523
  // src/ui/StormcloudVideoPlayer.tsx
1634
- import React, { useEffect, useRef, useMemo } from "react";
2524
+ import React3, { useEffect, useRef, useMemo } from "react";
1635
2525
  import {
1636
2526
  FaPlay,
1637
2527
  FaPause,
@@ -1650,7 +2540,7 @@ var CRITICAL_PROPS = [
1650
2540
  "lowLatencyMode",
1651
2541
  "driftToleranceMs"
1652
2542
  ];
1653
- var StormcloudVideoPlayerComponent = React.memo(
2543
+ var StormcloudVideoPlayerComponent = React3.memo(
1654
2544
  (props) => {
1655
2545
  const {
1656
2546
  src,
@@ -1680,20 +2570,20 @@ var StormcloudVideoPlayerComponent = React.memo(
1680
2570
  } = props;
1681
2571
  const videoRef = useRef(null);
1682
2572
  const playerRef = useRef(null);
1683
- const [adStatus, setAdStatus] = React.useState({ showAds: false, currentIndex: 0, totalAds: 0 });
1684
- const [shouldShowNativeControls, setShouldShowNativeControls] = React.useState(true);
1685
- const [isMuted, setIsMuted] = React.useState(false);
1686
- const [isFullscreen, setIsFullscreen] = React.useState(false);
1687
- const [isPlaying, setIsPlaying] = React.useState(false);
1688
- const [currentTime, setCurrentTime] = React.useState(0);
1689
- const [duration, setDuration] = React.useState(0);
1690
- const [volume, setVolume] = React.useState(1);
1691
- const [playbackRate, setPlaybackRate] = React.useState(1);
1692
- const [showVolumeSlider, setShowVolumeSlider] = React.useState(false);
1693
- const [showSpeedMenu, setShowSpeedMenu] = React.useState(false);
1694
- const [isLoading, setIsLoading] = React.useState(true);
1695
- const [isBuffering, setIsBuffering] = React.useState(false);
1696
- const [showCenterPlay, setShowCenterPlay] = React.useState(false);
2573
+ const [adStatus, setAdStatus] = React3.useState({ showAds: false, currentIndex: 0, totalAds: 0 });
2574
+ const [shouldShowNativeControls, setShouldShowNativeControls] = React3.useState(true);
2575
+ const [isMuted, setIsMuted] = React3.useState(false);
2576
+ const [isFullscreen, setIsFullscreen] = React3.useState(false);
2577
+ const [isPlaying, setIsPlaying] = React3.useState(false);
2578
+ const [currentTime, setCurrentTime] = React3.useState(0);
2579
+ const [duration, setDuration] = React3.useState(0);
2580
+ const [volume, setVolume] = React3.useState(1);
2581
+ const [playbackRate, setPlaybackRate] = React3.useState(1);
2582
+ const [showVolumeSlider, setShowVolumeSlider] = React3.useState(false);
2583
+ const [showSpeedMenu, setShowSpeedMenu] = React3.useState(false);
2584
+ const [isLoading, setIsLoading] = React3.useState(true);
2585
+ const [isBuffering, setIsBuffering] = React3.useState(false);
2586
+ const [showCenterPlay, setShowCenterPlay] = React3.useState(false);
1697
2587
  const formatTime = (seconds) => {
1698
2588
  if (!isFinite(seconds)) return "0:00:00";
1699
2589
  const hours = Math.floor(seconds / 3600);
@@ -1822,7 +2712,7 @@ var StormcloudVideoPlayerComponent = React.memo(
1822
2712
  });
1823
2713
  }
1824
2714
  };
1825
- const interval = setInterval(checkAdStatus, 500);
2715
+ const interval = setInterval(checkAdStatus, 100);
1826
2716
  return () => clearInterval(interval);
1827
2717
  }, []);
1828
2718
  useEffect(() => {
@@ -1900,7 +2790,11 @@ var StormcloudVideoPlayerComponent = React.memo(
1900
2790
  setShowCenterPlay(false);
1901
2791
  };
1902
2792
  const handlePause = () => {
1903
- setShowCenterPlay(true);
2793
+ if (playerRef.current && !playerRef.current.isShowingAds()) {
2794
+ setShowCenterPlay(true);
2795
+ } else {
2796
+ setShowCenterPlay(false);
2797
+ }
1904
2798
  };
1905
2799
  const handleEnded = () => {
1906
2800
  setShowCenterPlay(true);
@@ -2065,7 +2959,7 @@ var StormcloudVideoPlayerComponent = React.memo(
2065
2959
  )
2066
2960
  }
2067
2961
  ),
2068
- showCenterPlay && !isLoading && !isBuffering && /* @__PURE__ */ jsx(
2962
+ showCenterPlay && !isLoading && !isBuffering && !adStatus.showAds && /* @__PURE__ */ jsx(
2069
2963
  "div",
2070
2964
  {
2071
2965
  onClick: handleCenterPlayClick,
@@ -2116,29 +3010,6 @@ var StormcloudVideoPlayerComponent = React.memo(
2116
3010
  )
2117
3011
  }
2118
3012
  ),
2119
- adStatus.showAds && adStatus.totalAds > 0 && /* @__PURE__ */ jsxs(
2120
- "div",
2121
- {
2122
- style: {
2123
- position: "absolute",
2124
- top: "10px",
2125
- right: "10px",
2126
- backgroundColor: "rgba(0, 0, 0, 0.7)",
2127
- color: "white",
2128
- padding: "4px 8px",
2129
- borderRadius: "4px",
2130
- fontSize: "12px",
2131
- fontFamily: "Arial, sans-serif",
2132
- zIndex: 10
2133
- },
2134
- children: [
2135
- "Ad ",
2136
- adStatus.currentIndex,
2137
- "/",
2138
- adStatus.totalAds
2139
- ]
2140
- }
2141
- ),
2142
3013
  shouldShowEnhancedControls ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
2143
3014
  "div",
2144
3015
  {
@@ -2892,11 +3763,28 @@ var StormcloudVideoPlayerComponent = React.memo(
2892
3763
  }
2893
3764
  );
2894
3765
  export {
3766
+ IS_BROWSER,
3767
+ IS_GLOBAL,
3768
+ IS_IOS,
3769
+ IS_SAFARI,
3770
+ SUPPORTS_DASH,
3771
+ SUPPORTS_HLS,
2895
3772
  StormcloudVideoPlayer,
2896
3773
  StormcloudVideoPlayerComponent,
3774
+ canPlay,
3775
+ createStormcloudPlayer,
3776
+ StormcloudPlayer_default as default,
2897
3777
  getBrowserID,
2898
3778
  getClientInfo,
3779
+ isMediaStream,
3780
+ lazy,
3781
+ merge,
3782
+ omit,
3783
+ parseQuery,
3784
+ players_default as players,
3785
+ randomString,
2899
3786
  sendHeartbeat,
2900
- sendInitialTracking
3787
+ sendInitialTracking,
3788
+ supportsWebKitPresentationMode
2901
3789
  };
2902
3790
  //# sourceMappingURL=index.js.map