mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-26 20:31:03 +00:00
feat: adding installation logs
This commit is contained in:
@@ -44,7 +44,10 @@
|
|||||||
"downloading_metadata": "Downloading {{title}} metadata…",
|
"downloading_metadata": "Downloading {{title}} metadata…",
|
||||||
"downloading": "Downloading {{title}}… ({{percentage}} complete) - Completion {{eta}} - {{speed}}",
|
"downloading": "Downloading {{title}}… ({{percentage}} complete) - Completion {{eta}} - {{speed}}",
|
||||||
"calculating_eta": "Downloading {{title}}… ({{percentage}} complete) - Calculating remaining time…",
|
"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": {
|
"catalogue": {
|
||||||
"search": "Filter…",
|
"search": "Filter…",
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import fs from "node:fs";
|
|||||||
import cp from "node:child_process";
|
import cp from "node:child_process";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { logger } from "./logger";
|
import { logger } from "./logger";
|
||||||
|
import { app } from "electron";
|
||||||
|
import { WindowManager } from "./window-manager";
|
||||||
|
|
||||||
export class CommonRedistManager {
|
export class CommonRedistManager {
|
||||||
private static readonly redistributables = [
|
private static readonly redistributables = [
|
||||||
@@ -17,13 +19,59 @@ export class CommonRedistManager {
|
|||||||
"vcredist_x86.exe",
|
"vcredist_x86.exe",
|
||||||
"xnafx40_redist.msi",
|
"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() {
|
public static async installCommonRedist() {
|
||||||
cp.execFile(path.join(commonRedistPath, "install.bat"), (error) => {
|
const abortController = new AbortController();
|
||||||
if (error) {
|
const timeout = setTimeout(() => {
|
||||||
logger.error("Failed to run install.bat", error);
|
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() {
|
public static async canInstallCommonRedist() {
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export class DownloadManager {
|
|||||||
userPreferences?.seedAfterDownloadComplete &&
|
userPreferences?.seedAfterDownloadComplete &&
|
||||||
download.downloader === Downloader.Torrent
|
download.downloader === Downloader.Torrent
|
||||||
) {
|
) {
|
||||||
downloadsSublevel.put(gameId, {
|
await downloadsSublevel.put(gameId, {
|
||||||
...download,
|
...download,
|
||||||
status: "seeding",
|
status: "seeding",
|
||||||
shouldSeed: true,
|
shouldSeed: true,
|
||||||
@@ -154,7 +154,7 @@ export class DownloadManager {
|
|||||||
} else {
|
} else {
|
||||||
const shouldExtract = download.automaticallyExtract;
|
const shouldExtract = download.automaticallyExtract;
|
||||||
|
|
||||||
downloadsSublevel.put(gameId, {
|
await downloadsSublevel.put(gameId, {
|
||||||
...download,
|
...download,
|
||||||
status: "complete",
|
status: "complete",
|
||||||
shouldSeed: false,
|
shouldSeed: false,
|
||||||
|
|||||||
@@ -311,6 +311,16 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
ipcRenderer.removeListener("autoUpdaterEvent", listener);
|
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"),
|
checkForUpdates: () => ipcRenderer.invoke("checkForUpdates"),
|
||||||
restartAndInstallUpdate: () => ipcRenderer.invoke("restartAndInstallUpdate"),
|
restartAndInstallUpdate: () => ipcRenderer.invoke("restartAndInstallUpdate"),
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { useDownload, useLibrary, useUserDetails } from "@renderer/hooks";
|
import {
|
||||||
|
useDownload,
|
||||||
|
useLibrary,
|
||||||
|
useToast,
|
||||||
|
useUserDetails,
|
||||||
|
} from "@renderer/hooks";
|
||||||
|
|
||||||
import "./bottom-panel.scss";
|
import "./bottom-panel.scss";
|
||||||
|
|
||||||
@@ -17,20 +22,52 @@ export function BottomPanel() {
|
|||||||
|
|
||||||
const { library } = useLibrary();
|
const { library } = useLibrary();
|
||||||
|
|
||||||
|
const { showSuccessToast } = useToast();
|
||||||
|
|
||||||
const { lastPacket, progress, downloadSpeed, eta } = useDownload();
|
const { lastPacket, progress, downloadSpeed, eta } = useDownload();
|
||||||
|
|
||||||
const [version, setVersion] = useState("");
|
const [version, setVersion] = useState("");
|
||||||
const [sessionHash, setSessionHash] = useState<null | string>("");
|
const [sessionHash, setSessionHash] = useState<null | string>("");
|
||||||
|
const [commonRedistStatus, setCommonRedistStatus] = useState<string | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.electron.getVersion().then((result) => setVersion(result));
|
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(() => {
|
useEffect(() => {
|
||||||
window.electron.getSessionHash().then((result) => setSessionHash(result));
|
window.electron.getSessionHash().then((result) => setSessionHash(result));
|
||||||
}, [userDetails?.id]);
|
}, [userDetails?.id]);
|
||||||
|
|
||||||
const status = useMemo(() => {
|
const status = useMemo(() => {
|
||||||
|
if (commonRedistStatus) {
|
||||||
|
return t("installing_common_redist", { log: commonRedistStatus });
|
||||||
|
}
|
||||||
|
|
||||||
const game = lastPacket
|
const game = lastPacket
|
||||||
? library.find((game) => game.id === lastPacket?.gameId)
|
? library.find((game) => game.id === lastPacket?.gameId)
|
||||||
: undefined;
|
: undefined;
|
||||||
@@ -64,7 +101,15 @@ export function BottomPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return t("no_downloads_in_progress");
|
return t("no_downloads_in_progress");
|
||||||
}, [t, library, lastPacket, progress, eta, downloadSpeed]);
|
}, [
|
||||||
|
t,
|
||||||
|
library,
|
||||||
|
lastPacket,
|
||||||
|
progress,
|
||||||
|
eta,
|
||||||
|
downloadSpeed,
|
||||||
|
commonRedistStatus,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="bottom-panel">
|
<footer className="bottom-panel">
|
||||||
|
|||||||
3
src/renderer/src/declaration.d.ts
vendored
3
src/renderer/src/declaration.d.ts
vendored
@@ -235,6 +235,9 @@ declare global {
|
|||||||
getFeatures: () => Promise<string[]>;
|
getFeatures: () => Promise<string[]>;
|
||||||
getBadges: () => Promise<Badge[]>;
|
getBadges: () => Promise<Badge[]>;
|
||||||
installCommonRedist: () => Promise<void>;
|
installCommonRedist: () => Promise<void>;
|
||||||
|
onCommonRedistProgress: (
|
||||||
|
cb: (value: { component: string; complete: boolean }) => void
|
||||||
|
) => () => Electron.IpcRenderer;
|
||||||
platform: NodeJS.Platform;
|
platform: NodeJS.Platform;
|
||||||
|
|
||||||
/* Auto update */
|
/* Auto update */
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ export interface CatalogueCache {
|
|||||||
|
|
||||||
export const db = new Dexie("Hydra");
|
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`,
|
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`,
|
howLongToBeatEntries: `++id, categories, [shop+objectId], createdAt, updatedAt`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user