diff --git a/crates/librqbit/webui/src/api-types.ts b/crates/librqbit/webui/src/api-types.ts index 45be313..3d9488e 100644 --- a/crates/librqbit/webui/src/api-types.ts +++ b/crates/librqbit/webui/src/api-types.ts @@ -34,6 +34,21 @@ export interface Speed { human_readable: string; } +export interface AggregatePeerStats { + queued: number; + connecting: number; + live: number; + seen: number; + dead: number; + not_needed: number; +} + +export interface SessionStats { + download_speed: Speed; + upload_speed: Speed; + peers: AggregatePeerStats; +} + // Interface for the Torrent Stats API response export interface LiveTorrentStats { snapshot: { @@ -46,14 +61,7 @@ export interface LiveTorrentStats { remaining_bytes: number; total_bytes: number; total_piece_download_ms: number; - peer_stats: { - queued: number; - connecting: number; - live: number; - seen: number; - dead: number; - not_needed: number; - }; + peer_stats: AggregatePeerStats; }; average_piece_download_time: { secs: number; @@ -182,4 +190,5 @@ export interface RqbitAPI { start: (index: number) => Promise; forget: (index: number) => Promise; delete: (index: number) => Promise; + stats: () => Promise; } diff --git a/crates/librqbit/webui/src/components/Footer.tsx b/crates/librqbit/webui/src/components/Footer.tsx new file mode 100644 index 0000000..186cab6 --- /dev/null +++ b/crates/librqbit/webui/src/components/Footer.tsx @@ -0,0 +1,12 @@ +import { useStatsStore } from "../stores/statsStore"; +import { Speed } from "./Speed"; + +export const Footer: React.FC<{}> = () => { + let stats = useStatsStore((stats) => stats.stats); + return ( +
+
↓ {stats.download_speed.human_readable}
+
↑ {stats.upload_speed.human_readable}
+
+ ); +}; diff --git a/crates/librqbit/webui/src/components/TorrentsList.tsx b/crates/librqbit/webui/src/components/TorrentsList.tsx index 5136137..1b9410b 100644 --- a/crates/librqbit/webui/src/components/TorrentsList.tsx +++ b/crates/librqbit/webui/src/components/TorrentsList.tsx @@ -19,7 +19,9 @@ export const TorrentsList = (props: {

No existing torrents found.

) : ( props.torrents.map((t: TorrentId) => ( - + <> + + )) )} diff --git a/crates/librqbit/webui/src/http-api.ts b/crates/librqbit/webui/src/http-api.ts index ef6af66..3bf66d8 100644 --- a/crates/librqbit/webui/src/http-api.ts +++ b/crates/librqbit/webui/src/http-api.ts @@ -3,6 +3,7 @@ import { ErrorDetails, ListTorrentsResponse, RqbitAPI, + SessionStats, TorrentDetails, TorrentStats, } from "./api-types"; @@ -82,6 +83,9 @@ export const API: RqbitAPI & { getVersion: () => Promise } = { getTorrentStats: (index: number): Promise => { return makeRequest("GET", `/torrents/${index}/stats/v1`); }, + stats: (): Promise => { + return makeRequest("GET", "/stats"); + }, uploadTorrent: (data, opts): Promise => { let url = "/torrents?&overwrite=true"; @@ -152,6 +156,6 @@ export const API: RqbitAPI & { getVersion: () => Promise } = { return url; }, getPlaylistUrl: (index: number) => { - return (apiUrl || window.origin) + `/torrents/${index}/playlist`; + return (apiUrl || window.origin) + `/torrents/${index}/playlist`; }, }; diff --git a/crates/librqbit/webui/src/rqbit-web.tsx b/crates/librqbit/webui/src/rqbit-web.tsx index e2ac3a3..2542cce 100644 --- a/crates/librqbit/webui/src/rqbit-web.tsx +++ b/crates/librqbit/webui/src/rqbit-web.tsx @@ -11,6 +11,8 @@ import { DarkMode } from "./helper/darkMode"; import { useTorrentStore } from "./stores/torrentStore"; import { useErrorStore } from "./stores/errorStore"; import { AlertModal } from "./components/modal/AlertModal"; +import { useStatsStore } from "./stores/statsStore"; +import { Footer } from "./components/Footer"; export interface ErrorWithLabel { text: string; @@ -49,6 +51,8 @@ export const RqbitWebUI = (props: { }; setRefreshTorrents(refreshTorrents); + const setStats = useStatsStore((state) => state.setStats); + useEffect(() => { return customSetInterval( async () => @@ -67,8 +71,25 @@ export const RqbitWebUI = (props: { ); }, []); + useEffect(() => { + return customSetInterval( + async () => + API.stats().then( + (stats) => { + setStats(stats); + return 1000; + }, + (e) => { + console.error(e); + return 5000; + } + ), + 0 + ); + }, []); + return ( -
+
{/* Menu buttons */} @@ -82,10 +103,14 @@ export const RqbitWebUI = (props: {
+
+
+
+ setLogsOpened(false)} />
diff --git a/crates/librqbit/webui/src/stores/statsStore.ts b/crates/librqbit/webui/src/stores/statsStore.ts new file mode 100644 index 0000000..ebf2b4c --- /dev/null +++ b/crates/librqbit/webui/src/stores/statsStore.ts @@ -0,0 +1,26 @@ +import { create } from "zustand"; + +import { SessionStats } from "../api-types"; + +export interface StatsStore { + stats: SessionStats; + setStats: (stats: SessionStats) => void; +} + +export const useStatsStore = create((set) => ({ + stats: { + download_speed: { human_readable: "N/A", mbps: 0 }, + upload_speed: { human_readable: "N/A", mbps: 0 }, + peers: { + connecting: 0, + dead: 0, + live: 0, + not_needed: 0, + queued: 0, + seen: 0, + }, + }, + setStats: (stats) => { + set({ stats }); + }, +}));