mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-19 02:13:01 -03:00
Compare commits
9 Commits
release/v3
...
feat/LBX-3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9801644ac | ||
|
|
98cfe7be98 | ||
|
|
7293afb618 | ||
|
|
194e7918ca | ||
|
|
979958aca6 | ||
|
|
6e92e0f79f | ||
|
|
aef069d4c7 | ||
|
|
5d2dc3616c | ||
|
|
96140e614c |
@@ -25,7 +25,7 @@ const getLibrary = async (): Promise<LibraryGame[]> => {
|
||||
const achievements = await gameAchievementsSublevel.get(key);
|
||||
|
||||
unlockedAchievementCount =
|
||||
achievements?.unlockedAchievements.length ?? 0;
|
||||
achievements?.unlockedAchievements?.length ?? 0;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -362,6 +362,11 @@ export class DownloadManager {
|
||||
|
||||
if (download.automaticallyExtract) {
|
||||
this.handleExtraction(download, game);
|
||||
} else {
|
||||
// For downloads without extraction (e.g., torrents with ready-to-play files),
|
||||
// search for executable in the download folder
|
||||
const gameFilesManager = new GameFilesManager(game.shop, game.objectId);
|
||||
gameFilesManager.searchAndBindExecutable();
|
||||
}
|
||||
|
||||
await this.processNextQueuedDownload();
|
||||
|
||||
13
src/main/services/game-executables.ts
Normal file
13
src/main/services/game-executables.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { gameExecutables } from "./process-watcher";
|
||||
|
||||
export class GameExecutables {
|
||||
static getExecutablesForGame(objectId: string): string[] | null {
|
||||
const executables = gameExecutables[objectId];
|
||||
|
||||
if (!executables || executables.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return executables.map((exe) => exe.exe);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { SevenZip, ExtractionProgress } from "./7zip";
|
||||
import { WindowManager } from "./window-manager";
|
||||
import { publishExtractionCompleteNotification } from "./notifications";
|
||||
import { logger } from "./logger";
|
||||
import { GameExecutables } from "./game-executables";
|
||||
|
||||
const PROGRESS_THROTTLE_MS = 1000;
|
||||
|
||||
@@ -151,6 +152,100 @@ export class GameFilesManager {
|
||||
if (publishNotification && game) {
|
||||
publishExtractionCompleteNotification(game);
|
||||
}
|
||||
|
||||
await this.searchAndBindExecutable();
|
||||
}
|
||||
|
||||
async searchAndBindExecutable(): Promise<void> {
|
||||
try {
|
||||
const [download, game] = await Promise.all([
|
||||
downloadsSublevel.get(this.gameKey),
|
||||
gamesSublevel.get(this.gameKey),
|
||||
]);
|
||||
|
||||
if (!download || !game || game.executablePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
const executableNames = GameExecutables.getExecutablesForGame(
|
||||
this.objectId
|
||||
);
|
||||
|
||||
if (!executableNames || executableNames.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!download.folderName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const gameFolderPath = path.join(
|
||||
download.downloadPath,
|
||||
download.folderName
|
||||
);
|
||||
|
||||
if (!fs.existsSync(gameFolderPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const foundExePath = await this.findExecutableInFolder(
|
||||
gameFolderPath,
|
||||
executableNames
|
||||
);
|
||||
|
||||
if (foundExePath) {
|
||||
logger.info(
|
||||
`[GameFilesManager] Auto-detected executable for ${this.objectId}: ${foundExePath}`
|
||||
);
|
||||
|
||||
await gamesSublevel.put(this.gameKey, {
|
||||
...game,
|
||||
executablePath: foundExePath,
|
||||
});
|
||||
|
||||
WindowManager.mainWindow?.webContents.send("on-library-batch-complete");
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
`[GameFilesManager] Error searching for executable: ${this.objectId}`,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async findExecutableInFolder(
|
||||
folderPath: string,
|
||||
executableNames: string[]
|
||||
): Promise<string | null> {
|
||||
const normalizedNames = new Set(
|
||||
executableNames.map((name) => name.toLowerCase())
|
||||
);
|
||||
|
||||
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 (normalizedNames.has(fileName)) {
|
||||
const parentPath =
|
||||
"parentPath" in entry
|
||||
? entry.parentPath
|
||||
: (entry as unknown as { path?: string }).path || folderPath;
|
||||
|
||||
return path.join(parentPath, entry.name);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Silently fail if folder cannot be read
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async extractDownloadedFile() {
|
||||
|
||||
@@ -10,6 +10,7 @@ export * from "./ludusavi";
|
||||
export * from "./cloud-sync";
|
||||
export * from "./7zip";
|
||||
export * from "./game-files-manager";
|
||||
export * from "./game-executables";
|
||||
export * from "./common-redist-manager";
|
||||
export * from "./aria2";
|
||||
export * from "./ws";
|
||||
|
||||
@@ -69,7 +69,7 @@ const getGameExecutables = async () => {
|
||||
return gameExecutables;
|
||||
};
|
||||
|
||||
const gameExecutables = await getGameExecutables();
|
||||
export const gameExecutables = await getGameExecutables();
|
||||
|
||||
const findGamePathByProcess = async (
|
||||
processMap: Map<string, Set<string>>,
|
||||
|
||||
@@ -138,12 +138,21 @@ export class WindowManager {
|
||||
(details, callback) => {
|
||||
if (
|
||||
details.webContentsId !== this.mainWindow?.webContents.id ||
|
||||
details.url.includes("chatwoot") ||
|
||||
details.url.includes("workwonders")
|
||||
details.url.includes("chatwoot")
|
||||
) {
|
||||
return callback(details);
|
||||
}
|
||||
|
||||
if (details.url.includes("workwonders")) {
|
||||
return callback({
|
||||
...details,
|
||||
requestHeaders: {
|
||||
Origin: "https://workwonders.app",
|
||||
...details.requestHeaders,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const userAgent = new UserAgent();
|
||||
|
||||
callback({
|
||||
|
||||
@@ -225,6 +225,16 @@ export function GameDetailsContextProvider({
|
||||
};
|
||||
}, [game?.id, isGameRunning, updateGame]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = window.electron.onLibraryBatchComplete(() => {
|
||||
updateGame();
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [updateGame]);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (ev: Event) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user