feat: migrating games to level

This commit is contained in:
Chubby Granny Chaser
2025-01-20 10:09:49 +00:00
parent 1f0e195854
commit d760d0139d
47 changed files with 219 additions and 941 deletions

View File

@@ -1,6 +1,4 @@
import { gameRepository } from "@main/repository";
import { parseAchievementFile } from "./parse-achievement-file";
import { Game } from "@main/entity";
import { mergeAchievements } from "./merge-achievements";
import fs, { readdirSync } from "node:fs";
import {
@@ -9,10 +7,9 @@ import {
findAllAchievementFiles,
getAlternativeObjectIds,
} from "./find-achivement-files";
import type { AchievementFile, UnlockedAchievement } from "@types";
import type { AchievementFile, Game, UnlockedAchievement } from "@types";
import { achievementsLogger } from "../logger";
import { Cracker } from "@shared";
import { IsNull, Not } from "typeorm";
import { publishCombinedNewAchievementNotification } from "../notifications";
import { gamesSublevel } from "@main/level";
@@ -47,12 +44,12 @@ const watchAchievementsWindows = async () => {
};
const watchAchievementsWithWine = async () => {
const games = await gameRepository.find({
where: {
isDeleted: false,
winePrefixPath: Not(IsNull()),
},
});
const games = await gamesSublevel
.values()
.all()
.then((games) =>
games.filter((game) => !game.isDeleted && game.winePrefixPath)
);
for (const game of games) {
const gameAchievementFiles = findAchievementFiles(game);
@@ -188,11 +185,10 @@ export class AchievementWatcherManager {
};
private static preSearchAchievementsWindows = async () => {
const games = await gameRepository.find({
where: {
isDeleted: false,
},
});
const games = await gamesSublevel
.values()
.all()
.then((games) => games.filter((game) => !game.isDeleted));
const gameAchievementFilesMap = findAllAchievementFiles();
@@ -200,7 +196,7 @@ export class AchievementWatcherManager {
games.map((game) => {
const gameAchievementFiles: AchievementFile[] = [];
for (const objectId of getAlternativeObjectIds(game.objectID)) {
for (const objectId of getAlternativeObjectIds(game.objectId)) {
gameAchievementFiles.push(
...(gameAchievementFilesMap.get(objectId) || [])
);
@@ -216,11 +212,10 @@ export class AchievementWatcherManager {
};
private static preSearchAchievementsWithWine = async () => {
const games = await gameRepository.find({
where: {
isDeleted: false,
},
});
const games = await gamesSublevel
.values()
.all()
.then((games) => games.filter((game) => !game.isDeleted));
return Promise.all(
games.map((game) => {

View File

@@ -254,7 +254,7 @@ export const findAchievementFiles = (game: Game) => {
for (const cracker of crackers) {
for (const { folderPath, fileLocation } of getPathFromCracker(cracker)) {
for (const objectId of getAlternativeObjectIds(game.objectID)) {
for (const objectId of getAlternativeObjectIds(game.objectId)) {
const filePath = path.join(
game.winePrefixPath ?? "",
folderPath,

View File

@@ -4,8 +4,7 @@ import {
} from "./find-achivement-files";
import { parseAchievementFile } from "./parse-achievement-file";
import { mergeAchievements } from "./merge-achievements";
import type { UnlockedAchievement } from "@types";
import { Game } from "@main/entity";
import type { Game, UnlockedAchievement } from "@types";
export const updateLocalUnlockedAchivements = async (game: Game) => {
const gameAchievementFiles = findAchievementFiles(game);

View File

@@ -1,11 +1,6 @@
import { Game } from "@main/entity";
import { Downloader } from "@shared";
import { WindowManager } from "../window-manager";
import {
downloadQueueRepository,
gameRepository,
userPreferencesRepository,
} from "@main/repository";
import { userPreferencesRepository } from "@main/repository";
import { publishDownloadCompleteNotification } from "../notifications";
import type { Download, DownloadProgress } from "@types";
import { GofileApi, QiwiApi, DatanodesApi } from "../hosters";
@@ -23,7 +18,7 @@ import { logger } from "../logger";
import { downloadsSublevel, levelKeys } from "@main/level";
export class DownloadManager {
private static downloadingGameId: number | null = null;
private static downloadingGameId: string | null = null;
public static async startRPC(
download?: Download,
@@ -34,13 +29,15 @@ export class DownloadManager {
? await this.getDownloadPayload(download).catch(() => undefined)
: undefined,
downloadsToSeed?.map((download) => ({
game_id: game.id,
url: game.uri!,
save_path: game.downloadPath!,
game_id: `${download.shop}-${download.objectId}`,
url: download.uri!,
save_path: download.downloadPath!,
}))
);
this.downloadingGameId = game?.id ?? null;
if (download) {
this.downloadingGameId = `${download.shop}-${download.objectId}`;
}
}
private static async getDownloadStatus() {

View File

@@ -1,9 +1,9 @@
export interface PauseDownloadPayload {
game_id: number;
game_id: string;
}
export interface CancelDownloadPayload {
game_id: number;
game_id: string;
}
export enum LibtorrentStatus {

View File

@@ -1,17 +1,15 @@
import { gameRepository } from "@main/repository";
import { HydraApi } from "../hydra-api";
import { steamGamesWorker } from "@main/workers";
import { steamUrlBuilder } from "@shared";
import { gamesSublevel, levelKeys } from "@main/level";
export const mergeWithRemoteGames = async () => {
return HydraApi.get("/profile/games")
.then(async (response) => {
for (const game of response) {
const localGame = await gameRepository.findOne({
where: {
objectID: game.objectId,
},
});
const localGame = await gamesSublevel.get(
levelKeys.game(game.shop, game.objectId)
);
if (localGame) {
const updatedLastTimePlayed =
@@ -26,17 +24,12 @@ export const mergeWithRemoteGames = async () => {
? game.playTimeInMilliseconds
: localGame.playTimeInMilliseconds;
gameRepository.update(
{
objectID: game.objectId,
shop: "steam",
},
{
remoteId: game.id,
lastTimePlayed: updatedLastTimePlayed,
playTimeInMilliseconds: updatedPlayTime,
}
);
gamesSublevel.put(levelKeys.game(game.shop, game.objectId), {
...localGame,
remoteId: game.id,
lastTimePlayed: updatedLastTimePlayed,
playTimeInMilliseconds: updatedPlayTime,
});
} else {
const steamGame = await steamGamesWorker.run(Number(game.objectId), {
name: "getById",
@@ -47,14 +40,15 @@ export const mergeWithRemoteGames = async () => {
? steamUrlBuilder.icon(game.objectId, steamGame.clientIcon)
: null;
gameRepository.insert({
objectID: game.objectId,
gamesSublevel.put(levelKeys.game(game.shop, game.objectId), {
objectId: game.objectId,
title: steamGame?.name,
remoteId: game.id,
shop: game.shop,
iconUrl,
lastTimePlayed: game.lastTimePlayed,
playTimeInMilliseconds: game.playTimeInMilliseconds,
isDeleted: false,
});
}
}

View File

@@ -1,7 +1,6 @@
import { Notification, app } from "electron";
import { t } from "i18next";
import trayIcon from "@resources/tray-icon.png?asset";
import { Game } from "@main/entity";
import { userPreferencesRepository } from "@main/repository";
import fs from "node:fs";
import axios from "axios";
@@ -11,6 +10,7 @@ import { achievementSoundPath } from "@main/constants";
import icon from "@resources/icon.png?asset";
import { NotificationOptions, toXmlString } from "./xml";
import { logger } from "../logger";
import type { Game } from "@types";
async function downloadImage(url: string | null) {
if (!url) return undefined;

View File

@@ -10,7 +10,7 @@ import { Readable } from "node:stream";
import { app, dialog } from "electron";
interface GamePayload {
game_id: number;
game_id: string;
url: string;
save_path: string;
}

View File

@@ -13,10 +13,11 @@ import i18next, { t } from "i18next";
import path from "node:path";
import icon from "@resources/icon.png?asset";
import trayIcon from "@resources/tray-icon.png?asset";
import { gameRepository, userPreferencesRepository } from "@main/repository";
import { IsNull, Not } from "typeorm";
import { userPreferencesRepository } from "@main/repository";
import { HydraApi } from "./hydra-api";
import UserAgent from "user-agents";
import { gamesSublevel } from "@main/level";
import { slice, sortBy } from "lodash-es";
export class WindowManager {
public static mainWindow: Electron.BrowserWindow | null = null;
@@ -207,17 +208,22 @@ export class WindowManager {
}
const updateSystemTray = async () => {
const games = await gameRepository.find({
where: {
isDeleted: false,
executablePath: Not(IsNull()),
lastTimePlayed: Not(IsNull()),
},
take: 5,
order: {
lastTimePlayed: "DESC",
},
});
const games = await gamesSublevel
.values()
.all()
.then((games) =>
slice(
sortBy(
games.filter(
(game) =>
!game.isDeleted && game.executablePath && game.lastTimePlayed
),
"lastTimePlayed",
"DESC"
),
5
)
);
const recentlyPlayedGames: Array<MenuItemConstructorOptions | MenuItem> =
games.map(({ title, executablePath }) => ({