From 41227b125e18f94e0d09840fc9199dc2a8081725 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Tue, 14 Oct 2025 17:42:59 +0100 Subject: [PATCH] feat: improving performance on sources --- .../download-sources/add-download-source.ts | 57 ++++++++++++++++++- src/main/events/download-sources/helpers.ts | 3 + .../download-sources/sync-download-sources.ts | 50 +++++++++++++--- 3 files changed, 100 insertions(+), 10 deletions(-) diff --git a/src/main/events/download-sources/add-download-source.ts b/src/main/events/download-sources/add-download-source.ts index 81d40cd7..801b0635 100644 --- a/src/main/events/download-sources/add-download-source.ts +++ b/src/main/events/download-sources/add-download-source.ts @@ -1,7 +1,10 @@ import { registerEvent } from "../register-event"; -import { downloadSourcesSublevel } from "@main/level"; +import { downloadSourcesSublevel, repacksSublevel } from "@main/level"; import { HydraApi } from "@main/services"; -import { importDownloadSourceToLocal } from "./helpers"; +import { + importDownloadSourceToLocal, + invalidateDownloadSourcesCache, +} from "./helpers"; const addDownloadSource = async ( _event: Electron.IpcMainInvokeEvent, @@ -12,6 +15,28 @@ const addDownloadSource = async ( throw new Error("Failed to import download source"); } + // Verify that repacks were actually written to the database (read-after-write) + // This ensures all async operations are complete before proceeding + let repackCount = 0; + const repackIds: number[] = []; + for await (const [, repack] of repacksSublevel.iterator()) { + if (repack.downloadSourceId === result.id) { + repackCount++; + repackIds.push(repack.id); + } + } + + // Log for debugging - helps identify if repacks are being created + console.log( + `✅ Download source ${result.id} (${result.name}) created with ${repackCount} repacks` + ); + console.log( + ` Repack IDs: [${repackIds.slice(0, 5).join(", ")}${repackIds.length > 5 ? "..." : ""}]` + ); + console.log( + ` Object IDs: [${result.objectIds.slice(0, 5).join(", ")}${result.objectIds.length > 5 ? "..." : ""}]` + ); + await HydraApi.post("/profile/download-sources", { urls: [url], }); @@ -24,6 +49,7 @@ const addDownloadSource = async ( { needsAuth: false } ); + // Update the source with fingerprint const updatedSource = await downloadSourcesSublevel.get(`${result.id}`); if (updatedSource) { await downloadSourcesSublevel.put(`${result.id}`, { @@ -33,6 +59,33 @@ const addDownloadSource = async ( }); } + // Final verification: ensure the source with fingerprint is persisted + const finalSource = await downloadSourcesSublevel.get(`${result.id}`); + if (!finalSource || !finalSource.fingerprint) { + throw new Error("Failed to persist download source with fingerprint"); + } + + // Verify repacks still exist after fingerprint update + let finalRepackCount = 0; + for await (const [, repack] of repacksSublevel.iterator()) { + if (repack.downloadSourceId === result.id) { + finalRepackCount++; + } + } + + if (finalRepackCount !== repackCount) { + console.warn( + `⚠️ Repack count mismatch! Before: ${repackCount}, After: ${finalRepackCount}` + ); + } else { + console.log( + `✅ Final verification passed: ${finalRepackCount} repacks confirmed` + ); + } + + // Invalidate cache to ensure fresh data on next read + invalidateDownloadSourcesCache(); + return { ...result, fingerprint, diff --git a/src/main/events/download-sources/helpers.ts b/src/main/events/download-sources/helpers.ts index 95fdebe3..7f346fc0 100644 --- a/src/main/events/download-sources/helpers.ts +++ b/src/main/events/download-sources/helpers.ts @@ -282,6 +282,9 @@ export const importDownloadSourceToLocal = async ( steamGames ); + // Invalidate ID caches after creating new repacks to prevent ID collisions + invalidateIdCaches(); + return { ...downloadSource, objectIds, diff --git a/src/main/events/download-sources/sync-download-sources.ts b/src/main/events/download-sources/sync-download-sources.ts index 4e3837ba..b567ca63 100644 --- a/src/main/events/download-sources/sync-download-sources.ts +++ b/src/main/events/download-sources/sync-download-sources.ts @@ -3,7 +3,7 @@ import axios, { AxiosError } from "axios"; import { z } from "zod"; import { downloadSourcesSublevel, repacksSublevel } from "@main/level"; import { DownloadSourceStatus } from "@shared"; -import { invalidateDownloadSourcesCache } from "./helpers"; +import { invalidateDownloadSourcesCache, invalidateIdCaches } from "./helpers"; const downloadSourceSchema = z.object({ name: z.string().max(255), @@ -111,19 +111,52 @@ const addNewDownloads = async ( for (const download of downloads) { const formattedTitle = formatRepackName(download.title); - const [firstLetter] = formattedTitle; - const games = steamGames[firstLetter] || []; + let gamesInSteam: FormattedSteamGame[] = []; - const gamesInSteam = games.filter((game) => - formattedTitle.startsWith(game.formattedName) - ); + // Only try to match if we have a valid formatted title + if (formattedTitle && formattedTitle.length > 0) { + const [firstLetter] = formattedTitle; + const games = steamGames[firstLetter] || []; - if (gamesInSteam.length === 0) continue; + // Try exact prefix match first + gamesInSteam = games.filter((game) => + formattedTitle.startsWith(game.formattedName) + ); + // If no exact prefix match, try contains match (more lenient) + if (gamesInSteam.length === 0) { + gamesInSteam = games.filter( + (game) => + formattedTitle.includes(game.formattedName) || + game.formattedName.includes(formattedTitle) + ); + } + + // If still no match, try checking all letters (not just first letter) + // This helps with repacks that use abbreviations or alternate naming + if (gamesInSteam.length === 0) { + for (const letter of Object.keys(steamGames)) { + const letterGames = steamGames[letter] || []; + const matches = letterGames.filter( + (game) => + formattedTitle.includes(game.formattedName) || + game.formattedName.includes(formattedTitle) + ); + if (matches.length > 0) { + gamesInSteam = matches; + break; + } + } + } + } + + // Add matched game IDs to source tracking for (const game of gamesInSteam) { objectIdsOnSource.add(String(game.id)); } + // Create the repack even if no games matched + // This ensures all repacks from sources are imported const repack = { id: nextRepackId++, objectIds: gamesInSteam.map((game) => String(game.id)), @@ -248,8 +281,9 @@ const syncDownloadSources = async ( } } - // Invalidate cache after all sync operations complete + // Invalidate caches after all sync operations complete invalidateDownloadSourcesCache(); + invalidateIdCaches(); return newRepacksCount; } catch (err) {