IGDB integration: auto-scrape on upload + manual scrape button

- New IgdbService: Twitch OAuth2, IGDB API v4 search, cover download
- Upload auto-scrapes IGDB after extracting game ZIP
- GameDetail page: 'Auto Scrape' + 'Search' buttons + result picker
- Upload dialog: optional title field (defaults to filename)
- Jackson SNAKE_CASE naming to match frontend expectations
- IGDB status endpoint to check credentials
This commit is contained in:
David Alvarez
2026-05-25 10:22:58 +02:00
parent f2bd6ca3fb
commit 466299b8f0
8 changed files with 779 additions and 32 deletions
+28 -2
View File
@@ -59,9 +59,35 @@ export async function uploadGame(file, title) {
return res.json();
}
/** Search IGDB (placeholder) */
/** Search IGDB for a game title (requires TWITCH_CLIENT_ID/SECRET set) */
export async function searchIGDB(query) {
return request(`/api/igdb/search?query=${encodeURIComponent(query)}`);
const res = await fetch(`${BASE}/api/igdb/search?q=${encodeURIComponent(query)}`);
if (!res.ok) {
const err = await res.json().catch(() => ({ error: res.statusText }));
throw new Error(err.error || `HTTP ${res.status}`);
}
return res.json();
}
/** Apply IGDB metadata + cover to a game (auto or by igdb_id) */
export async function scrapeIGDB(gameId, igdbId) {
const body = igdbId ? { igdb_id: igdbId } : {};
const res = await fetch(`${BASE}/api/igdb/scrape/${gameId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!res.ok) {
const err = await res.json().catch(() => ({ error: res.statusText }));
throw new Error(err.error || `HTTP ${res.status}`);
}
return res.json();
}
/** Check IGDB status */
export async function igdbStatus() {
const res = await fetch(`${BASE}/api/igdb/status`);
return res.json();
}
/** Get artwork URL for a path — returns proxy URL or placeholder */