feat: optimizations

This commit is contained in:
Zamitto 2024-10-21 04:28:04 -03:00
parent fd5262cd6e
commit 27e8a0820f
6 changed files with 167 additions and 116 deletions

View file

@ -40,10 +40,8 @@ const watchAchievementsWindows = async () => {
); );
} }
if (!gameAchievementFiles.length) continue;
for (const file of gameAchievementFiles) { for (const file of gameAchievementFiles) {
await compareFile(game, file); compareFile(game, file);
} }
} }
}; };
@ -56,8 +54,6 @@ const watchAchievementsWithWine = async () => {
}, },
}); });
if (games.length === 0) return;
for (const game of games) { for (const game of games) {
const gameAchievementFiles = findAchievementFiles(game); const gameAchievementFiles = findAchievementFiles(game);
const achievementFileInsideDirectory = const achievementFileInsideDirectory =
@ -65,10 +61,8 @@ const watchAchievementsWithWine = async () => {
gameAchievementFiles.push(...achievementFileInsideDirectory); gameAchievementFiles.push(...achievementFileInsideDirectory);
if (!gameAchievementFiles.length) continue;
for (const file of gameAchievementFiles) { for (const file of gameAchievementFiles) {
await compareFile(game, file); compareFile(game, file);
} }
} }
}; };
@ -104,7 +98,7 @@ const compareFile = (game: Game, file: AchievementFile) => {
const previousStat = fileStats.get(file.filePath); const previousStat = fileStats.get(file.filePath);
fileStats.set(file.filePath, currentStat.mtimeMs); fileStats.set(file.filePath, currentStat.mtimeMs);
if (!previousStat) { if (!previousStat || previousStat === -1) {
if (currentStat.mtimeMs) { if (currentStat.mtimeMs) {
achievementsLogger.log( achievementsLogger.log(
"First change in file", "First change in file",
@ -153,17 +147,55 @@ const processAchievementFileDiff = async (
export class AchievementWatcherManager { export class AchievementWatcherManager {
private static hasFinishedMergingWithRemote = false; private static hasFinishedMergingWithRemote = false;
public static watchAchievements = async () => { public static watchAchievements = () => {
if (!this.hasFinishedMergingWithRemote) return; if (!this.hasFinishedMergingWithRemote) return;
if (process.platform === "win32") { if (process.platform === "win32") {
return watchAchievementsWindows(); return watchAchievementsWindows();
} }
watchAchievementsWithWine(); return watchAchievementsWithWine();
}; };
public static preSearchAchievements = async () => { private static preProcessGameAchievementFiles = (
game: Game,
gameAchievementFiles: AchievementFile[]
) => {
const unlockedAchievements: UnlockedAchievement[] = [];
for (const achievementFile of gameAchievementFiles) {
const parsedAchievements = parseAchievementFile(
achievementFile.filePath,
achievementFile.type
);
try {
const currentStat = fs.statSync(achievementFile.filePath);
fileStats.set(achievementFile.filePath, currentStat.mtimeMs);
} catch {
fileStats.set(achievementFile.filePath, -1);
}
if (parsedAchievements.length) {
unlockedAchievements.push(...parsedAchievements);
achievementsLogger.log(
"Achievement file for",
game.title,
achievementFile.filePath,
parsedAchievements
);
}
}
return mergeAchievements(
game.objectID,
"steam",
unlockedAchievements,
false
);
};
private static preSearchAchievementsWindows = async () => {
const games = await gameRepository.find({ const games = await gameRepository.find({
where: { where: {
isDeleted: false, isDeleted: false,
@ -172,20 +204,19 @@ export class AchievementWatcherManager {
const gameAchievementFilesMap = findAllAchievementFiles(); const gameAchievementFilesMap = findAllAchievementFiles();
await Promise.all( return Promise.all(
games.map(async (game) => { games.map((game) => {
gameAchievementRepository gameAchievementRepository
.findOne({ .findOne({
where: { objectId: game.objectID, shop: "steam" }, where: { objectId: game.objectID, shop: game.shop },
}) })
.then((localAchievements) => { .then((localAchievements) => {
if (!localAchievements || !localAchievements.achievements) { if (!localAchievements || !localAchievements.achievements) {
getGameAchievementData(game.objectID, "steam"); getGameAchievementData(game.objectID, game.shop);
} }
}); });
const gameAchievementFiles: AchievementFile[] = []; const gameAchievementFiles: AchievementFile[] = [];
const unlockedAchievements: UnlockedAchievement[] = [];
for (const objectId of getAlternativeObjectIds(game.objectID)) { for (const objectId of getAlternativeObjectIds(game.objectID)) {
gameAchievementFiles.push( gameAchievementFiles.push(
@ -197,39 +228,48 @@ export class AchievementWatcherManager {
); );
} }
for (const achievementFile of gameAchievementFiles) { return this.preProcessGameAchievementFiles(game, gameAchievementFiles);
const parsedAchievements = parseAchievementFile(
achievementFile.filePath,
achievementFile.type
);
try {
const currentStat = fs.statSync(achievementFile.filePath);
fileStats.set(achievementFile.filePath, currentStat.mtimeMs);
} catch {
fileStats.set(achievementFile.filePath, -1);
}
if (parsedAchievements.length) {
unlockedAchievements.push(...parsedAchievements);
achievementsLogger.log(
"Achievement file for",
game.title,
achievementFile.filePath,
parsedAchievements
);
}
await mergeAchievements(
game.objectID,
"steam",
unlockedAchievements,
false
);
}
}) })
); );
};
private static preSearchAchievementsWithWine = async () => {
const games = await gameRepository.find({
where: {
isDeleted: false,
winePrefixPath: Not(IsNull()),
},
});
return Promise.all(
games.map((game) => {
gameAchievementRepository
.findOne({
where: { objectId: game.objectID, shop: game.shop },
})
.then((localAchievements) => {
if (!localAchievements || !localAchievements.achievements) {
getGameAchievementData(game.objectID, game.shop);
}
});
const gameAchievementFiles = findAchievementFiles(game);
const achievementFileInsideDirectory =
findAchievementFileInExecutableDirectory(game);
gameAchievementFiles.push(...achievementFileInsideDirectory);
return this.preProcessGameAchievementFiles(game, gameAchievementFiles);
})
);
};
public static preSearchAchievements = async () => {
if (process.platform === "win32") {
await this.preSearchAchievementsWindows();
} else {
await this.preSearchAchievementsWithWine();
}
this.hasFinishedMergingWithRemote = true; this.hasFinishedMergingWithRemote = true;
}; };

View file

@ -11,7 +11,8 @@ import { getUnlockedAchievements } from "@main/events/user/get-unlocked-achievem
const saveAchievementsOnLocal = async ( const saveAchievementsOnLocal = async (
objectId: string, objectId: string,
shop: GameShop, shop: GameShop,
achievements: any[] achievements: any[],
sendUpdateEvent: boolean
) => { ) => {
return gameAchievementRepository return gameAchievementRepository
.upsert( .upsert(
@ -23,6 +24,8 @@ const saveAchievementsOnLocal = async (
["objectId", "shop"] ["objectId", "shop"]
) )
.then(() => { .then(() => {
if (!sendUpdateEvent) return;
return getUnlockedAchievements(objectId, shop) return getUnlockedAchievements(objectId, shop)
.then((achievements) => { .then((achievements) => {
WindowManager.mainWindow?.webContents.send( WindowManager.mainWindow?.webContents.send(
@ -133,13 +136,24 @@ export const mergeAchievements = async (
return saveAchievementsOnLocal( return saveAchievementsOnLocal(
response.objectId, response.objectId,
response.shop, response.shop,
response.achievements response.achievements,
publishNotification
); );
}) })
.catch(() => { .catch(() => {
return saveAchievementsOnLocal(objectId, shop, mergedLocalAchievements); return saveAchievementsOnLocal(
objectId,
shop,
mergedLocalAchievements,
publishNotification
);
}); });
} }
return saveAchievementsOnLocal(objectId, shop, mergedLocalAchievements); return saveAchievementsOnLocal(
objectId,
shop,
mergedLocalAchievements,
publishNotification
);
}; };

View file

@ -14,6 +14,7 @@ import {
} from "@shared"; } from "@shared";
// import { omit } from "lodash-es"; // import { omit } from "lodash-es";
import { appVersion } from "@main/constants"; import { appVersion } from "@main/constants";
import { omit } from "lodash-es";
interface HydraApiOptions { interface HydraApiOptions {
needsAuth?: boolean; needsAuth?: boolean;
@ -24,7 +25,7 @@ export class HydraApi {
private static instance: AxiosInstance; private static instance: AxiosInstance;
private static readonly EXPIRATION_OFFSET_IN_MS = 1000 * 60 * 5; // 5 minutes private static readonly EXPIRATION_OFFSET_IN_MS = 1000 * 60 * 5; // 5 minutes
private static readonly ADD_LOG_INTERCEPTOR = true; private static readonly ADD_LOG_INTERCEPTOR = false;
private static secondsToMilliseconds = (seconds: number) => seconds * 1000; private static secondsToMilliseconds = (seconds: number) => seconds * 1000;
@ -109,59 +110,59 @@ export class HydraApi {
}); });
if (this.ADD_LOG_INTERCEPTOR) { if (this.ADD_LOG_INTERCEPTOR) {
// this.instance.interceptors.request.use( this.instance.interceptors.request.use(
// (request) => { (request) => {
// logger.log(" ---- REQUEST -----"); logger.log(" ---- REQUEST -----");
// const data = Array.isArray(request.data) const data = Array.isArray(request.data)
// ? request.data ? request.data
// : omit(request.data, ["refreshToken"]); : omit(request.data, ["refreshToken"]);
// logger.log(request.method, request.url, request.params, data); logger.log(request.method, request.url, request.params, data);
// return request; return request;
// }, },
// (error) => { (error) => {
// logger.error("request error", error); logger.error("request error", error);
// return Promise.reject(error); return Promise.reject(error);
// } }
// ); );
// this.instance.interceptors.response.use( this.instance.interceptors.response.use(
// (response) => { (response) => {
// logger.log(" ---- RESPONSE -----"); logger.log(" ---- RESPONSE -----");
// const data = Array.isArray(response.data) const data = Array.isArray(response.data)
// ? response.data ? response.data
// : omit(response.data, ["username", "accessToken", "refreshToken"]); : omit(response.data, ["username", "accessToken", "refreshToken"]);
// logger.log( logger.log(
// response.status, response.status,
// response.config.method, response.config.method,
// response.config.url, response.config.url,
// data data
// ); );
// return response; return response;
// }, },
// (error) => { (error) => {
// logger.error(" ---- RESPONSE ERROR -----"); logger.error(" ---- RESPONSE ERROR -----");
// const { config } = error; const { config } = error;
// logger.error( logger.error(
// config.method, config.method,
// config.baseURL, config.baseURL,
// config.url, config.url,
// config.headers, config.headers,
// config.data config.data
// ); );
// if (error.response) { if (error.response) {
// logger.error( logger.error(
// "Response", "Response",
// error.response.status, error.response.status,
// error.response.data error.response.data
// ); );
// } else if (error.request) { } else if (error.request) {
// logger.error("Request", error.request); logger.error("Request", error.request);
// } else { } else {
// logger.error("Error", error.message); logger.error("Error", error.message);
// } }
// logger.error(" ----- END RESPONSE ERROR -------"); logger.error(" ----- END RESPONSE ERROR -------");
// return Promise.reject(error); return Promise.reject(error);
// } }
// ); );
} }
const userAuth = await userAuthRepository.findOne({ const userAuth = await userAuthRepository.findOne({

View file

@ -4,7 +4,7 @@ import { IsNull } from "typeorm";
import { HydraApi } from "../hydra-api"; import { HydraApi } from "../hydra-api";
import { mergeWithRemoteGames } from "./merge-with-remote-games"; import { mergeWithRemoteGames } from "./merge-with-remote-games";
import { WindowManager } from "../window-manager"; import { WindowManager } from "../window-manager";
import { AchievementWatcherManager } from "../achievements/AchievementWatcherManager"; import { AchievementWatcherManager } from "../achievements/achievement-watcher-manager";
export const uploadGamesBatch = async () => { export const uploadGamesBatch = async () => {
const games = await gameRepository.find({ const games = await gameRepository.find({
@ -29,7 +29,7 @@ export const uploadGamesBatch = async () => {
await mergeWithRemoteGames(); await mergeWithRemoteGames();
await AchievementWatcherManager.preSearchAchievements(); AchievementWatcherManager.preSearchAchievements();
if (WindowManager.mainWindow) if (WindowManager.mainWindow)
WindowManager.mainWindow.webContents.send("on-library-batch-complete"); WindowManager.mainWindow.webContents.send("on-library-batch-complete");

View file

@ -1,7 +1,7 @@
import { sleep } from "@main/helpers"; import { sleep } from "@main/helpers";
import { DownloadManager } from "./download"; import { DownloadManager } from "./download";
import { watchProcesses } from "./process-watcher"; import { watchProcesses } from "./process-watcher";
import { AchievementWatcherManager } from "./achievements/AchievementWatcherManager"; import { AchievementWatcherManager } from "./achievements/achievement-watcher-manager";
export const startMainLoop = async () => { export const startMainLoop = async () => {
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition

View file

@ -2,7 +2,7 @@ import { useNavigate } from "react-router-dom";
import { PeopleIcon } from "@primer/octicons-react"; import { PeopleIcon } from "@primer/octicons-react";
import * as styles from "./sidebar-profile.css"; import * as styles from "./sidebar-profile.css";
import { useAppSelector, useUserDetails } from "@renderer/hooks"; import { useAppSelector, useUserDetails } from "@renderer/hooks";
import { useEffect, useMemo, useRef } from "react"; import { useEffect, useMemo } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal"; import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
import SteamLogo from "@renderer/assets/steam-logo.svg?react"; import SteamLogo from "@renderer/assets/steam-logo.svg?react";
@ -13,8 +13,6 @@ const LONG_POLLING_INTERVAL = 60_000;
export function SidebarProfile() { export function SidebarProfile() {
const navigate = useNavigate(); const navigate = useNavigate();
const pollingInterval = useRef<NodeJS.Timeout | null>(null);
const { t } = useTranslation("sidebar"); const { t } = useTranslation("sidebar");
const { const {
@ -36,14 +34,12 @@ export function SidebarProfile() {
}; };
useEffect(() => { useEffect(() => {
pollingInterval.current = setInterval(() => { const pollingInterval = setInterval(() => {
syncFriendRequests(); syncFriendRequests();
}, LONG_POLLING_INTERVAL); }, LONG_POLLING_INTERVAL);
return () => { return () => {
if (pollingInterval.current) { clearInterval(pollingInterval);
clearInterval(pollingInterval.current);
}
}; };
}, [syncFriendRequests]); }, [syncFriendRequests]);