feat: batch games and code refactor

This commit is contained in:
Zamitto 2024-06-16 23:43:18 -03:00
parent da5cc11bff
commit 7fc376b47f
10 changed files with 161 additions and 106 deletions

View File

@ -23,7 +23,7 @@ export class Game {
objectID: string; objectID: string;
@Column("text", { unique: true, nullable: true }) @Column("text", { unique: true, nullable: true })
remoteId: string; remoteId: string | null;
@Column("text") @Column("text")
title: string; title: string;

View File

@ -6,7 +6,7 @@ import type { GameShop } from "@types";
import { getFileBase64, getSteamAppAsset } from "@main/helpers"; import { getFileBase64, getSteamAppAsset } from "@main/helpers";
import { steamGamesWorker } from "@main/workers"; import { steamGamesWorker } from "@main/workers";
import { HydraApi } from "@main/services/hydra-api"; import { createGame } from "@main/services/library-sync";
const addGameToLibrary = async ( const addGameToLibrary = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
@ -53,12 +53,7 @@ const addGameToLibrary = async (
const game = await gameRepository.findOne({ where: { objectID } }); const game = await gameRepository.findOne({ where: { objectID } });
HydraApi.post("/games", { createGame(game!).then((response) => {
objectId: objectID,
playTimeInMilliseconds: game?.playTimeInMilliseconds,
shop,
lastTimePlayed: game?.lastTimePlayed,
}).then((response) => {
const { const {
id: remoteId, id: remoteId,
playTimeInMilliseconds, playTimeInMilliseconds,

View File

@ -1,22 +1,16 @@
import { import { DownloadManager, RepacksManager, startMainLoop } from "./services";
DownloadManager,
RepacksManager,
logger,
startMainLoop,
} from "./services";
import { import {
downloadQueueRepository, downloadQueueRepository,
gameRepository,
repackRepository, repackRepository,
userPreferencesRepository, userPreferencesRepository,
} from "./repository"; } from "./repository";
import { UserPreferences } from "./entity"; import { UserPreferences } from "./entity";
import { RealDebridClient } from "./services/real-debrid"; import { RealDebridClient } from "./services/real-debrid";
import { fetchDownloadSourcesAndUpdate, getSteamAppAsset } from "./helpers"; import { fetchDownloadSourcesAndUpdate } from "./helpers";
import { publishNewRepacksNotifications } from "./services/notifications"; import { publishNewRepacksNotifications } from "./services/notifications";
import { MoreThan } from "typeorm"; import { MoreThan } from "typeorm";
import { HydraApi } from "./services/hydra-api"; import { HydraApi } from "./services/hydra-api";
import { steamGamesWorker } from "./workers"; import { getRemoteGames } from "./services/library-sync";
startMainLoop(); startMainLoop();
@ -28,69 +22,11 @@ const loadState = async (userPreferences: UserPreferences | null) => {
if (userPreferences?.realDebridApiToken) if (userPreferences?.realDebridApiToken)
RealDebridClient.authorize(userPreferences?.realDebridApiToken); RealDebridClient.authorize(userPreferences?.realDebridApiToken);
HydraApi.setupApi() HydraApi.setupApi().then(async () => {
.then(async () => { if (HydraApi.isLoggedIn()) {
if (HydraApi.isLoggedIn()) { getRemoteGames();
const games = await HydraApi.get("/games"); }
});
for (const game of games.data) {
const localGame = await gameRepository.findOne({
where: {
objectID: game.objectId,
},
});
if (localGame) {
const updatedLastTimePlayed =
localGame.lastTimePlayed == null ||
new Date(game.lastTimePlayed) > localGame.lastTimePlayed
? new Date(game.lastTimePlayed)
: localGame.lastTimePlayed;
const updatedPlayTime =
localGame.playTimeInMilliseconds < game.playTimeInMilliseconds
? game.playTimeInMilliseconds
: localGame.playTimeInMilliseconds;
gameRepository.update(
{
objectID: game.objectId,
shop: "steam",
lastTimePlayed: updatedLastTimePlayed,
playTimeInMilliseconds: updatedPlayTime,
},
{ remoteId: game.id }
);
} else {
const steamGame = await steamGamesWorker.run(
Number(game.objectId),
{
name: "getById",
}
);
if (steamGame) {
const iconUrl = steamGame?.clientIcon
? getSteamAppAsset("icon", game.objectId, steamGame.clientIcon)
: null;
gameRepository.insert({
objectID: game.objectId,
title: steamGame?.name,
remoteId: game.id,
shop: game.shop,
iconUrl,
lastTimePlayed: game.lastTimePlayed,
playTimeInMilliseconds: game.playTimeInMilliseconds,
});
}
}
}
}
})
.catch((err) => {
logger.error("erro api GET: /games", err);
});
const [nextQueueItem] = await downloadQueueRepository.find({ const [nextQueueItem] = await downloadQueueRepository.find({
order: { order: {

View File

@ -2,6 +2,7 @@ import { userAuthRepository } from "@main/repository";
import axios, { AxiosError, AxiosInstance } from "axios"; import axios, { AxiosError, AxiosInstance } from "axios";
import { WindowManager } from "./window-manager"; import { WindowManager } from "./window-manager";
import url from "url"; import url from "url";
import { getRemoteGames, uploadBatchGames } from "./library-sync";
export class HydraApi { export class HydraApi {
private static instance: AxiosInstance; private static instance: AxiosInstance;
@ -49,6 +50,9 @@ export class HydraApi {
if (WindowManager.mainWindow) { if (WindowManager.mainWindow) {
WindowManager.mainWindow.webContents.send("on-signin"); WindowManager.mainWindow.webContents.send("on-signin");
await uploadBatchGames();
await getRemoteGames();
} }
} }

View File

@ -0,0 +1,11 @@
import { Game } from "@main/entity";
import { HydraApi } from "../hydra-api";
export const createGame = async (game: Game) => {
return HydraApi.post(`/games`, {
objectId: game.objectID,
playTimeInMilliseconds: Math.round(game.playTimeInMilliseconds),
shop: game.shop,
lastTimePlayed: game.lastTimePlayed,
});
};

View File

@ -0,0 +1,69 @@
import { gameRepository } from "@main/repository";
import { HydraApi } from "../hydra-api";
import { steamGamesWorker } from "@main/workers";
import { getSteamAppAsset } from "@main/helpers";
import { logger } from "../logger";
import { AxiosError } from "axios";
export const getRemoteGames = async () => {
try {
const games = await HydraApi.get("/games");
for (const game of games.data) {
const localGame = await gameRepository.findOne({
where: {
objectID: game.objectId,
},
});
if (localGame) {
const updatedLastTimePlayed =
localGame.lastTimePlayed == null ||
new Date(game.lastTimePlayed) > localGame.lastTimePlayed
? new Date(game.lastTimePlayed)
: localGame.lastTimePlayed;
const updatedPlayTime =
localGame.playTimeInMilliseconds < game.playTimeInMilliseconds
? game.playTimeInMilliseconds
: localGame.playTimeInMilliseconds;
gameRepository.update(
{
objectID: game.objectId,
shop: "steam",
lastTimePlayed: updatedLastTimePlayed,
playTimeInMilliseconds: updatedPlayTime,
},
{ remoteId: game.id }
);
} else {
const steamGame = await steamGamesWorker.run(Number(game.objectId), {
name: "getById",
});
if (steamGame) {
const iconUrl = steamGame?.clientIcon
? getSteamAppAsset("icon", game.objectId, steamGame.clientIcon)
: null;
gameRepository.insert({
objectID: game.objectId,
title: steamGame?.name,
remoteId: game.id,
shop: game.shop,
iconUrl,
lastTimePlayed: game.lastTimePlayed,
playTimeInMilliseconds: game.playTimeInMilliseconds,
});
}
}
}
} catch (err) {
if (err instanceof AxiosError) {
logger.error("getRemoteGames", err.response, err.message);
} else {
logger.error("getRemoteGames", err);
}
}
};

View File

@ -0,0 +1,4 @@
export * from "./get-remote-games";
export * from "./upload-batch-games";
export * from "./update-game-playtime";
export * from "./create-game";

View File

@ -0,0 +1,13 @@
import { Game } from "@main/entity";
import { HydraApi } from "../hydra-api";
export const updateGamePlaytime = async (
game: Game,
delta: number,
lastTimePlayed: Date
) => {
return HydraApi.put(`/games/${game.remoteId}`, {
playTimeDeltaInSeconds: delta,
lastTimePlayed,
});
};

View File

@ -0,0 +1,35 @@
import { gameRepository } from "@main/repository";
import { chunk } from "lodash-es";
import { IsNull } from "typeorm";
import { HydraApi } from "../hydra-api";
import { logger } from "../logger";
import { AxiosError } from "axios";
export const uploadBatchGames = async () => {
try {
const games = await gameRepository.find({
where: { remoteId: IsNull(), isDeleted: false },
});
const gamesChunks = chunk(games, 200);
for (const chunk of gamesChunks) {
await HydraApi.post(
"/games/batch",
chunk.map((game) => {
return {
objectId: game.objectID,
playTimeInMilliseconds: Math.round(game.playTimeInMilliseconds),
shop: game.shop,
lastTimePlayed: game.lastTimePlayed,
};
})
);
}
} catch (err) {
if (err instanceof AxiosError) {
logger.error("uploadBatchGames", err.response, err.message);
} else {
logger.error("uploadBatchGames", err);
}
}
};

View File

@ -4,7 +4,7 @@ import { IsNull, Not } from "typeorm";
import { gameRepository } from "@main/repository"; import { gameRepository } from "@main/repository";
import { getProcesses } from "@main/helpers"; import { getProcesses } from "@main/helpers";
import { WindowManager } from "./window-manager"; import { WindowManager } from "./window-manager";
import { HydraApi } from "./hydra-api"; import { createGame, updateGamePlaytime } from "./library-sync";
const gamesPlaytime = new Map< const gamesPlaytime = new Map<
number, number,
@ -61,20 +61,14 @@ export const watchProcesses = async () => {
}); });
} else { } else {
if (game.remoteId) { if (game.remoteId) {
HydraApi.put(`/games/${game.remoteId}`, { updateGamePlaytime(game, 0, new Date());
playTimeDeltaInMilliseconds: 0,
lastTimePlayed: new Date(),
});
} else { } else {
HydraApi.post("/games", { createGame({ ...game, lastTimePlayed: new Date() }).then(
objectId: game.objectID, (response) => {
playTimeInMilliseconds: Math.round(game.playTimeInMilliseconds), const { id: remoteId } = response.data;
shop: game.shop, gameRepository.update({ objectID: game.objectID }, { remoteId });
lastTimePlayed: new Date(), }
}).then((response) => { );
const { id: remoteId } = response.data;
gameRepository.update({ objectID: game.objectID }, { remoteId });
});
} }
gamesPlaytime.set(game.id, { gamesPlaytime.set(game.id, {
@ -87,19 +81,13 @@ export const watchProcesses = async () => {
gamesPlaytime.delete(game.id); gamesPlaytime.delete(game.id);
if (game.remoteId) { if (game.remoteId) {
HydraApi.put(`/games/${game.remoteId}`, { updateGamePlaytime(
playTimeInMilliseconds: Math.round( game,
performance.now() - gamePlaytime.firstTick performance.now() - gamePlaytime.firstTick,
), game.lastTimePlayed!
lastTimePlayed: game.lastTimePlayed, );
});
} else { } else {
HydraApi.post("/games", { createGame(game).then((response) => {
objectId: game.objectID,
playTimeInMilliseconds: Math.round(game.playTimeInMilliseconds),
shop: game.shop,
lastTimePlayed: game.lastTimePlayed,
}).then((response) => {
const { id: remoteId } = response.data; const { id: remoteId } = response.data;
gameRepository.update({ objectID: game.objectID }, { remoteId }); gameRepository.update({ objectID: game.objectID }, { remoteId });
}); });