mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-01-23 21:44:55 +03:00
feat: batch games and code refactor
This commit is contained in:
parent
da5cc11bff
commit
7fc376b47f
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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: {
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
src/main/services/library-sync/create-game.ts
Normal file
11
src/main/services/library-sync/create-game.ts
Normal 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,
|
||||||
|
});
|
||||||
|
};
|
69
src/main/services/library-sync/get-remote-games.ts
Normal file
69
src/main/services/library-sync/get-remote-games.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
4
src/main/services/library-sync/index.ts
Normal file
4
src/main/services/library-sync/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from "./get-remote-games";
|
||||||
|
export * from "./upload-batch-games";
|
||||||
|
export * from "./update-game-playtime";
|
||||||
|
export * from "./create-game";
|
13
src/main/services/library-sync/update-game-playtime.ts
Normal file
13
src/main/services/library-sync/update-game-playtime.ts
Normal 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,
|
||||||
|
});
|
||||||
|
};
|
35
src/main/services/library-sync/upload-batch-games.ts
Normal file
35
src/main/services/library-sync/upload-batch-games.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -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 });
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user