feat: sync library

This commit is contained in:
Zamitto 2024-06-15 02:15:58 -03:00
parent 79ca354da1
commit da5cc11bff
6 changed files with 167 additions and 7 deletions

View File

@ -22,6 +22,9 @@ export class Game {
@Column("text", { unique: true }) @Column("text", { unique: true })
objectID: string; objectID: string;
@Column("text", { unique: true, nullable: true })
remoteId: string;
@Column("text") @Column("text")
title: string; title: string;

View File

@ -6,6 +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";
const addGameToLibrary = async ( const addGameToLibrary = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
@ -49,6 +50,26 @@ const addGameToLibrary = async (
} }
}); });
} }
const game = await gameRepository.findOne({ where: { objectID } });
HydraApi.post("/games", {
objectId: objectID,
playTimeInMilliseconds: game?.playTimeInMilliseconds,
shop,
lastTimePlayed: game?.lastTimePlayed,
}).then((response) => {
const {
id: remoteId,
playTimeInMilliseconds,
lastTimePlayed,
} = response.data;
gameRepository.update(
{ objectID },
{ remoteId, playTimeInMilliseconds, lastTimePlayed }
);
});
}); });
}; };

View File

@ -1,5 +1,6 @@
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { gameRepository } from "../../repository"; import { gameRepository } from "../../repository";
import { HydraApi } from "@main/services/hydra-api";
const removeGameFromLibrary = async ( const removeGameFromLibrary = async (
_event: Electron.IpcMainInvokeEvent, _event: Electron.IpcMainInvokeEvent,
@ -9,6 +10,12 @@ const removeGameFromLibrary = async (
{ id: gameId }, { id: gameId },
{ isDeleted: true, executablePath: null } { isDeleted: true, executablePath: null }
); );
const game = await gameRepository.findOne({ where: { id: gameId } });
if (game?.remoteId) {
HydraApi.delete(`/games/${game.remoteId}`);
}
}; };
registerEvent("removeGameFromLibrary", removeGameFromLibrary); registerEvent("removeGameFromLibrary", removeGameFromLibrary);

View File

@ -1,15 +1,22 @@
import { DownloadManager, RepacksManager, startMainLoop } from "./services"; import {
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 } from "./helpers"; import { fetchDownloadSourcesAndUpdate, getSteamAppAsset } 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";
startMainLoop(); startMainLoop();
@ -21,7 +28,69 @@ 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 () => {
if (HydraApi.isLoggedIn()) {
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

@ -14,6 +14,10 @@ export class HydraApi {
expirationTimestamp: 0, expirationTimestamp: 0,
}; };
static isLoggedIn() {
return this.userAuth.authToken !== "";
}
static async handleExternalAuth(auth: string) { static async handleExternalAuth(auth: string) {
const { payload } = url.parse(auth, true).query; const { payload } = url.parse(auth, true).query;
@ -140,4 +144,9 @@ export class HydraApi {
await this.revalidateAccessTokenIfExpired(); await this.revalidateAccessTokenIfExpired();
return this.instance.patch(url, data, this.getAxiosConfig()); return this.instance.patch(url, data, this.getAxiosConfig());
} }
static async delete(url: string) {
await this.revalidateAccessTokenIfExpired();
return this.instance.delete(url, this.getAxiosConfig());
}
} }

View File

@ -4,8 +4,12 @@ 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";
const gamesPlaytime = new Map<number, number>(); const gamesPlaytime = new Map<
number,
{ lastTick: number; firstTick: number }
>();
export const watchProcesses = async () => { export const watchProcesses = async () => {
const games = await gameRepository.find({ const games = await gameRepository.find({
@ -37,7 +41,9 @@ export const watchProcesses = async () => {
if (gameProcess) { if (gameProcess) {
if (gamesPlaytime.has(game.id)) { if (gamesPlaytime.has(game.id)) {
const zero = gamesPlaytime.get(game.id) ?? 0; const gamePlaytime = gamesPlaytime.get(game.id)!;
const zero = gamePlaytime.lastTick;
const delta = performance.now() - zero; const delta = performance.now() - zero;
if (WindowManager.mainWindow) { if (WindowManager.mainWindow) {
@ -48,12 +54,57 @@ export const watchProcesses = async () => {
playTimeInMilliseconds: game.playTimeInMilliseconds + delta, playTimeInMilliseconds: game.playTimeInMilliseconds + delta,
lastTimePlayed: new Date(), lastTimePlayed: new Date(),
}); });
}
gamesPlaytime.set(game.id, performance.now()); gamesPlaytime.set(game.id, {
...gamePlaytime,
lastTick: performance.now(),
});
} else {
if (game.remoteId) {
HydraApi.put(`/games/${game.remoteId}`, {
playTimeDeltaInMilliseconds: 0,
lastTimePlayed: new Date(),
});
} else {
HydraApi.post("/games", {
objectId: game.objectID,
playTimeInMilliseconds: Math.round(game.playTimeInMilliseconds),
shop: game.shop,
lastTimePlayed: new Date(),
}).then((response) => {
const { id: remoteId } = response.data;
gameRepository.update({ objectID: game.objectID }, { remoteId });
});
}
gamesPlaytime.set(game.id, {
lastTick: performance.now(),
firstTick: performance.now(),
});
}
} else if (gamesPlaytime.has(game.id)) { } else if (gamesPlaytime.has(game.id)) {
const gamePlaytime = gamesPlaytime.get(game.id)!;
gamesPlaytime.delete(game.id); gamesPlaytime.delete(game.id);
if (game.remoteId) {
HydraApi.put(`/games/${game.remoteId}`, {
playTimeInMilliseconds: Math.round(
performance.now() - gamePlaytime.firstTick
),
lastTimePlayed: game.lastTimePlayed,
});
} else {
HydraApi.post("/games", {
objectId: game.objectID,
playTimeInMilliseconds: Math.round(game.playTimeInMilliseconds),
shop: game.shop,
lastTimePlayed: game.lastTimePlayed,
}).then((response) => {
const { id: remoteId } = response.data;
gameRepository.update({ objectID: game.objectID }, { remoteId });
});
}
if (WindowManager.mainWindow) { if (WindowManager.mainWindow) {
WindowManager.mainWindow.webContents.send("on-game-close", game.id); WindowManager.mainWindow.webContents.send("on-game-close", game.id);
} }