mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-27 04:41:03 +00:00
feat: add scan installed games functionality with UI integration
This commit is contained in:
@@ -24,6 +24,7 @@ import "./remove-game-from-favorites";
|
||||
import "./remove-game-from-library";
|
||||
import "./remove-game";
|
||||
import "./reset-game-achievements";
|
||||
import "./scan-installed-games";
|
||||
import "./select-game-wine-prefix";
|
||||
import "./toggle-automatic-cloud-sync";
|
||||
import "./toggle-game-pin";
|
||||
|
||||
129
src/main/events/library/scan-installed-games.ts
Normal file
129
src/main/events/library/scan-installed-games.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { gamesSublevel, levelKeys } from "@main/level";
|
||||
import { GameExecutables, logger, WindowManager } from "@main/services";
|
||||
|
||||
const SCAN_DIRECTORIES = [
|
||||
"C:\\Games",
|
||||
"D:\\Games",
|
||||
"C:\\Program Files (x86)\\Steam\\steamapps\\common",
|
||||
"C:\\Program Files\\Steam\\steamapps\\common",
|
||||
"C:\\Program Files (x86)\\DODI-Repacks",
|
||||
];
|
||||
|
||||
interface FoundGame {
|
||||
title: string;
|
||||
executablePath: string;
|
||||
}
|
||||
|
||||
interface ScanResult {
|
||||
foundGames: FoundGame[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
const scanInstalledGames = async (
|
||||
_event: Electron.IpcMainInvokeEvent
|
||||
): Promise<ScanResult> => {
|
||||
const games = await gamesSublevel
|
||||
.iterator()
|
||||
.all()
|
||||
.then((results) =>
|
||||
results
|
||||
.filter(
|
||||
([_key, game]) => game.isDeleted === false && game.shop !== "custom"
|
||||
)
|
||||
.map(([key, game]) => ({ key, game }))
|
||||
);
|
||||
|
||||
const foundGames: FoundGame[] = [];
|
||||
|
||||
for (const { key, game } of games) {
|
||||
if (game.executablePath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const executableNames = GameExecutables.getExecutablesForGame(
|
||||
game.objectId
|
||||
);
|
||||
|
||||
if (!executableNames || executableNames.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const normalizedNames = new Set(
|
||||
executableNames.map((name) => name.toLowerCase())
|
||||
);
|
||||
|
||||
let foundPath: string | null = null;
|
||||
|
||||
for (const scanDir of SCAN_DIRECTORIES) {
|
||||
if (!fs.existsSync(scanDir)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foundPath = await findExecutableInFolder(scanDir, normalizedNames);
|
||||
|
||||
if (foundPath) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundPath) {
|
||||
await gamesSublevel.put(key, {
|
||||
...game,
|
||||
executablePath: foundPath,
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`[ScanInstalledGames] Found executable for ${game.objectId}: ${foundPath}`
|
||||
);
|
||||
|
||||
foundGames.push({
|
||||
title: game.title,
|
||||
executablePath: foundPath,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
WindowManager.mainWindow?.webContents.send("on-library-batch-complete");
|
||||
|
||||
return {
|
||||
foundGames,
|
||||
total: games.filter((g) => !g.game.executablePath).length,
|
||||
};
|
||||
};
|
||||
|
||||
async function findExecutableInFolder(
|
||||
folderPath: string,
|
||||
executableNames: Set<string>
|
||||
): Promise<string | null> {
|
||||
try {
|
||||
const entries = await fs.promises.readdir(folderPath, {
|
||||
withFileTypes: true,
|
||||
recursive: true,
|
||||
});
|
||||
|
||||
for (const entry of entries) {
|
||||
if (!entry.isFile()) continue;
|
||||
|
||||
const fileName = entry.name.toLowerCase();
|
||||
|
||||
if (executableNames.has(fileName)) {
|
||||
const parentPath =
|
||||
"parentPath" in entry ? (entry.parentPath as string) : folderPath;
|
||||
|
||||
return path.join(parentPath, entry.name);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
`[ScanInstalledGames] Error reading folder ${folderPath}:`,
|
||||
err
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
registerEvent("scanInstalledGames", scanInstalledGames);
|
||||
Reference in New Issue
Block a user