mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-02-15 04:32:13 +00:00
fix: removing repacks from worker threads to fix race condition
This commit is contained in:
parent
4559e23610
commit
ea923d5082
13 changed files with 71 additions and 105 deletions
|
@ -2,8 +2,7 @@ import { getSteamAppAsset } from "@main/helpers";
|
||||||
import type { CatalogueEntry, GameShop } from "@types";
|
import type { CatalogueEntry, GameShop } from "@types";
|
||||||
|
|
||||||
import { registerEvent } from "../register-event";
|
import { registerEvent } from "../register-event";
|
||||||
import { requestSteam250 } from "@main/services";
|
import { RepacksManager, requestSteam250 } from "@main/services";
|
||||||
import { repacksWorker } from "@main/workers";
|
|
||||||
import { formatName } from "@shared";
|
import { formatName } from "@shared";
|
||||||
|
|
||||||
const resultSize = 12;
|
const resultSize = 12;
|
||||||
|
@ -19,10 +18,7 @@ const getCatalogue = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title, objectID } = trendingGames[i]!;
|
const { title, objectID } = trendingGames[i]!;
|
||||||
const repacks = await repacksWorker.run(
|
const repacks = RepacksManager.search({ query: formatName(title) });
|
||||||
{ query: formatName(title) },
|
|
||||||
{ name: "search" }
|
|
||||||
);
|
|
||||||
|
|
||||||
const catalogueEntry = {
|
const catalogueEntry = {
|
||||||
objectID,
|
objectID,
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import type { CatalogueEntry } from "@types";
|
import type { CatalogueEntry } from "@types";
|
||||||
|
|
||||||
import { registerEvent } from "../register-event";
|
import { registerEvent } from "../register-event";
|
||||||
import { repacksWorker, steamGamesWorker } from "@main/workers";
|
import { steamGamesWorker } from "@main/workers";
|
||||||
import { convertSteamGameToCatalogueEntry } from "../helpers/search-games";
|
import { convertSteamGameToCatalogueEntry } from "../helpers/search-games";
|
||||||
|
import { RepacksManager } from "@main/services";
|
||||||
|
|
||||||
const getGames = async (
|
const getGames = async (
|
||||||
_event: Electron.IpcMainInvokeEvent,
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
|
@ -14,11 +15,8 @@ const getGames = async (
|
||||||
{ name: "list" }
|
{ name: "list" }
|
||||||
);
|
);
|
||||||
|
|
||||||
const entries = await repacksWorker.run(
|
const entries = RepacksManager.findRepacksForCatalogueEntries(
|
||||||
steamGames.map((game) => convertSteamGameToCatalogueEntry(game)),
|
steamGames.map((game) => convertSteamGameToCatalogueEntry(game))
|
||||||
{
|
|
||||||
name: "findRepacksForCatalogueEntries",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import { RepacksManager } from "@main/services";
|
||||||
import { registerEvent } from "../register-event";
|
import { registerEvent } from "../register-event";
|
||||||
import { repacksWorker } from "@main/workers";
|
|
||||||
|
|
||||||
const searchGameRepacks = (
|
const searchGameRepacks = (
|
||||||
_event: Electron.IpcMainInvokeEvent,
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
query: string
|
query: string
|
||||||
) => repacksWorker.run({ query }, { name: "search" });
|
) => RepacksManager.search({ query });
|
||||||
|
|
||||||
registerEvent("searchGameRepacks", searchGameRepacks);
|
registerEvent("searchGameRepacks", searchGameRepacks);
|
||||||
|
|
|
@ -3,9 +3,8 @@ import { dataSource } from "@main/data-source";
|
||||||
import { DownloadSource } from "@main/entity";
|
import { DownloadSource } from "@main/entity";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { downloadSourceSchema } from "../helpers/validators";
|
import { downloadSourceSchema } from "../helpers/validators";
|
||||||
import { repackRepository } from "@main/repository";
|
|
||||||
import { repacksWorker } from "@main/workers";
|
|
||||||
import { insertDownloadsFromSource } from "@main/helpers";
|
import { insertDownloadsFromSource } from "@main/helpers";
|
||||||
|
import { RepacksManager } from "@main/services";
|
||||||
|
|
||||||
const addDownloadSource = async (
|
const addDownloadSource = async (
|
||||||
_event: Electron.IpcMainInvokeEvent,
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
|
@ -31,15 +30,7 @@ const addDownloadSource = async (
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
repackRepository
|
await RepacksManager.updateRepacks();
|
||||||
.find({
|
|
||||||
order: {
|
|
||||||
createdAt: "DESC",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((repacks) => {
|
|
||||||
repacksWorker.run(repacks, { name: "setRepacks" });
|
|
||||||
});
|
|
||||||
|
|
||||||
return downloadSource;
|
return downloadSource;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,22 +1,13 @@
|
||||||
import { downloadSourceRepository, repackRepository } from "@main/repository";
|
import { downloadSourceRepository } from "@main/repository";
|
||||||
import { registerEvent } from "../register-event";
|
import { registerEvent } from "../register-event";
|
||||||
import { repacksWorker } from "@main/workers";
|
import { RepacksManager } from "@main/services";
|
||||||
|
|
||||||
const removeDownloadSource = async (
|
const removeDownloadSource = async (
|
||||||
_event: Electron.IpcMainInvokeEvent,
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
id: number
|
id: number
|
||||||
) => {
|
) => {
|
||||||
await downloadSourceRepository.delete(id);
|
await downloadSourceRepository.delete(id);
|
||||||
|
await RepacksManager.updateRepacks();
|
||||||
repackRepository
|
|
||||||
.find({
|
|
||||||
order: {
|
|
||||||
createdAt: "DESC",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((repacks) => {
|
|
||||||
repacksWorker.run(repacks, { name: "setRepacks" });
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
registerEvent("removeDownloadSource", removeDownloadSource);
|
registerEvent("removeDownloadSource", removeDownloadSource);
|
||||||
|
|
|
@ -2,8 +2,7 @@ import { registerEvent } from "../register-event";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { downloadSourceRepository } from "@main/repository";
|
import { downloadSourceRepository } from "@main/repository";
|
||||||
import { downloadSourceSchema } from "../helpers/validators";
|
import { downloadSourceSchema } from "../helpers/validators";
|
||||||
import { repacksWorker } from "@main/workers";
|
import { RepacksManager } from "@main/services";
|
||||||
import { GameRepack } from "@types";
|
|
||||||
|
|
||||||
const validateDownloadSource = async (
|
const validateDownloadSource = async (
|
||||||
_event: Electron.IpcMainInvokeEvent,
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
|
@ -20,9 +19,7 @@ const validateDownloadSource = async (
|
||||||
if (existingSource)
|
if (existingSource)
|
||||||
throw new Error("Source with the same url already exists");
|
throw new Error("Source with the same url already exists");
|
||||||
|
|
||||||
const repacks = (await repacksWorker.run(undefined, {
|
const repacks = RepacksManager.repacks;
|
||||||
name: "list",
|
|
||||||
})) as GameRepack[];
|
|
||||||
|
|
||||||
const existingUris = source.downloads
|
const existingUris = source.downloads
|
||||||
.flatMap((download) => download.uris)
|
.flatMap((download) => download.uris)
|
||||||
|
|
|
@ -4,7 +4,8 @@ import flexSearch from "flexsearch";
|
||||||
import type { GameShop, CatalogueEntry, SteamGame } from "@types";
|
import type { GameShop, CatalogueEntry, SteamGame } from "@types";
|
||||||
|
|
||||||
import { getSteamAppAsset } from "@main/helpers";
|
import { getSteamAppAsset } from "@main/helpers";
|
||||||
import { repacksWorker, steamGamesWorker } from "@main/workers";
|
import { steamGamesWorker } from "@main/workers";
|
||||||
|
import { RepacksManager } from "@main/services";
|
||||||
|
|
||||||
export interface SearchGamesArgs {
|
export interface SearchGamesArgs {
|
||||||
query?: string;
|
query?: string;
|
||||||
|
@ -29,11 +30,8 @@ export const searchSteamGames = async (
|
||||||
name: "search",
|
name: "search",
|
||||||
})) as SteamGame[];
|
})) as SteamGame[];
|
||||||
|
|
||||||
const result = await repacksWorker.run(
|
const result = RepacksManager.findRepacksForCatalogueEntries(
|
||||||
steamGames.map((game) => convertSteamGameToCatalogueEntry(game)),
|
steamGames.map((game) => convertSteamGameToCatalogueEntry(game))
|
||||||
{
|
|
||||||
name: "findRepacksForCatalogueEntries",
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return orderBy(
|
return orderBy(
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { dataSource } from "@main/data-source";
|
||||||
import { DownloadSource, Repack } from "@main/entity";
|
import { DownloadSource, Repack } from "@main/entity";
|
||||||
import { downloadSourceSchema } from "@main/events/helpers/validators";
|
import { downloadSourceSchema } from "@main/events/helpers/validators";
|
||||||
import { downloadSourceRepository } from "@main/repository";
|
import { downloadSourceRepository } from "@main/repository";
|
||||||
|
import { RepacksManager } from "@main/services";
|
||||||
import { downloadSourceWorker } from "@main/workers";
|
import { downloadSourceWorker } from "@main/workers";
|
||||||
import { chunk } from "lodash-es";
|
import { chunk } from "lodash-es";
|
||||||
import type { EntityManager } from "typeorm";
|
import type { EntityManager } from "typeorm";
|
||||||
|
@ -65,5 +66,7 @@ export const fetchDownloadSourcesAndUpdate = async () => {
|
||||||
result.downloads
|
result.downloads
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await RepacksManager.updateRepacks();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
import { DownloadManager, startMainLoop } from "./services";
|
import { DownloadManager, RepacksManager, startMainLoop } from "./services";
|
||||||
import {
|
import { gameRepository, userPreferencesRepository } from "./repository";
|
||||||
gameRepository,
|
|
||||||
repackRepository,
|
|
||||||
userPreferencesRepository,
|
|
||||||
} from "./repository";
|
|
||||||
import { UserPreferences } from "./entity";
|
import { UserPreferences } from "./entity";
|
||||||
import { RealDebridClient } from "./services/real-debrid";
|
import { RealDebridClient } from "./services/real-debrid";
|
||||||
import { Not } from "typeorm";
|
import { Not } from "typeorm";
|
||||||
import { repacksWorker } from "./workers";
|
|
||||||
import { fetchDownloadSourcesAndUpdate } from "./helpers";
|
import { fetchDownloadSourcesAndUpdate } from "./helpers";
|
||||||
|
|
||||||
startMainLoop();
|
startMainLoop();
|
||||||
|
|
||||||
const loadState = async (userPreferences: UserPreferences | null) => {
|
const loadState = async (userPreferences: UserPreferences | null) => {
|
||||||
|
await RepacksManager.updateRepacks();
|
||||||
|
|
||||||
import("./events");
|
import("./events");
|
||||||
|
|
||||||
if (userPreferences?.realDebridApiToken)
|
if (userPreferences?.realDebridApiToken)
|
||||||
|
@ -28,14 +25,6 @@ const loadState = async (userPreferences: UserPreferences | null) => {
|
||||||
|
|
||||||
if (game) DownloadManager.startDownload(game);
|
if (game) DownloadManager.startDownload(game);
|
||||||
|
|
||||||
const repacks = await repackRepository.find({
|
|
||||||
order: {
|
|
||||||
createdAt: "DESC",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
repacksWorker.run(repacks, { name: "setRepacks" });
|
|
||||||
|
|
||||||
fetchDownloadSourcesAndUpdate();
|
fetchDownloadSourcesAndUpdate();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,3 +7,4 @@ export * from "./download-manager";
|
||||||
export * from "./how-long-to-beat";
|
export * from "./how-long-to-beat";
|
||||||
export * from "./process-watcher";
|
export * from "./process-watcher";
|
||||||
export * from "./main-loop";
|
export * from "./main-loop";
|
||||||
|
export * from "./repacks-manager";
|
||||||
|
|
44
src/main/services/repacks-manager.ts
Normal file
44
src/main/services/repacks-manager.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { repackRepository } from "@main/repository";
|
||||||
|
import { formatName } from "@shared";
|
||||||
|
import { CatalogueEntry, GameRepack } from "@types";
|
||||||
|
import flexSearch from "flexsearch";
|
||||||
|
|
||||||
|
export class RepacksManager {
|
||||||
|
public static repacks: GameRepack[] = [];
|
||||||
|
private static repacksIndex = new flexSearch.Index();
|
||||||
|
|
||||||
|
public static async updateRepacks() {
|
||||||
|
this.repacks = await repackRepository.find({
|
||||||
|
order: {
|
||||||
|
createdAt: "DESC",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < this.repacks.length; i++) {
|
||||||
|
this.repacksIndex.remove(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.repacksIndex = new flexSearch.Index();
|
||||||
|
|
||||||
|
for (let i = 0; i < this.repacks.length; i++) {
|
||||||
|
const repack = this.repacks[i];
|
||||||
|
|
||||||
|
const formattedTitle = formatName(repack.title);
|
||||||
|
|
||||||
|
this.repacksIndex.add(i, formattedTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static search(options: flexSearch.SearchOptions) {
|
||||||
|
return this.repacksIndex
|
||||||
|
.search({ ...options, query: formatName(options.query ?? "") })
|
||||||
|
.map((index) => this.repacks[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static findRepacksForCatalogueEntries(entries: CatalogueEntry[]) {
|
||||||
|
return entries.map((entry) => {
|
||||||
|
const repacks = this.search({ query: formatName(entry.title) });
|
||||||
|
return { ...entry, repacks };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import steamGamesWorkerPath from "./steam-games.worker?modulePath";
|
import steamGamesWorkerPath from "./steam-games.worker?modulePath";
|
||||||
import repacksWorkerPath from "./repacks.worker?modulePath";
|
|
||||||
import downloadSourceWorkerPath from "./download-source.worker?modulePath";
|
import downloadSourceWorkerPath from "./download-source.worker?modulePath";
|
||||||
|
|
||||||
import Piscina from "piscina";
|
import Piscina from "piscina";
|
||||||
|
@ -14,10 +13,6 @@ export const steamGamesWorker = new Piscina({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const repacksWorker = new Piscina({
|
|
||||||
filename: repacksWorkerPath,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const downloadSourceWorker = new Piscina({
|
export const downloadSourceWorker = new Piscina({
|
||||||
filename: downloadSourceWorkerPath,
|
filename: downloadSourceWorkerPath,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
import { formatName } from "@shared";
|
|
||||||
import { CatalogueEntry, GameRepack } from "@types";
|
|
||||||
import flexSearch from "flexsearch";
|
|
||||||
|
|
||||||
const repacksIndex = new flexSearch.Index();
|
|
||||||
|
|
||||||
const state: { repacks: GameRepack[] } = { repacks: [] };
|
|
||||||
|
|
||||||
export const setRepacks = (repacks: GameRepack[]) => {
|
|
||||||
for (let i = 0; i < state.repacks.length; i++) {
|
|
||||||
repacksIndex.remove(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.repacks = repacks;
|
|
||||||
|
|
||||||
for (let i = 0; i < repacks.length; i++) {
|
|
||||||
const repack = repacks[i];
|
|
||||||
|
|
||||||
const formattedTitle = formatName(repack.title);
|
|
||||||
|
|
||||||
repacksIndex.add(i, formattedTitle);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const search = (options: flexSearch.SearchOptions) =>
|
|
||||||
repacksIndex
|
|
||||||
.search({ ...options, query: formatName(options.query ?? "") })
|
|
||||||
.map((index) => state.repacks[index]);
|
|
||||||
|
|
||||||
export const list = () => state.repacks;
|
|
||||||
|
|
||||||
export const findRepacksForCatalogueEntries = (entries: CatalogueEntry[]) => {
|
|
||||||
return entries.map((entry) => {
|
|
||||||
const repacks = search({ query: formatName(entry.title) });
|
|
||||||
return { ...entry, repacks };
|
|
||||||
});
|
|
||||||
};
|
|
Loading…
Reference in a new issue