chore: merge with main

This commit is contained in:
Hydra
2024-04-25 05:52:19 +01:00
47 changed files with 1337 additions and 451 deletions

View File

@@ -40,7 +40,7 @@ export class Game {
shop: GameShop;
@Column("text", { nullable: true })
status: string;
status: string | null;
@Column("float", { default: 0 })
progress: number;
@@ -61,6 +61,9 @@ export class Game {
@JoinColumn()
repack: Repack;
@Column("boolean", { default: false })
isDeleted: boolean;
@CreateDateColumn()
createdAt: Date;

View File

@@ -16,8 +16,6 @@ const getGames = async (
let i = 0 + cursor;
if (!steamGames.length) return [];
while (results.length < take) {
const game = steamGames[i];
const repacks = searchRepacks(game.name);

View File

@@ -1,10 +1,11 @@
import checkDiskSpace from "check-disk-space";
import { registerEvent } from "../register-event";
import { getDownloadsPath } from "../helpers/get-downloads-path";
const getDiskFreeSpace = async (_event: Electron.IpcMainInvokeEvent) =>
checkDiskSpace(await getDownloadsPath());
const getDiskFreeSpace = async (
_event: Electron.IpcMainInvokeEvent,
path: string
) => checkDiskSpace(path);
registerEvent(getDiskFreeSpace, {
name: "getDiskFreeSpace",

View File

@@ -7,8 +7,8 @@ import { formatName, getSteamAppAsset, repackerFormatter } from "@main/helpers";
import { stateManager } from "@main/state-manager";
const { Index } = flexSearch;
const repacksIndex = new Index({ tokenize: "strict" });
const steamGamesIndex = new Index({ tokenize: "forward" });
const repacksIndex = new Index();
const steamGamesIndex = new Index({ tokenize: "reverse" });
const repacks = stateManager.getValue("repacks");
const steamGames = stateManager.getValue("steamGames");
@@ -21,8 +21,6 @@ for (let i = 0; i < repacks.length; i++) {
repacksIndex.add(i, formatName(formatter(repack.title)));
}
console.log(true);
for (let i = 0; i < steamGames.length; i++) {
const steamGame = steamGames[i];
steamGamesIndex.add(i, formatName(steamGame.name));

View File

@@ -17,6 +17,7 @@ import "./library/get-repackers-friendly-names";
import "./library/open-game";
import "./library/open-game-installer";
import "./library/remove-game";
import "./library/remove-game-from-library";
import "./misc/get-or-cache-image";
import "./misc/open-external";
import "./misc/show-open-dialog";
@@ -24,6 +25,7 @@ import "./torrenting/cancel-game-download";
import "./torrenting/pause-game-download";
import "./torrenting/resume-game-download";
import "./torrenting/start-game-download";
import "./torrenting/remove-game-from-download";
import "./user-preferences/get-user-preferences";
import "./user-preferences/update-user-preferences";

View File

@@ -13,15 +13,34 @@ const addGameToLibrary = async (
gameShop: GameShop,
executablePath: string
) => {
const iconUrl = await getImageBase64(await getSteamGameIconUrl(objectID));
return gameRepository.insert({
title,
iconUrl,
objectID,
shop: gameShop,
executablePath,
const game = await gameRepository.findOne({
where: {
objectID,
},
});
if (game) {
return gameRepository.update(
{
id: game.id,
},
{
shop: gameShop,
executablePath,
isDeleted: false,
}
);
} else {
const iconUrl = await getImageBase64(await getSteamGameIconUrl(objectID));
return gameRepository.insert({
title,
iconUrl,
objectID,
shop: gameShop,
executablePath,
});
}
};
registerEvent(addGameToLibrary, {

View File

@@ -22,7 +22,10 @@ const deleteGameFolder = async (
if (!game) return;
if (game.folderName) {
const folderPath = path.join(await getDownloadsPath(), game.folderName);
const folderPath = path.join(
game.downloadPath ?? (await getDownloadsPath()),
game.folderName
);
if (fs.existsSync(folderPath)) {
return new Promise((resolve, reject) => {

View File

@@ -9,6 +9,7 @@ const getGameByObjectID = async (
gameRepository.findOne({
where: {
objectID,
isDeleted: false,
},
relations: {
repack: true,

View File

@@ -5,9 +5,12 @@ import { searchRepacks } from "../helpers/search-games";
import { registerEvent } from "../register-event";
import { sortBy } from "lodash-es";
const getLibrary = async (_event: Electron.IpcMainInvokeEvent) =>
const getLibrary = async () =>
gameRepository
.find({
where: {
isDeleted: false,
},
order: {
createdAt: "desc",
},

View File

@@ -1,7 +1,7 @@
import { registerEvent } from "../register-event";
import { stateManager } from "@main/state-manager";
const getRepackersFriendlyNames = async (_event: Electron.IpcMainInvokeEvent) =>
const getRepackersFriendlyNames = async () =>
stateManager.getValue("repackersFriendlyNames").reduce((prev, next) => {
return { ...prev, [next.name]: next.friendlyName };
}, {});

View File

@@ -23,7 +23,7 @@ const openGameInstaller = async (
);
if (!fs.existsSync(gamePath)) {
await gameRepository.delete({ id: gameId });
await gameRepository.update({ id: gameId }, { status: null });
return true;
}

View File

@@ -0,0 +1,13 @@
import { registerEvent } from "../register-event";
import { gameRepository } from "../../repository";
const removeGameFromLibrary = async (
_event: Electron.IpcMainInvokeEvent,
gameId: number
) => {
gameRepository.update({ id: gameId }, { isDeleted: true });
};
registerEvent(removeGameFromLibrary, {
name: "removeGameFromLibrary",
});

View File

@@ -25,14 +25,13 @@ const cancelGameDownload = async (
if (!game) return;
gameRepository
await gameRepository
.update(
{
id: game.id,
},
{
status: GameStatus.Cancelled,
downloadPath: null,
bytesDownloaded: 0,
progress: 0,
}

View File

@@ -0,0 +1,34 @@
import { GameStatus } from "@main/constants";
import { gameRepository } from "@main/repository";
import { registerEvent } from "../register-event";
const removeGameFromDownload = async (
_event: Electron.IpcMainInvokeEvent,
gameId: number
) => {
const game = await gameRepository.findOne({
where: {
id: gameId,
status: GameStatus.Cancelled,
},
});
if (!game) return;
gameRepository.update(
{
id: game.id,
},
{
status: null,
downloadPath: null,
bytesDownloaded: 0,
progress: 0,
}
);
};
registerEvent(removeGameFromDownload, {
name: "removeGameFromDownload",
});

View File

@@ -5,7 +5,6 @@ import { GameStatus } from "@main/constants";
import { registerEvent } from "../register-event";
import type { GameShop } from "@types";
import { getDownloadsPath } from "../helpers/get-downloads-path";
import { getImageBase64 } from "@main/helpers";
import { In } from "typeorm";
@@ -14,7 +13,8 @@ const startGameDownload = async (
repackId: number,
objectID: string,
title: string,
gameShop: GameShop
gameShop: GameShop,
downloadPath: string
) => {
const [game, repack] = await Promise.all([
gameRepository.findOne({
@@ -37,8 +37,6 @@ const startGameDownload = async (
writePipe.write({ action: "pause" });
const downloadsPath = game?.downloadPath ?? (await getDownloadsPath());
await gameRepository.update(
{
status: In([
@@ -57,8 +55,9 @@ const startGameDownload = async (
},
{
status: GameStatus.DownloadingMetadata,
downloadPath: downloadsPath,
downloadPath: downloadPath,
repack: { id: repackId },
isDeleted: false,
}
);
@@ -66,18 +65,11 @@ const startGameDownload = async (
action: "start",
game_id: game.id,
magnet: repack.magnet,
save_path: downloadsPath,
save_path: downloadPath,
});
game.status = GameStatus.DownloadingMetadata;
writePipe.write({
action: "start",
game_id: game.id,
magnet: repack.magnet,
save_path: downloadsPath,
});
return game;
} else {
const iconUrl = await getImageBase64(await getSteamGameIconUrl(objectID));
@@ -88,7 +80,7 @@ const startGameDownload = async (
objectID,
shop: gameShop,
status: GameStatus.DownloadingMetadata,
downloadPath: downloadsPath,
downloadPath: downloadPath,
repack: { id: repackId },
});
@@ -96,7 +88,7 @@ const startGameDownload = async (
action: "start",
game_id: createdGame.id,
magnet: repack.magnet,
save_path: downloadsPath,
save_path: downloadPath,
});
const { repack: _, ...rest } = createdGame;

View File

@@ -2,6 +2,7 @@ import { app, BrowserWindow } from "electron";
import { init } from "@sentry/electron/main";
import i18n from "i18next";
import path from "node:path";
import { electronApp, optimizer } from "@electron-toolkit/utils";
import { resolveDatabaseUpdates, WindowManager } from "@main/services";
import { dataSource } from "@main/data-source";
import * as resources from "@locales";
@@ -49,8 +50,10 @@ if (process.defaultApp) {
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
electronApp.setAppUserModelId("site.hydralauncher.hydra");
dataSource.initialize().then(async () => {
// await resolveDatabaseUpdates();
await resolveDatabaseUpdates();
await import("./main");
@@ -59,10 +62,14 @@ app.whenReady().then(() => {
});
WindowManager.createMainWindow();
// WindowManager.createSystemTray(userPreferences?.language || "en");
WindowManager.createSystemTray(userPreferences?.language || "en");
});
});
app.on("browser-window-created", (_, window) => {
optimizer.watchWindowShortcuts(window);
});
app.on("second-instance", (_event, commandLine) => {
// Someone tried to run a second instance, we should focus our window.
if (WindowManager.mainWindow) {

View File

@@ -4,7 +4,7 @@ import {
getNewGOGGames,
getNewRepacksFromCPG,
getNewRepacksFromUser,
getNewRepacksFromXatab,
// getNewRepacksFromXatab,
// getNewRepacksFromOnlineFix,
readPipe,
startProcessWatcher,
@@ -73,9 +73,9 @@ const checkForNewRepacks = async () => {
getNewGOGGames(
existingRepacks.filter((repack) => repack.repacker === "GOG")
),
getNewRepacksFromXatab(
existingRepacks.filter((repack) => repack.repacker === "Xatab")
),
// getNewRepacksFromXatab(
// existingRepacks.filter((repack) => repack.repacker === "Xatab")
// ),
getNewRepacksFromCPG(
existingRepacks.filter((repack) => repack.repacker === "CPG")
),

View File

@@ -1,11 +1,27 @@
import { BrowserWindow, Menu, Tray, app } from "electron";
import { electronApp, optimizer, is } from "@electron-toolkit/utils";
import { is } from "@electron-toolkit/utils";
import { t } from "i18next";
import path from "node:path";
import icon from "../../../resources/icon.png?asset";
import trayIcon from "../../../resources/icon.png?asset";
export class WindowManager {
public static mainWindow: Electron.BrowserWindow | null = null;
private static loadURL(hash = "") {
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
this.mainWindow.loadURL(
`${process.env["ELECTRON_RENDERER_URL"]}#/${hash}`
);
} else {
this.mainWindow.loadFile(path.join(__dirname, "../renderer/index.html"), {
hash,
});
}
}
public static createMainWindow() {
// Create the browser window.
this.mainWindow = new BrowserWindow({
@@ -14,7 +30,7 @@ export class WindowManager {
minWidth: 1024,
minHeight: 540,
titleBarStyle: "hidden",
// icon: path.join(__dirname, "..", "..", "images", "icon.png"),
...(process.platform === "linux" ? { icon } : {}),
trafficLightPosition: { x: 16, y: 16 },
titleBarOverlay: {
symbolColor: "#DADBE1",
@@ -27,42 +43,24 @@ export class WindowManager {
},
});
this.loadURL();
this.mainWindow.removeMenu();
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
this.mainWindow.loadURL(process.env["ELECTRON_RENDERER_URL"]);
} else {
this.mainWindow.loadFile(path.join(__dirname, "../renderer/index.html"));
}
this.mainWindow.webContents.on("did-finish-load", () => {
if (!app.isPackaged) {
// Open the DevTools.
this.mainWindow.webContents.openDevTools();
}
});
this.mainWindow.on("close", () => {
WindowManager.mainWindow.setProgressBar(-1);
});
}
public static redirect(path: string) {
public static redirect(hash: string) {
if (!this.mainWindow) this.createMainWindow();
this.mainWindow.loadURL(`${MAIN_WINDOW_WEBPACK_ENTRY}#${path}`);
this.loadURL(hash);
if (this.mainWindow.isMinimized()) this.mainWindow.restore();
this.mainWindow.focus();
}
public static createSystemTray(language: string) {
const tray = new Tray(
app.isPackaged
? path.join(process.resourcesPath, "icon_tray.png")
: path.join(__dirname, "..", "..", "resources", "icon_tray.png")
);
const tray = new Tray(trayIcon);
const contextMenu = Menu.buildFromTemplate([
{