mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-02-02 16:23:48 +03:00
feat: adding dexie
This commit is contained in:
parent
30aa3f5470
commit
849b6de6bc
@ -51,6 +51,7 @@
|
||||
"color.js": "^1.2.0",
|
||||
"create-desktop-shortcuts": "^1.11.0",
|
||||
"date-fns": "^3.6.0",
|
||||
"dexie": "^4.0.8",
|
||||
"electron-log": "^5.1.4",
|
||||
"electron-updater": "^6.1.8",
|
||||
"fetch-cookie": "^3.0.1",
|
||||
|
@ -1,8 +1,8 @@
|
||||
import type { GameShop } from "@types";
|
||||
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi, RepacksManager } from "@main/services";
|
||||
import { CatalogueCategory, formatName, steamUrlBuilder } from "@shared";
|
||||
import { HydraApi } from "@main/services";
|
||||
import { CatalogueCategory, steamUrlBuilder } from "@shared";
|
||||
import { steamGamesWorker } from "@main/workers";
|
||||
|
||||
const getCatalogue = async (
|
||||
@ -26,14 +26,9 @@ const getCatalogue = async (
|
||||
name: "getById",
|
||||
});
|
||||
|
||||
const repacks = RepacksManager.search({
|
||||
query: formatName(steamGame.name),
|
||||
});
|
||||
|
||||
return {
|
||||
title: steamGame.name,
|
||||
shop: game.shop,
|
||||
repacks,
|
||||
cover: steamUrlBuilder.library(game.objectId),
|
||||
objectID: game.objectId,
|
||||
};
|
||||
|
@ -45,6 +45,7 @@ const getGameShopDetails = async (
|
||||
|
||||
const appDetails = getLocalizedSteamAppDetails(objectID, language).then(
|
||||
(result) => {
|
||||
if (result) {
|
||||
gameShopCacheRepository.upsert(
|
||||
{
|
||||
objectID,
|
||||
@ -54,6 +55,7 @@ const getGameShopDetails = async (
|
||||
},
|
||||
["objectID"]
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -2,8 +2,6 @@ import type { CatalogueEntry } from "@types";
|
||||
|
||||
import { registerEvent } from "../register-event";
|
||||
import { steamGamesWorker } from "@main/workers";
|
||||
import { convertSteamGameToCatalogueEntry } from "../helpers/search-games";
|
||||
import { RepacksManager } from "@main/services";
|
||||
|
||||
const getGames = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
@ -15,13 +13,9 @@ const getGames = async (
|
||||
{ name: "list" }
|
||||
);
|
||||
|
||||
const entries = RepacksManager.findRepacksForCatalogueEntries(
|
||||
steamGames.map((game) => convertSteamGameToCatalogueEntry(game))
|
||||
);
|
||||
|
||||
return {
|
||||
results: entries,
|
||||
cursor: cursor + entries.length,
|
||||
results: steamGames,
|
||||
cursor: cursor + steamGames.length,
|
||||
};
|
||||
};
|
||||
|
||||
|
7
src/main/events/catalogue/get-repacks.ts
Normal file
7
src/main/events/catalogue/get-repacks.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { knexClient } from "@main/knex-client";
|
||||
|
||||
const getRepacks = (_event: Electron.IpcMainInvokeEvent) =>
|
||||
knexClient.select("*").from("repack");
|
||||
|
||||
registerEvent("getRepacks", getRepacks);
|
@ -1,9 +0,0 @@
|
||||
import { RepacksManager } from "@main/services";
|
||||
import { registerEvent } from "../register-event";
|
||||
|
||||
const searchGameRepacks = (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
query: string
|
||||
) => RepacksManager.search({ query });
|
||||
|
||||
registerEvent("searchGameRepacks", searchGameRepacks);
|
@ -4,7 +4,6 @@ import { DownloadSource } from "@main/entity";
|
||||
import axios from "axios";
|
||||
import { downloadSourceSchema } from "../helpers/validators";
|
||||
import { insertDownloadsFromSource } from "@main/helpers";
|
||||
import { RepacksManager } from "@main/services";
|
||||
|
||||
const addDownloadSource = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
@ -34,8 +33,6 @@ const addDownloadSource = async (
|
||||
}
|
||||
);
|
||||
|
||||
await RepacksManager.updateRepacks();
|
||||
|
||||
return downloadSource;
|
||||
};
|
||||
|
||||
|
@ -1,11 +1,7 @@
|
||||
import { downloadSourceRepository } from "@main/repository";
|
||||
import { registerEvent } from "../register-event";
|
||||
import { knexClient } from "@main/knex-client";
|
||||
|
||||
const getDownloadSources = async (_event: Electron.IpcMainInvokeEvent) =>
|
||||
downloadSourceRepository.find({
|
||||
order: {
|
||||
createdAt: "DESC",
|
||||
},
|
||||
});
|
||||
knexClient.select("*").from("download_source");
|
||||
|
||||
registerEvent("getDownloadSources", getDownloadSources);
|
||||
|
@ -5,9 +5,6 @@ import { RepacksManager } from "@main/services";
|
||||
const removeDownloadSource = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
id: number
|
||||
) => {
|
||||
await downloadSourceRepository.delete(id);
|
||||
await RepacksManager.updateRepacks();
|
||||
};
|
||||
) => downloadSourceRepository.delete(id);
|
||||
|
||||
registerEvent("removeDownloadSource", removeDownloadSource);
|
||||
|
@ -17,7 +17,6 @@ export const convertSteamGameToCatalogueEntry = (
|
||||
title: game.name,
|
||||
shop: "steam" as GameShop,
|
||||
cover: steamUrlBuilder.library(String(game.id)),
|
||||
repacks: [],
|
||||
});
|
||||
|
||||
export const getSteamGameById = async (
|
||||
|
@ -7,9 +7,9 @@ import "./catalogue/get-games";
|
||||
import "./catalogue/get-how-long-to-beat";
|
||||
import "./catalogue/get-random-game";
|
||||
import "./catalogue/search-games";
|
||||
import "./catalogue/search-game-repacks";
|
||||
import "./catalogue/get-game-stats";
|
||||
import "./catalogue/get-trending-games";
|
||||
import "./catalogue/get-repacks";
|
||||
import "./hardware/get-disk-free-space";
|
||||
import "./library/add-game-to-library";
|
||||
import "./library/create-game-shortcut";
|
||||
|
@ -9,36 +9,25 @@ import { steamGamesWorker } from "@main/workers";
|
||||
import { createGame } from "@main/services/library-sync";
|
||||
import { steamUrlBuilder } from "@shared";
|
||||
import { dataSource } from "@main/data-source";
|
||||
import { DownloadQueue, Game, Repack } from "@main/entity";
|
||||
import { DownloadQueue, Game } from "@main/entity";
|
||||
|
||||
const startGameDownload = async (
|
||||
_event: Electron.IpcMainInvokeEvent,
|
||||
payload: StartGameDownloadPayload
|
||||
) => {
|
||||
const { repackId, objectID, title, shop, downloadPath, downloader, uri } =
|
||||
payload;
|
||||
const { objectID, title, shop, downloadPath, downloader, uri } = payload;
|
||||
|
||||
return dataSource.transaction(async (transactionalEntityManager) => {
|
||||
const gameRepository = transactionalEntityManager.getRepository(Game);
|
||||
const repackRepository = transactionalEntityManager.getRepository(Repack);
|
||||
const downloadQueueRepository =
|
||||
transactionalEntityManager.getRepository(DownloadQueue);
|
||||
|
||||
const [game, repack] = await Promise.all([
|
||||
gameRepository.findOne({
|
||||
const game = await gameRepository.findOne({
|
||||
where: {
|
||||
objectID,
|
||||
shop,
|
||||
},
|
||||
}),
|
||||
repackRepository.findOne({
|
||||
where: {
|
||||
id: repackId,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
if (!repack) return;
|
||||
});
|
||||
|
||||
await DownloadManager.pauseDownload();
|
||||
|
||||
|
@ -68,7 +68,6 @@ const runMigrations = async () => {
|
||||
});
|
||||
|
||||
await knexClient.migrate.latest(migrationConfig);
|
||||
await knexClient.destroy();
|
||||
};
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
|
@ -1,9 +1,4 @@
|
||||
import {
|
||||
DownloadManager,
|
||||
RepacksManager,
|
||||
PythonInstance,
|
||||
startMainLoop,
|
||||
} from "./services";
|
||||
import { DownloadManager, PythonInstance, startMainLoop } from "./services";
|
||||
import {
|
||||
downloadQueueRepository,
|
||||
repackRepository,
|
||||
@ -18,8 +13,6 @@ import { HydraApi } from "./services/hydra-api";
|
||||
import { uploadGamesBatch } from "./services/library-sync";
|
||||
|
||||
const loadState = async (userPreferences: UserPreferences | null) => {
|
||||
RepacksManager.updateRepacks();
|
||||
|
||||
import("./events");
|
||||
|
||||
if (userPreferences?.realDebridApiToken) {
|
||||
|
@ -49,6 +49,8 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
getGameStats: (objectId: string, shop: GameShop) =>
|
||||
ipcRenderer.invoke("getGameStats", objectId, shop),
|
||||
getTrendingGames: () => ipcRenderer.invoke("getTrendingGames"),
|
||||
/* Meant for Dexie migration */
|
||||
getRepacks: () => ipcRenderer.invoke("getRepacks"),
|
||||
|
||||
/* User preferences */
|
||||
getUserPreferences: () => ipcRenderer.invoke("getUserPreferences"),
|
||||
|
@ -26,6 +26,10 @@ import {
|
||||
} from "@renderer/features";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UserFriendModal } from "./pages/shared-modals/user-friend-modal";
|
||||
import { RepacksContextProvider } from "./context";
|
||||
import { downloadSourcesWorker } from "./workers";
|
||||
|
||||
downloadSourcesWorker.postMessage("OK");
|
||||
|
||||
export interface AppProps {
|
||||
children: React.ReactNode;
|
||||
@ -197,7 +201,7 @@ export function App() {
|
||||
|
||||
useEffect(() => {
|
||||
new MutationObserver(() => {
|
||||
const modal = document.body.querySelector("[role=modal]");
|
||||
const modal = document.body.querySelector("[role=dialog]");
|
||||
|
||||
dispatch(toggleDraggingDisabled(Boolean(modal)));
|
||||
}).observe(document.body, {
|
||||
@ -211,6 +215,7 @@ export function App() {
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<RepacksContextProvider>
|
||||
<>
|
||||
{window.electron.platform === "win32" && (
|
||||
<div className={styles.titleBar}>
|
||||
@ -252,5 +257,6 @@ export function App() {
|
||||
|
||||
<BottomPanel />
|
||||
</>
|
||||
</RepacksContextProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { DownloadIcon, PeopleIcon } from "@primer/octicons-react";
|
||||
import type { CatalogueEntry, GameStats } from "@types";
|
||||
import type { CatalogueEntry, GameRepack, GameStats } from "@types";
|
||||
|
||||
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
||||
|
||||
import * as styles from "./game-card.css";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Badge } from "../badge/badge";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useCallback, useContext, useEffect, useState } from "react";
|
||||
import { useFormat } from "@renderer/hooks";
|
||||
import { repacksContext } from "@renderer/context";
|
||||
|
||||
export interface GameCardProps
|
||||
extends React.DetailedHTMLProps<
|
||||
@ -25,9 +26,20 @@ export function GameCard({ game, ...props }: GameCardProps) {
|
||||
const { t } = useTranslation("game_card");
|
||||
|
||||
const [stats, setStats] = useState<GameStats | null>(null);
|
||||
const [repacks, setRepacks] = useState<GameRepack[]>([]);
|
||||
|
||||
const { searchRepacks, isIndexingRepacks } = useContext(repacksContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isIndexingRepacks) {
|
||||
searchRepacks(game.title).then((repacks) => {
|
||||
setRepacks(repacks);
|
||||
});
|
||||
}
|
||||
}, [game, isIndexingRepacks, searchRepacks]);
|
||||
|
||||
const uniqueRepackers = Array.from(
|
||||
new Set(game.repacks.map(({ repacker }) => repacker))
|
||||
new Set(repacks.map(({ repacker }) => repacker))
|
||||
);
|
||||
|
||||
const handleHover = useCallback(() => {
|
||||
|
@ -1,4 +1,10 @@
|
||||
import { createContext, useCallback, useEffect, useState } from "react";
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useParams, useSearchParams } from "react-router-dom";
|
||||
|
||||
import { setHeaderTitle } from "@renderer/features";
|
||||
@ -16,6 +22,7 @@ import type {
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { GameDetailsContext } from "./game-details.context.types";
|
||||
import { SteamContentDescriptor } from "@shared";
|
||||
import { repacksContext } from "../repacks/repacks.context";
|
||||
|
||||
export const gameDetailsContext = createContext<GameDetailsContext>({
|
||||
game: null,
|
||||
@ -52,7 +59,6 @@ export function GameDetailsContextProvider({
|
||||
const { objectID, shop } = useParams();
|
||||
|
||||
const [shopDetails, setShopDetails] = useState<ShopDetails | null>(null);
|
||||
const [repacks, setRepacks] = useState<GameRepack[]>([]);
|
||||
const [game, setGame] = useState<Game | null>(null);
|
||||
const [hasNSFWContentBlocked, setHasNSFWContentBlocked] = useState(false);
|
||||
|
||||
@ -64,10 +70,22 @@ export function GameDetailsContextProvider({
|
||||
const [showRepacksModal, setShowRepacksModal] = useState(false);
|
||||
const [showGameOptionsModal, setShowGameOptionsModal] = useState(false);
|
||||
|
||||
const [repacks, setRepacks] = useState<GameRepack[]>([]);
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const gameTitle = searchParams.get("title")!;
|
||||
|
||||
const { searchRepacks, isIndexingRepacks } = useContext(repacksContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isIndexingRepacks) {
|
||||
searchRepacks(gameTitle).then((repacks) => {
|
||||
setRepacks(repacks);
|
||||
});
|
||||
}
|
||||
}, [game, gameTitle, isIndexingRepacks, searchRepacks]);
|
||||
|
||||
const { i18n } = useTranslation("game_details");
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
@ -91,37 +109,31 @@ export function GameDetailsContextProvider({
|
||||
}, [updateGame, isGameDownloading, lastPacket?.game.status]);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.allSettled([
|
||||
window.electron.getGameShopDetails(
|
||||
window.electron
|
||||
.getGameShopDetails(
|
||||
objectID!,
|
||||
shop as GameShop,
|
||||
getSteamLanguage(i18n.language)
|
||||
),
|
||||
window.electron.searchGameRepacks(gameTitle),
|
||||
window.electron.getGameStats(objectID!, shop as GameShop),
|
||||
])
|
||||
.then(([appDetailsResult, repacksResult, statsResult]) => {
|
||||
if (appDetailsResult.status === "fulfilled") {
|
||||
setShopDetails(appDetailsResult.value);
|
||||
)
|
||||
.then((result) => {
|
||||
setShopDetails(result);
|
||||
|
||||
if (
|
||||
appDetailsResult.value?.content_descriptors.ids.includes(
|
||||
result?.content_descriptors.ids.includes(
|
||||
SteamContentDescriptor.AdultOnlySexualContent
|
||||
)
|
||||
) {
|
||||
setHasNSFWContentBlocked(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (repacksResult.status === "fulfilled")
|
||||
setRepacks(repacksResult.value);
|
||||
|
||||
if (statsResult.status === "fulfilled") setStats(statsResult.value);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
|
||||
window.electron.getGameStats(objectID!, shop as GameShop).then((result) => {
|
||||
setStats(result);
|
||||
});
|
||||
|
||||
updateGame();
|
||||
}, [updateGame, dispatch, gameTitle, objectID, shop, i18n.language]);
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
export * from "./game-details/game-details.context";
|
||||
export * from "./settings/settings.context";
|
||||
export * from "./user-profile/user-profile.context";
|
||||
export * from "./repacks/repacks.context";
|
||||
|
58
src/renderer/src/context/repacks/repacks.context.tsx
Normal file
58
src/renderer/src/context/repacks/repacks.context.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
import type { GameRepack } from "@types";
|
||||
import { createContext, useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { repacksWorker } from "@renderer/workers";
|
||||
|
||||
export interface RepacksContext {
|
||||
searchRepacks: (query: string) => Promise<GameRepack[]>;
|
||||
isIndexingRepacks: boolean;
|
||||
}
|
||||
|
||||
export const repacksContext = createContext<RepacksContext>({
|
||||
searchRepacks: async () => [] as GameRepack[],
|
||||
isIndexingRepacks: false,
|
||||
});
|
||||
|
||||
const { Provider } = repacksContext;
|
||||
export const { Consumer: RepacksContextConsumer } = repacksContext;
|
||||
|
||||
export interface RepacksContextProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function RepacksContextProvider({ children }: RepacksContextProps) {
|
||||
const [isIndexingRepacks, setIsIndexingRepacks] = useState(true);
|
||||
|
||||
const searchRepacks = useCallback(async (query: string) => {
|
||||
return new Promise<GameRepack[]>((resolve) => {
|
||||
const channelId = crypto.randomUUID();
|
||||
repacksWorker.postMessage([channelId, query]);
|
||||
|
||||
const channel = new BroadcastChannel(`repacks:search:${channelId}`);
|
||||
channel.onmessage = (event: MessageEvent<GameRepack[]>) => {
|
||||
resolve(event.data);
|
||||
};
|
||||
|
||||
return [];
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
repacksWorker.postMessage("INDEX_REPACKS");
|
||||
|
||||
repacksWorker.onmessage = () => {
|
||||
setIsIndexingRepacks(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Provider
|
||||
value={{
|
||||
searchRepacks,
|
||||
isIndexingRepacks,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Provider>
|
||||
);
|
||||
}
|
2
src/renderer/src/declaration.d.ts
vendored
2
src/renderer/src/declaration.d.ts
vendored
@ -65,6 +65,8 @@ declare global {
|
||||
searchGameRepacks: (query: string) => Promise<GameRepack[]>;
|
||||
getGameStats: (objectId: string, shop: GameShop) => Promise<GameStats>;
|
||||
getTrendingGames: () => Promise<TrendingGame[]>;
|
||||
/* Meant for Dexie migration */
|
||||
getRepacks: () => Promise<GameRepack[]>;
|
||||
|
||||
/* Library */
|
||||
addGameToLibrary: (
|
||||
|
13
src/renderer/src/dexie.ts
Normal file
13
src/renderer/src/dexie.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Dexie } from "dexie";
|
||||
|
||||
export const db = new Dexie("Hydra");
|
||||
|
||||
db.version(1).stores({
|
||||
repacks: `++id, title, uri, fileSize, uploadDate, downloadSourceId, repacker, createdAt, updatedAt`,
|
||||
downloadSources: `++id, url, name, etag, downloadCount, status, createdAt, updatedAt`,
|
||||
});
|
||||
|
||||
export const downloadSourcesTable = db.table("downloadSources");
|
||||
export const repacksTable = db.table("repacks");
|
||||
|
||||
db.open();
|
@ -29,6 +29,8 @@ import { store } from "./store";
|
||||
|
||||
import resources from "@locales";
|
||||
|
||||
import "./workers";
|
||||
|
||||
Sentry.init({});
|
||||
|
||||
i18n
|
||||
|
@ -8,6 +8,7 @@ import { useForm } from "react-hook-form";
|
||||
|
||||
import * as yup from "yup";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { downloadSourcesTable } from "@renderer/dexie";
|
||||
|
||||
interface AddDownloadSourceModalProps {
|
||||
visible: boolean;
|
||||
@ -91,6 +92,9 @@ export function AddDownloadSourceModal({
|
||||
}, [visible, clearErrors, handleSubmit, onSubmit, setValue, sourceUrl]);
|
||||
|
||||
const handleAddDownloadSource = async () => {
|
||||
await downloadSourcesTable.add({
|
||||
url,
|
||||
});
|
||||
await window.electron.addDownloadSource(url);
|
||||
onClose();
|
||||
onAddDownloadSource();
|
||||
|
@ -11,6 +11,7 @@ import { useToast } from "@renderer/hooks";
|
||||
import { DownloadSourceStatus } from "@shared";
|
||||
import { SPACING_UNIT } from "@renderer/theme.css";
|
||||
import { settingsContext } from "@renderer/context";
|
||||
import { db, downloadSourcesTable, repacksTable } from "@renderer/dexie";
|
||||
|
||||
export function SettingsDownloadSources() {
|
||||
const [showAddDownloadSourceModal, setShowAddDownloadSourceModal] =
|
||||
@ -25,7 +26,7 @@ export function SettingsDownloadSources() {
|
||||
const { showSuccessToast } = useToast();
|
||||
|
||||
const getDownloadSources = async () => {
|
||||
return window.electron.getDownloadSources().then((sources) => {
|
||||
downloadSourcesTable.toArray().then((sources) => {
|
||||
setDownloadSources(sources);
|
||||
});
|
||||
};
|
||||
@ -39,7 +40,11 @@ export function SettingsDownloadSources() {
|
||||
}, [sourceUrl]);
|
||||
|
||||
const handleRemoveSource = async (id: number) => {
|
||||
await window.electron.removeDownloadSource(id);
|
||||
await db.transaction("rw", downloadSourcesTable, repacksTable, async () => {
|
||||
await downloadSourcesTable.where({ id }).delete();
|
||||
await repacksTable.where({ downloadSourceId: id }).delete();
|
||||
});
|
||||
|
||||
showSuccessToast(t("removed_download_source"));
|
||||
|
||||
getDownloadSources();
|
||||
|
8
src/renderer/src/workers/download-sources.worker.ts
Normal file
8
src/renderer/src/workers/download-sources.worker.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { db, downloadSourcesTable, repacksTable } from "@renderer/dexie";
|
||||
|
||||
self.onmessage = () => {
|
||||
db.transaction("rw", repacksTable, downloadSourcesTable, async () => {
|
||||
await repacksTable.where({ downloadSourceId: 10 }).delete();
|
||||
await downloadSourcesTable.where({ id: 10 }).delete();
|
||||
});
|
||||
};
|
24
src/renderer/src/workers/index.ts
Normal file
24
src/renderer/src/workers/index.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import MigrationWorker from "./migration.worker?worker";
|
||||
import RepacksWorker from "./repacks.worker?worker";
|
||||
import DownloadSourcesWorker from "./download-sources.worker?worker";
|
||||
|
||||
// const migrationWorker = new MigrationWorker();
|
||||
export const repacksWorker = new RepacksWorker();
|
||||
export const downloadSourcesWorker = new DownloadSourcesWorker();
|
||||
|
||||
// window.electron.getRepacks().then((repacks) => {
|
||||
// console.log(repacks);
|
||||
// migrationWorker.postMessage(["MIGRATE_REPACKS", repacks]);
|
||||
// });
|
||||
|
||||
// window.electron.getDownloadSources().then((downloadSources) => {
|
||||
// migrationWorker.postMessage(["MIGRATE_DOWNLOAD_SOURCES", downloadSources]);
|
||||
// });
|
||||
|
||||
// migrationWorker.onmessage = (event) => {
|
||||
// console.log(event.data);
|
||||
// };
|
||||
|
||||
// setTimeout(() => {
|
||||
// repacksWorker.postMessage("god");
|
||||
// }, 500);
|
32
src/renderer/src/workers/migration.worker.ts
Normal file
32
src/renderer/src/workers/migration.worker.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { downloadSourcesTable, repacksTable } from "@renderer/dexie";
|
||||
import { DownloadSource, GameRepack } from "@types";
|
||||
|
||||
export type Payload =
|
||||
| ["MIGRATE_REPACKS", GameRepack[]]
|
||||
| ["MIGRATE_DOWNLOAD_SOURCES", DownloadSource[]];
|
||||
|
||||
self.onmessage = async (event: MessageEvent<Payload>) => {
|
||||
const [type, data] = event.data;
|
||||
|
||||
if (type === "MIGRATE_DOWNLOAD_SOURCES") {
|
||||
const dexieDownloadSources = await downloadSourcesTable.count();
|
||||
|
||||
if (data.length !== dexieDownloadSources) {
|
||||
await downloadSourcesTable.clear();
|
||||
await downloadSourcesTable.bulkAdd(data);
|
||||
}
|
||||
|
||||
self.postMessage("MIGRATE_DOWNLOAD_SOURCES_COMPLETE");
|
||||
}
|
||||
|
||||
if (type === "MIGRATE_REPACKS") {
|
||||
const dexieRepacks = await repacksTable.count();
|
||||
|
||||
if (data.length !== dexieRepacks) {
|
||||
await repacksTable.clear();
|
||||
await repacksTable.bulkAdd(data);
|
||||
}
|
||||
|
||||
self.postMessage("MIGRATE_REPACKS_COMPLETE");
|
||||
}
|
||||
};
|
52
src/renderer/src/workers/repacks.worker.ts
Normal file
52
src/renderer/src/workers/repacks.worker.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { repacksTable } from "@renderer/dexie";
|
||||
import { formatName } from "@shared";
|
||||
import { GameRepack } from "@types";
|
||||
import flexSearch from "flexsearch";
|
||||
|
||||
const index = new flexSearch.Index();
|
||||
|
||||
const state = {
|
||||
repacks: [] as any[],
|
||||
};
|
||||
|
||||
interface SerializedGameRepack extends Omit<GameRepack, "uris"> {
|
||||
uris: string;
|
||||
}
|
||||
|
||||
self.onmessage = async (
|
||||
event: MessageEvent<[string, string] | "INDEX_REPACKS">
|
||||
) => {
|
||||
if (event.data === "INDEX_REPACKS") {
|
||||
repacksTable
|
||||
.toCollection()
|
||||
.sortBy("uploadDate")
|
||||
.then((results) => {
|
||||
state.repacks = results.reverse();
|
||||
|
||||
for (let i = 0; i < state.repacks.length; i++) {
|
||||
const repack = state.repacks[i];
|
||||
const formattedTitle = formatName(repack.title);
|
||||
index.add(i, formattedTitle);
|
||||
}
|
||||
|
||||
self.postMessage("INDEXING_COMPLETE");
|
||||
});
|
||||
} else {
|
||||
const [requestId, query] = event.data;
|
||||
|
||||
const results = index.search(formatName(query)).map((index) => {
|
||||
const repack = state.repacks.at(index as number) as SerializedGameRepack;
|
||||
|
||||
const uris = JSON.parse(repack.uris);
|
||||
|
||||
return {
|
||||
...repack,
|
||||
uris: [...uris, repack.magnet].filter(Boolean),
|
||||
};
|
||||
});
|
||||
|
||||
const channel = new BroadcastChannel(`repacks:search:${requestId}`);
|
||||
|
||||
channel.postMessage(results);
|
||||
}
|
||||
};
|
@ -44,7 +44,6 @@ export interface CatalogueEntry {
|
||||
title: string;
|
||||
/* Epic Games covers cannot be guessed with objectID */
|
||||
cover: string;
|
||||
repacks: GameRepack[];
|
||||
}
|
||||
|
||||
export interface UserGame {
|
||||
@ -71,7 +70,6 @@ export interface Game {
|
||||
status: GameStatus | null;
|
||||
folderName: string;
|
||||
downloadPath: string | null;
|
||||
repacks: GameRepack[];
|
||||
progress: number;
|
||||
bytesDownloaded: number;
|
||||
playTimeInMilliseconds: number;
|
||||
|
@ -3638,6 +3638,11 @@ detect-node@^2.0.4:
|
||||
resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz"
|
||||
integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
|
||||
|
||||
dexie@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/dexie/-/dexie-4.0.8.tgz#21fca70686bdaa1d86fad45b6b19316f6a084a1d"
|
||||
integrity sha512-1G6cJevS17KMDK847V3OHvK2zei899GwpDiqfEXHP1ASvme6eWJmAp9AU4s1son2TeGkWmC0g3y8ezOBPnalgQ==
|
||||
|
||||
diff@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||
|
Loading…
Reference in New Issue
Block a user