IGDB auto-enrichment: Server-Start + Frontend auto-trigger
- Server enriched bestehende User beim Plugin-Start automatisch (fire-and-forget) - Frontend triggert IGDB-Enrichment automatisch beim Öffnen einer User-Bibliothek - Reduzierte Log-Ausgabe (kein Spam pro Cache-Hit mehr) - IGDB-Button zeigt Lade-Animation während Enrichment Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
916006e815
commit
4994d5c245
4 changed files with 68 additions and 6 deletions
|
|
@ -271,14 +271,18 @@ export async function enrichGames(
|
||||||
for (const game of games) {
|
for (const game of games) {
|
||||||
const cached = enrichmentCache.get(game.appid);
|
const cached = enrichmentCache.get(game.appid);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
console.log(`[IGDB] Cache hit for appid ${game.appid} ("${cached.name}")`);
|
|
||||||
result.set(game.appid, cached);
|
result.set(game.appid, cached);
|
||||||
} else {
|
} else {
|
||||||
toFetch.push(game);
|
toFetch.push(game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toFetch.length === 0) return result;
|
if (toFetch.length === 0) {
|
||||||
|
console.log(`[IGDB] All ${games.length} games found in cache`);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[IGDB] ${result.size} cache hits, ${toFetch.length} to fetch`);
|
||||||
|
|
||||||
const BATCH_SIZE = 4;
|
const BATCH_SIZE = 4;
|
||||||
for (let i = 0; i < toFetch.length; i += BATCH_SIZE) {
|
for (let i = 0; i < toFetch.length; i += BATCH_SIZE) {
|
||||||
|
|
|
||||||
|
|
@ -118,8 +118,41 @@ const gameLibraryPlugin: Plugin = {
|
||||||
description: 'Steam Spielebibliothek',
|
description: 'Steam Spielebibliothek',
|
||||||
|
|
||||||
async init(ctx) {
|
async init(ctx) {
|
||||||
loadData(ctx); // ensure file exists
|
const data = loadData(ctx); // ensure file exists
|
||||||
console.log('[GameLibrary] Initialized');
|
console.log('[GameLibrary] Initialized');
|
||||||
|
|
||||||
|
// Fire-and-forget: auto-enrich all existing users with IGDB data
|
||||||
|
const allUsers = Object.values(data.users);
|
||||||
|
const unenrichedUsers = allUsers.filter(u =>
|
||||||
|
u.games.some(g => !g.igdb),
|
||||||
|
);
|
||||||
|
if (unenrichedUsers.length > 0) {
|
||||||
|
console.log(`[GameLibrary] Auto-enriching ${unenrichedUsers.length} user(s) with IGDB data...`);
|
||||||
|
(async () => {
|
||||||
|
for (const user of unenrichedUsers) {
|
||||||
|
try {
|
||||||
|
const unenrichedGames = user.games.filter(g => !g.igdb).map(g => ({ appid: g.appid, name: g.name }));
|
||||||
|
console.log(`[GameLibrary] IGDB auto-enriching ${user.personaName}: ${unenrichedGames.length} games...`);
|
||||||
|
const igdbMap = await enrichGames(unenrichedGames);
|
||||||
|
// Reload fresh to avoid stale writes
|
||||||
|
const freshData = loadData(ctx);
|
||||||
|
const freshUser = freshData.users[user.steamId];
|
||||||
|
if (freshUser) {
|
||||||
|
let count = 0;
|
||||||
|
for (const game of freshUser.games) {
|
||||||
|
const info = igdbMap.get(game.appid);
|
||||||
|
if (info) { game.igdb = info; count++; }
|
||||||
|
}
|
||||||
|
saveData(ctx, freshData);
|
||||||
|
console.log(`[GameLibrary] IGDB startup enrichment for ${user.personaName}: ${count}/${unenrichedGames.length} games matched`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[GameLibrary] IGDB startup enrichment error for ${user.personaName}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('[GameLibrary] IGDB startup enrichment complete.');
|
||||||
|
})();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
registerRoutes(app: express.Application, ctx: PluginContext) {
|
registerRoutes(app: express.Application, ctx: PluginContext) {
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,23 @@ export default function GameLibraryTab({ data }: { data: any }) {
|
||||||
const resp = await fetch(`/api/game-library/user/${steamId}`);
|
const resp = await fetch(`/api/game-library/user/${steamId}`);
|
||||||
if (resp.ok) {
|
if (resp.ok) {
|
||||||
const d = await resp.json();
|
const d = await resp.json();
|
||||||
setUserGames(d.games || d);
|
const games: SteamGame[] = d.games || d;
|
||||||
|
setUserGames(games);
|
||||||
|
|
||||||
|
// Auto-enrich with IGDB if many games lack data
|
||||||
|
const unenriched = games.filter(g => !g.igdb).length;
|
||||||
|
if (unenriched > 0) {
|
||||||
|
setEnriching(steamId);
|
||||||
|
fetch(`/api/game-library/igdb/enrich/${steamId}`)
|
||||||
|
.then(r => r.ok ? r.json() : null)
|
||||||
|
.then(() => fetch(`/api/game-library/user/${steamId}`))
|
||||||
|
.then(r => r.ok ? r.json() : null)
|
||||||
|
.then(d2 => {
|
||||||
|
if (d2) setUserGames(d2.games || d2);
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
.finally(() => setEnriching(null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
/* silent */
|
/* silent */
|
||||||
|
|
@ -439,10 +455,10 @@ export default function GameLibraryTab({ data }: { data: any }) {
|
||||||
↻
|
↻
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="gl-enrich-btn"
|
className={`gl-enrich-btn ${enriching === selectedUser ? 'enriching' : ''}`}
|
||||||
onClick={() => enrichUser(selectedUser!)}
|
onClick={() => enrichUser(selectedUser!)}
|
||||||
disabled={enriching === selectedUser}
|
disabled={enriching === selectedUser}
|
||||||
title="Mit IGDB-Daten anreichern"
|
title={enriching === selectedUser ? 'IGDB-Daten werden geladen...' : 'Mit IGDB-Daten anreichern (erneut)'}
|
||||||
>
|
>
|
||||||
{enriching === selectedUser ? '\u23F3' : '\uD83C\uDF10'} IGDB
|
{enriching === selectedUser ? '\u23F3' : '\uD83C\uDF10'} IGDB
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -553,6 +553,15 @@
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gl-enrich-btn.enriching {
|
||||||
|
animation: gl-pulse 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes gl-pulse {
|
||||||
|
0%, 100% { opacity: 0.5; }
|
||||||
|
50% { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Responsive ── */
|
/* ── Responsive ── */
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue