From ede538392f194d042cf14aa4f2a73e0a66cbdbd2 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Sat, 5 Apr 2025 03:31:56 +0100 Subject: [PATCH] feat: adding installation logs --- src/locales/en/translation.json | 5 +- src/main/services/common-redist-manager.ts | 56 +++++++++++++++++-- .../services/download/download-manager.ts | 4 +- src/preload/index.ts | 10 ++++ .../components/bottom-panel/bottom-panel.tsx | 49 +++++++++++++++- src/renderer/src/declaration.d.ts | 3 + src/renderer/src/dexie.ts | 4 +- 7 files changed, 120 insertions(+), 11 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 04bb5493..f8e7a0d1 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -44,7 +44,10 @@ "downloading_metadata": "Downloading {{title}} metadata…", "downloading": "Downloading {{title}}… ({{percentage}} complete) - Completion {{eta}} - {{speed}}", "calculating_eta": "Downloading {{title}}… ({{percentage}} complete) - Calculating remaining time…", - "checking_files": "Checking {{title}} files… ({{percentage}} complete)" + "checking_files": "Checking {{title}} files… ({{percentage}} complete)", + "installing_common_redist": "{{log}}…", + "installation_complete": "Installation complete", + "installation_complete_message": "Common redistributables installed successfully" }, "catalogue": { "search": "Filter…", diff --git a/src/main/services/common-redist-manager.ts b/src/main/services/common-redist-manager.ts index 9c8c7b67..d23306da 100644 --- a/src/main/services/common-redist-manager.ts +++ b/src/main/services/common-redist-manager.ts @@ -4,6 +4,8 @@ import fs from "node:fs"; import cp from "node:child_process"; import path from "node:path"; import { logger } from "./logger"; +import { app } from "electron"; +import { WindowManager } from "./window-manager"; export class CommonRedistManager { private static readonly redistributables = [ @@ -17,13 +19,59 @@ export class CommonRedistManager { "vcredist_x86.exe", "xnafx40_redist.msi", ]; + private static readonly installationTimeout = 1000 * 60 * 5; // 5 minutes + private static readonly installationLog = path.join( + app.getPath("temp"), + "common_redist_install.log" + ); public static async installCommonRedist() { - cp.execFile(path.join(commonRedistPath, "install.bat"), (error) => { - if (error) { - logger.error("Failed to run install.bat", error); - } + const abortController = new AbortController(); + const timeout = setTimeout(() => { + abortController.abort(); + logger.error("Installation timed out"); + + WindowManager.mainWindow?.webContents.send("common-redist-progress", { + log: "Installation timed out", + complete: false, + }); + }, this.installationTimeout); + + const installationCompleteMessage = "Installation complete"; + + fs.watch(this.installationLog, { signal: abortController.signal }, () => { + fs.readFile(this.installationLog, "utf-8", (err, data) => { + if (err) return logger.error("Error reading log file:", err); + + const tail = data.split("\n").at(-2)?.trim(); + + if (tail?.includes(installationCompleteMessage)) { + clearTimeout(timeout); + if (!abortController.signal.aborted) { + abortController.abort(); + } + } + + const [_, component] = tail?.split("Installing ") ?? []; + + WindowManager.mainWindow?.webContents.send("common-redist-progress", { + component: component, + complete: tail?.includes(installationCompleteMessage), + }); + }); }); + + cp.exec( + path.join(commonRedistPath, "install.bat"), + { + windowsHide: true, + }, + (error) => { + if (error) { + logger.error("Failed to run install.bat", error); + } + } + ); } public static async canInstallCommonRedist() { diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index 84289039..b643938a 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -145,7 +145,7 @@ export class DownloadManager { userPreferences?.seedAfterDownloadComplete && download.downloader === Downloader.Torrent ) { - downloadsSublevel.put(gameId, { + await downloadsSublevel.put(gameId, { ...download, status: "seeding", shouldSeed: true, @@ -154,7 +154,7 @@ export class DownloadManager { } else { const shouldExtract = download.automaticallyExtract; - downloadsSublevel.put(gameId, { + await downloadsSublevel.put(gameId, { ...download, status: "complete", shouldSeed: false, diff --git a/src/preload/index.ts b/src/preload/index.ts index 63f77dcf..8d5e2e8d 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -311,6 +311,16 @@ contextBridge.exposeInMainWorld("electron", { ipcRenderer.removeListener("autoUpdaterEvent", listener); }; }, + onCommonRedistProgress: ( + cb: (value: { component: string; complete: boolean }) => void + ) => { + const listener = ( + _event: Electron.IpcRendererEvent, + value: { component: string; complete: boolean } + ) => cb(value); + ipcRenderer.on("common-redist-progress", listener); + return () => ipcRenderer.removeListener("common-redist-progress", listener); + }, checkForUpdates: () => ipcRenderer.invoke("checkForUpdates"), restartAndInstallUpdate: () => ipcRenderer.invoke("restartAndInstallUpdate"), diff --git a/src/renderer/src/components/bottom-panel/bottom-panel.tsx b/src/renderer/src/components/bottom-panel/bottom-panel.tsx index 16f1de06..2c32c5da 100644 --- a/src/renderer/src/components/bottom-panel/bottom-panel.tsx +++ b/src/renderer/src/components/bottom-panel/bottom-panel.tsx @@ -1,7 +1,12 @@ import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; -import { useDownload, useLibrary, useUserDetails } from "@renderer/hooks"; +import { + useDownload, + useLibrary, + useToast, + useUserDetails, +} from "@renderer/hooks"; import "./bottom-panel.scss"; @@ -17,20 +22,52 @@ export function BottomPanel() { const { library } = useLibrary(); + const { showSuccessToast } = useToast(); + const { lastPacket, progress, downloadSpeed, eta } = useDownload(); const [version, setVersion] = useState(""); const [sessionHash, setSessionHash] = useState(""); + const [commonRedistStatus, setCommonRedistStatus] = useState( + null + ); useEffect(() => { window.electron.getVersion().then((result) => setVersion(result)); }, []); + useEffect(() => { + const unlisten = window.electron.onCommonRedistProgress( + ({ log, complete }) => { + if (log === "Installation timed out" || complete) { + setCommonRedistStatus(null); + + if (complete) { + showSuccessToast( + t("installation_complete"), + t("installation_complete_message") + ); + } + + return; + } + + setCommonRedistStatus(log); + } + ); + + return () => unlisten(); + }, [t, showSuccessToast]); + useEffect(() => { window.electron.getSessionHash().then((result) => setSessionHash(result)); }, [userDetails?.id]); const status = useMemo(() => { + if (commonRedistStatus) { + return t("installing_common_redist", { log: commonRedistStatus }); + } + const game = lastPacket ? library.find((game) => game.id === lastPacket?.gameId) : undefined; @@ -64,7 +101,15 @@ export function BottomPanel() { } return t("no_downloads_in_progress"); - }, [t, library, lastPacket, progress, eta, downloadSpeed]); + }, [ + t, + library, + lastPacket, + progress, + eta, + downloadSpeed, + commonRedistStatus, + ]); return (