feat: adding installation logs

This commit is contained in:
Chubby Granny Chaser
2025-04-05 03:31:56 +01:00
parent f464850c38
commit ede538392f
7 changed files with 120 additions and 11 deletions

View File

@@ -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…",

View File

@@ -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() {

View File

@@ -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,

View File

@@ -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"),

View File

@@ -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<null | string>("");
const [commonRedistStatus, setCommonRedistStatus] = useState<string | null>(
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 (
<footer className="bottom-panel">

View File

@@ -235,6 +235,9 @@ declare global {
getFeatures: () => Promise<string[]>;
getBadges: () => Promise<Badge[]>;
installCommonRedist: () => Promise<void>;
onCommonRedistProgress: (
cb: (value: { component: string; complete: boolean }) => void
) => () => Electron.IpcRenderer;
platform: NodeJS.Platform;
/* Auto update */

View File

@@ -21,9 +21,9 @@ export interface CatalogueCache {
export const db = new Dexie("Hydra");
db.version(8).stores({
db.version(9).stores({
repacks: `++id, title, uris, fileSize, uploadDate, downloadSourceId, repacker, objectIds, createdAt, updatedAt`,
downloadSources: `++id, url, name, etag, objectIds, downloadCount, status, fingerprint, createdAt, updatedAt`,
downloadSources: `++id, &url, name, etag, objectIds, downloadCount, status, fingerprint, createdAt, updatedAt`,
howLongToBeatEntries: `++id, categories, [shop+objectId], createdAt, updatedAt`,
});