feat: adding ota updates

This commit is contained in:
Chubby Granny Chaser
2025-10-13 23:55:33 +01:00
12 changed files with 754 additions and 595 deletions

View File

@@ -22,7 +22,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20.18.0
node-version: 20.18.3
- name: Install dependencies
run: yarn --frozen-lockfile

View File

@@ -17,7 +17,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20.18.0
node-version: 20.18.3
- name: Install dependencies
run: yarn --frozen-lockfile

View File

@@ -6,7 +6,7 @@ concurrency:
on:
push:
branches: main
branches: [main]
jobs:
build:
@@ -23,7 +23,7 @@ jobs:
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20.18.0
node-version: 20.18.3
- name: Install dependencies
run: yarn --frozen-lockfile

View File

@@ -32,13 +32,13 @@
"protoc": "npx protoc --ts_out src/main/generated --proto_path proto proto/*.proto"
},
"dependencies": {
"@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0",
"@fontsource/noto-sans": "^5.1.0",
"@hookform/resolvers": "^3.9.1",
"@electron-toolkit/preload": "^3.0.2",
"@electron-toolkit/utils": "^4.0.0",
"@fontsource/noto-sans": "^5.2.10",
"@hookform/resolvers": "^5.2.2",
"@monaco-editor/react": "^4.6.0",
"@primer/octicons-react": "^19.9.0",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@reduxjs/toolkit": "^2.2.3",
"@tiptap/extension-bold": "^3.6.2",
"@tiptap/extension-italic": "^3.6.2",
@@ -47,8 +47,9 @@
"@tiptap/react": "^3.6.2",
"@tiptap/starter-kit": "^3.6.2",
"auto-launch": "^5.0.6",
"axios": "^1.7.9",
"axios": "^1.12.2",
"axios-cookiejar-support": "^5.0.5",
"check-disk-space": "^3.4.0",
"classic-level": "^2.0.0",
"classnames": "^2.5.1",
"color": "^4.2.3",
@@ -57,8 +58,7 @@
"create-desktop-shortcuts": "^1.11.1",
"date-fns": "^3.6.0",
"dexie": "^4.0.10",
"diskusage": "^1.2.0",
"electron-log": "^5.2.4",
"electron-log": "^5.4.3",
"electron-updater": "^6.6.2",
"embla-carousel-autoplay": "^8.6.0",
"embla-carousel-react": "^8.6.0",
@@ -117,7 +117,7 @@
"@types/winreg": "^1.2.36",
"@types/ws": "^8.18.1",
"@vitejs/plugin-react": "^4.2.1",
"electron": "^32.3.3",
"electron": "^33.4.11",
"electron-builder": "^26.0.12",
"electron-vite": "^3.0.0",
"eslint": "^8.56.0",
@@ -131,8 +131,8 @@
"sass-embedded": "^1.80.6",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"vite": "^5.0.12",
"vite-plugin-svgr": "^4.2.0"
"vite": "5.4.20",
"vite-plugin-svgr": "^4.5.0"
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}

View File

@@ -1,10 +1,13 @@
import disk from "diskusage";
import { DiskUsage } from "@types";
import { registerEvent } from "../register-event";
import checkDiskSpace from "check-disk-space";
const getDiskFreeSpace = async (
_event: Electron.IpcMainInvokeEvent,
path: string
) => disk.check(path);
): Promise<DiskUsage> => {
const result = await checkDiskSpace(path);
return { free: result.free, total: result.size };
};
registerEvent("getDiskFreeSpace", getDiskFreeSpace);

View File

