From 2c8141660c1bac6b122ec3d4e5db2a866c22387a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20F=C3=B6rtsch?= Date: Mon, 2 Mar 2026 23:40:00 +0100 Subject: [PATCH] fix IGDB resolution: drop broken category filter, match by URL prefix the IGDB external_games category filter returns empty for all values. filter steam/gog entries by URL prefix instead (store.steampowered.com, gog.com). reduce batch size to 50 to stay within 500-result API limit since each uid can return multiple platform entries. Co-Authored-By: Claude Opus 4.6 --- server/src/features/igdb/service.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/server/src/features/igdb/service.ts b/server/src/features/igdb/service.ts index 730ef4d..7c18fdb 100644 --- a/server/src/features/igdb/service.ts +++ b/server/src/features/igdb/service.ts @@ -2,12 +2,9 @@ import { env } from "../../shared/lib/env.ts" import { getCacheEntry, getCacheSize, saveCache, setCacheEntry } from "./cache.ts" import { type IgdbMetadata, getMetadata, setMetadataBatch } from "./metadata-cache.ts" -const CATEGORY_STEAM = 1 -const CATEGORY_GOG = 2 - -const SOURCE_TO_CATEGORY: Record = { - steam: CATEGORY_STEAM, - gog: CATEGORY_GOG, +const SOURCE_URL_PREFIX: Record = { + steam: "https://store.steampowered.com/app/", + gog: "https://www.gog.com/", } let twitchToken: string | null = null @@ -65,22 +62,28 @@ async function igdbRequest(endpoint: string, query: string): Promise const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) -async function batchResolve(category: number, sourceIds: string[]): Promise> { +async function batchResolve(source: string, sourceIds: string[]): Promise> { const results = new Map() - const BATCH_SIZE = 500 + const urlPrefix = SOURCE_URL_PREFIX[source] + if (!urlPrefix) return results + + // Without category filter, each uid may return multiple platform entries, + // so keep batches small to stay within the 500-result API limit + const BATCH_SIZE = 50 for (let i = 0; i < sourceIds.length; i += BATCH_SIZE) { const batch = sourceIds.slice(i, i + BATCH_SIZE) const uids = batch.map((id) => `"${id}"`).join(",") - const query = `fields game,uid; where category = ${category} & uid = (${uids}); limit ${BATCH_SIZE};` + const query = `fields game,uid,url; where uid = (${uids}); limit 500;` const data = (await igdbRequest("/external_games", query)) as Array<{ game?: number uid?: string + url?: string }> for (const entry of data) { - if (entry.game && entry.uid) { + if (entry.game && entry.uid && entry.url?.startsWith(urlPrefix)) { results.set(entry.uid, entry.game) } } @@ -109,7 +112,7 @@ export async function enrichGamesWithIgdb( const uncachedBySource: Record = {} for (const game of games) { const cacheKey = `${game.source}:${game.sourceId}` - if (!getCacheEntry(cacheKey) && SOURCE_TO_CATEGORY[game.source]) { + if (!getCacheEntry(cacheKey) && SOURCE_URL_PREFIX[game.source]) { if (!uncachedBySource[game.source]) { uncachedBySource[game.source] = [] } @@ -120,10 +123,9 @@ export async function enrichGamesWithIgdb( let newEntries = 0 try { for (const [source, sourceIds] of Object.entries(uncachedBySource)) { - const category = SOURCE_TO_CATEGORY[source] console.log(`[IGDB] Resolving ${sourceIds.length} ${source} games...`) - const resolved = await batchResolve(category, sourceIds) + const resolved = await batchResolve(source, sourceIds) for (const [uid, igdbId] of resolved) { setCacheEntry(`${source}:${uid}`, { igdbId })