From 56687948bf8406d27239c6af9f2560cfe234eb35 Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Sat, 9 Nov 2024 12:45:48 -0300 Subject: [PATCH] feat: display upload speed during seeding --- .../services/download/download-manager.ts | 7 ++++-- src/preload/index.ts | 9 +++++++ src/renderer/src/declaration.d.ts | 4 ++++ .../src/pages/downloads/download-group.tsx | 24 ++++++++++++++++--- .../src/pages/downloads/downloads.tsx | 11 +++++++-- src/types/index.ts | 6 +++++ 6 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index 241e4806..9efe3e2e 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -76,8 +76,6 @@ export class DownloadManager { const seedStatus = await PythonInstance.getSeedStatus(); - console.log(seedStatus); - if (seedStatus.length === 0) { for (const game of gamesToSeed) { if (game.uri && game.downloadPath) { @@ -102,6 +100,11 @@ export class DownloadManager { { status: "seeding" } ); } + + WindowManager.mainWindow?.webContents.send( + "on-seeding-status", + JSON.parse(JSON.stringify(seedStatus)) + ); } static async pauseSeeding(gameId: number) { diff --git a/src/preload/index.ts b/src/preload/index.ts index f1a26150..12c5054a 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -11,6 +11,7 @@ import type { GameRunning, FriendRequestAction, UpdateProfileRequest, + SeedingStatus, } from "@types"; import type { CatalogueCategory } from "@shared"; import type { AxiosProgressEvent } from "axios"; @@ -38,6 +39,14 @@ contextBridge.exposeInMainWorld("electron", { ipcRenderer.on("on-download-progress", listener); return () => ipcRenderer.removeListener("on-download-progress", listener); }, + onSeedingStatus: (cb: (value: SeedingStatus[]) => void) => { + const listener = ( + _event: Electron.IpcRendererEvent, + value: SeedingStatus[] + ) => cb(value); + ipcRenderer.on("on-seeding-status", listener); + return () => ipcRenderer.removeListener("on-seeding-status", listener); + }, /* Catalogue */ searchGames: (query: string) => ipcRenderer.invoke("searchGames", query), diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 93e5b295..9e658463 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -10,6 +10,7 @@ import type { ShopDetails, Steam250Game, DownloadProgress, + SeedingStatus, UserPreferences, StartGameDownloadPayload, RealDebridUser, @@ -51,6 +52,9 @@ declare global { onDownloadProgress: ( cb: (value: DownloadProgress) => void ) => () => Electron.IpcRenderer; + onSeedingStatus: ( + cb: (value: SeedingStatus[]) => void + ) => () => Electron.IpcRenderer; /* Catalogue */ searchGames: (query: string) => Promise; diff --git a/src/renderer/src/pages/downloads/download-group.tsx b/src/renderer/src/pages/downloads/download-group.tsx index 34d44006..7b0e3332 100644 --- a/src/renderer/src/pages/downloads/download-group.tsx +++ b/src/renderer/src/pages/downloads/download-group.tsx @@ -1,6 +1,6 @@ import { useNavigate } from "react-router-dom"; -import type { LibraryGame } from "@types"; +import type { LibraryGame, SeedingStatus } from "@types"; import { Badge, Button } from "@renderer/components"; import { @@ -16,12 +16,14 @@ import * as styles from "./download-group.css"; import { useTranslation } from "react-i18next"; import { SPACING_UNIT, vars } from "@renderer/theme.css"; import { XCircleIcon } from "@primer/octicons-react"; +import { useMemo } from "react"; export interface DownloadGroupProps { library: LibraryGame[]; title: string; openDeleteGameModal: (gameId: number) => void; openGameInstaller: (gameId: number) => void; + seedingStatus: SeedingStatus[]; } export function DownloadGroup({ @@ -29,6 +31,7 @@ export function DownloadGroup({ title, openDeleteGameModal, openGameInstaller, + seedingStatus, }: DownloadGroupProps) { const navigate = useNavigate(); @@ -60,9 +63,21 @@ export function DownloadGroup({ return "N/A"; }; + const seedingMap = useMemo(() => { + if (!Array.isArray(seedingStatus) || seedingStatus.length === 0) { + return new Map(); + } + const map = new Map(); + seedingStatus.forEach((seed) => { + map.set(seed.gameId, seed); + }); + return map; + }, [seedingStatus]); + const getGameInfo = (game: LibraryGame) => { const isGameDownloading = lastPacket?.game.id === game.id; const finalDownloadSize = getFinalDownloadSize(game); + const seedingStatus = seedingMap.get(game.id); if (isGameDeleting(game.id)) { return

{t("deleting")}

; @@ -101,10 +116,13 @@ export function DownloadGroup({ } if (game.progress === 1) { - // return

{t("completed")}

; + const uploadSpeed = formatBytes(seedingStatus?.uploadSpeed ?? 0); return game.status === "seeding" ? ( -

{t("seeding")}

+ <> +

{t("seeding")}

+ {uploadSpeed &&

{uploadSpeed}/s

} + ) : (

{t("completed")}

); diff --git a/src/renderer/src/pages/downloads/downloads.tsx b/src/renderer/src/pages/downloads/downloads.tsx index ed541745..6acfb82b 100644 --- a/src/renderer/src/pages/downloads/downloads.tsx +++ b/src/renderer/src/pages/downloads/downloads.tsx @@ -2,12 +2,12 @@ import { useTranslation } from "react-i18next"; import { useDownload, useLibrary } from "@renderer/hooks"; -import { useMemo, useRef, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { BinaryNotFoundModal } from "../shared-modals/binary-not-found-modal"; import * as styles from "./downloads.css"; import { DeleteGameModal } from "./delete-game-modal"; import { DownloadGroup } from "./download-group"; -import type { LibraryGame } from "@types"; +import type { LibraryGame, SeedingStatus } from "@types"; import { orderBy } from "lodash-es"; import { ArrowDownIcon } from "@primer/octicons-react"; @@ -30,6 +30,12 @@ export default function Downloads() { const { lastPacket } = useDownload(); + const [seedingStatus, setSeedingStatus] = useState([]); + + useEffect(() => { + window.electron.onSeedingStatus((value) => setSeedingStatus(value)); + }, []); + const handleOpenGameInstaller = (gameId: number) => window.electron.openGameInstaller(gameId).then((isBinaryInPath) => { if (!isBinaryInPath) setShowBinaryNotFoundModal(true); @@ -122,6 +128,7 @@ export default function Downloads() { library={group.library} openDeleteGameModal={handleOpenDeleteGameModal} openGameInstaller={handleOpenGameInstaller} + seedingStatus={seedingStatus} /> ))} diff --git a/src/types/index.ts b/src/types/index.ts index 2e4894b3..a65cd587 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -153,6 +153,12 @@ export interface DownloadProgress { game: LibraryGame; } +export interface SeedingStatus { + gameId: number; + status: GameStatus; + uploadSpeed: number; +} + export interface UserPreferences { downloadsPath: string | null; language: string;