@@ -9,6 +9,7 @@ import {
clearGamesPlaytime,
WindowManager,
Lock,
Aria2,
} from "@main/services";
import resources from "@locales";
import { PythonRPC } from "./services/python-rpc";
@@ -222,6 +223,7 @@ app.on("before-quit", async (e) => {
e.preventDefault();
/* Disconnects libtorrent */
PythonRPC.kill();
Aria2.kill();
await clearGamesPlaytime();
canAppBeClosed = true;
app.quit();

View File

@@ -1,6 +1,7 @@
import path from "node:path";
import cp from "node:child_process";
import { app } from "electron";
import { logger } from "./logger";
export class Aria2 {
private static process: cp.ChildProcess | null = null;
@@ -23,6 +24,9 @@ export class Aria2 {
}
public static kill() {
this.process?.kill();
if (this.process) {
logger.log("Killing aria2 process");
this.process.kill();
}
}
}

View File

@@ -79,11 +79,18 @@ const findGamePathByProcess = async (
const executables = gameExecutables[gameId];
for (const executable of executables) {
const pathSet = processMap.get(executable.exe);
const executablewithoutExtension = executable.exe.replace(/\.exe$/i, "");
const pathSet =
processMap.get(executable.exe) ??
processMap.get(executablewithoutExtension);
if (pathSet) {
for (const path of pathSet) {
if (path.toLowerCase().endsWith(executable.name)) {
if (
path.toLowerCase().endsWith(executable.name) ||
path.toLowerCase().endsWith(executablewithoutExtension)
) {
const gameKey = levelKeys.game("steam", gameId);
const game = await gamesSublevel.get(gameKey);
@@ -124,7 +131,6 @@ const getSystemProcessMap = async () => {
if (!key || !value) return;
const STEAM_COMPAT_DATA_PATH = process.environ?.STEAM_COMPAT_DATA_PATH;
if (STEAM_COMPAT_DATA_PATH) {
winePrefixMap.set(value, STEAM_COMPAT_DATA_PATH);
}

View File

@@ -8,58 +8,65 @@ import { levelKeys } from "@main/level/sublevels";
export const getUserData = async () => {
return HydraApi.get<UserDetails>(`/profile/me`)
.then(async (me) => {
db.get<string, User>(levelKeys.user, { valueEncoding: "json" }).then(
(user) => {
return db.put<string, User>(
levelKeys.user,
{
...user,
id: me.id,
displayName: me.displayName,
profileImageUrl: me.profileImageUrl,
backgroundImageUrl: me.backgroundImageUrl,
subscription: me.subscription,
},
{ valueEncoding: "json" }
);
}
);
try {
const user = await db.get<string, User>(levelKeys.user, {
valueEncoding: "json",
});
await db.put<string, User>(
levelKeys.user,
{
...user,
id: me.id,
displayName: me.displayName,
profileImageUrl: me.profileImageUrl,
backgroundImageUrl: me.backgroundImageUrl,
subscription: me.subscription,
},
{ valueEncoding: "json" }
);
} catch (error) {
logger.error("Failed to update user in DB", error);
}
return me;
})
.catch(async (err) => {
if (err instanceof UserNotLoggedInError) {
return null;
}
logger.error("Failed to get logged user");
const loggedUser = await db.get<string, User>(levelKeys.user, {
valueEncoding: "json",
});
logger.error("Failed to get logged user", err);
if (loggedUser) {
return {
...loggedUser,
username: "",
bio: "",
email: null,
profileVisibility: "PUBLIC" as ProfileVisibility,
quirks: {
backupsPerGameLimit: 0,
},
subscription: loggedUser.subscription
? {
id: loggedUser.subscription.id,
status: loggedUser.subscription.status,
plan: {
id: loggedUser.subscription.plan.id,
name: loggedUser.subscription.plan.name,
},
expiresAt: loggedUser.subscription.expiresAt,
}
: null,
featurebaseJwt: "",
} as UserDetails;
try {
const loggedUser = await db.get<string, User>(levelKeys.user, {
valueEncoding: "json",
});
if (loggedUser) {
return {
...loggedUser,
username: "",
bio: "",
email: null,
profileVisibility: "PUBLIC" as ProfileVisibility,
quirks: {
backupsPerGameLimit: 0,
},
subscription: loggedUser.subscription
? {
id: loggedUser.subscription.id,
status: loggedUser.subscription.status,
plan: {
id: loggedUser.subscription.plan.id,
name: loggedUser.subscription.plan.name,
},
expiresAt: loggedUser.subscription.expiresAt,
}
: null,
featurebaseJwt: "",
} as UserDetails;
}
} catch (dbError) {
logger.error("Failed to read user from DB", dbError);
}
return null;

View File

@@ -30,9 +30,9 @@ import type {
AchievementCustomNotificationPosition,
AchievementNotificationInfo,
Game,
DiskUsage,
} from "@types";
import type { AxiosProgressEvent } from "axios";
import type disk from "diskusage";
declare global {
declare module "*.svg" {
@@ -220,7 +220,7 @@ declare global {
>;
/* Hardware */
getDiskFreeSpace: (path: string) => Promise<disk.DiskUsage>;
getDiskFreeSpace: (path: string) => Promise<DiskUsage>;
checkFolderWritePermission: (path: string) => Promise<boolean>;
/* Cloud save */

View File

@@ -10,6 +10,11 @@ export type HydraCloudFeature =
| "backup"
| "achievements-points";
export interface DiskUsage {
free: number;
total: number;
}
export interface GameRepack {
id: number;
title: string;

1186
yarn.lock

File diff suppressed because it is too large Load Diff