diff --git a/CHANGELOG.md b/CHANGELOG.md index 84823da44..c731a65bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Fixes - Raise Windows timer resolution during ACC and AC Evo capture so shared-memory polling no longer collapses to the default ~64 Hz tick - Make stale-session reprocessing recoverable with retry and dismissal actions, accessible progress states, and clear failure feedback +- Skip recordings from games unavailable in the current RaceIQ build during stale-session checks and bulk reprocessing - Open RaceIQ faster by skipping unnecessary historical race-result work during startup - Show actionable, neutral guidance when AI provider, credentials, or model configuration is incomplete - Keep iRacing lap replay within saved frame boundaries so telemetry from the following lap is not included diff --git a/server/db/session-queries.ts b/server/db/session-queries.ts index fcbc0f03c..dfbdecf21 100644 --- a/server/db/session-queries.ts +++ b/server/db/session-queries.ts @@ -63,7 +63,10 @@ export async function updateSessionRawFile( * cards and per-game pages now both report the full picture. */ -export async function countStaleSessions(currentIds: string | string[]): Promise { +export async function countStaleSessions( + currentIds: string | string[], + reprocessableGameIds: GameId[], +): Promise { const ids = Array.isArray(currentIds) ? currentIds : [currentIds]; const rows = await db .select({ id: sessions.id }) @@ -71,6 +74,7 @@ export async function countStaleSessions(currentIds: string | string[]): Promise .where( and( sql`${sessions.rawFile} IS NOT NULL`, + inArray(sessions.gameId, reprocessableGameIds), or(isNull(sessions.lapDetectorVersion), notInArray(sessions.lapDetectorVersion, ids)) ) ) @@ -82,7 +86,10 @@ export async function countStaleSessions(currentIds: string | string[]): Promise * Get IDs of sessions with stale lap detector version that have a raw file. */ -export async function getStaleSessions(currentIds: string | string[]): Promise { +export async function getStaleSessions( + currentIds: string | string[], + reprocessableGameIds: GameId[], +): Promise { const ids = Array.isArray(currentIds) ? currentIds : [currentIds]; const rows = await db .select({ id: sessions.id }) @@ -90,6 +97,7 @@ export async function getStaleSessions(currentIds: string | string[]): Promise adapter.id), + ); if (remaining === 0) wsManager.setStaleSessionsNotification(null); return c.json(result); }) .post("/api/sessions/reprocess-stale", async (c) => { - const staleIds = await getStaleSessions(ALL_DETECTOR_IDS); + const staleIds = await getStaleSessions( + ALL_DETECTOR_IDS, + getAllServerGames().map((adapter) => adapter.id), + ); const results = []; for (const id of staleIds) { const result = await reprocessSession(id); diff --git a/server/runtime/startup-jobs.ts b/server/runtime/startup-jobs.ts index 3da2c083b..578998a95 100644 --- a/server/runtime/startup-jobs.ts +++ b/server/runtime/startup-jobs.ts @@ -7,6 +7,7 @@ import { LAP_DETECTOR_ID } from "../lap-detection/detector"; import { LAP_DETECTOR_ACC_ID } from "../games/acc/lap-detector"; import { LAP_DETECTOR_AC_EVO_ID } from "../games/ac-evo/lap-detector"; import { LAP_DETECTOR_IRACING_ID } from "../games/iracing/lap-detector"; +import { getAllServerGames } from "../games/registry"; import { wsManager } from "./websocket-manager"; import { startSessionCompressor } from "../session-capture/compressor"; import { startUpdateCheckSchedule } from "./update/check"; @@ -31,7 +32,10 @@ export function startSyncAndStaleSessionJobs(dependencies: StartupJobDependencie (dependencies.startCommunityTunesSync ?? startCommunityTunesSync)(); (dependencies.startLaptimesSync ?? startLaptimesSync)(); - (dependencies.countStaleSessions ?? countStaleSessions)(ALL_DETECTOR_IDS).then((count) => { + (dependencies.countStaleSessions ?? countStaleSessions)( + ALL_DETECTOR_IDS, + getAllServerGames().map((adapter) => adapter.id), + ).then((count) => { if (count > 0) { console.log(`[Server] ${count} session(s) recorded with stale lap detector — will prompt user to reprocess`); wsManager.setStaleSessionsNotification({ diff --git a/test/session-capture/raw-binary-storage.test.ts b/test/session-capture/raw-binary-storage.test.ts index 6f9d73093..d741dd7ea 100644 --- a/test/session-capture/raw-binary-storage.test.ts +++ b/test/session-capture/raw-binary-storage.test.ts @@ -280,10 +280,14 @@ describe("countStaleSessions", () => { insertedIds.length = 0; }); - async function insertSession(rawFile: string | null, lapDetectorVersion: string | null): Promise { + async function insertSession( + rawFile: string | null, + lapDetectorVersion: string | null, + gameId = "fm-2023", + ): Promise { const row = await db .insert(sessions) - .values({ carOrdinal: 1, trackOrdinal: 1, gameId: "fm-2023", rawFile, lapDetectorVersion }) + .values({ carOrdinal: 1, trackOrdinal: 1, gameId, rawFile, lapDetectorVersion }) .returning({ id: sessions.id }) .get(); const id = row!.id; @@ -292,28 +296,30 @@ describe("countStaleSessions", () => { } test("counts only raw sessions with stale detector versions", async () => { - const beforeCount = await countStaleSessions(detectorId); + const beforeCount = await countStaleSessions(detectorId, ["fm-2023"]); await insertSession("/some/path-old.bin", "lapdetector_v0"); await insertSession("/some/path-null.bin", null); await insertSession("/some/path-current.bin", detectorId); await insertSession(null, null); + await insertSession("/some/path-unsupported.bin", "lapdetector_v0", "lmu"); - const afterCount = await countStaleSessions(detectorId); + const afterCount = await countStaleSessions(detectorId, ["fm-2023"]); expect(afterCount - beforeCount).toBe(2); }); test("getStaleSessions returns only raw sessions with stale detector versions", async () => { - const baselineIds = await getStaleSessions(detectorId); + const baselineIds = await getStaleSessions(detectorId, ["fm-2023"]); const baselineSet = new Set(baselineIds); const staleRawOldVersion = await insertSession("/some/path-old.bin", "lapdetector_v0"); const staleRawNullVersion = await insertSession("/some/path-null.bin", null); const currentVersion = await insertSession("/some/path-current.bin", detectorId); const noRaw = await insertSession(null, null); + const unsupportedGame = await insertSession("/some/path-unsupported.bin", "lapdetector_v0", "lmu"); - const allIds = await getStaleSessions(detectorId); + const allIds = await getStaleSessions(detectorId, ["fm-2023"]); const insertedIdsOnly = allIds.filter((id) => !baselineSet.has(id)); expect(insertedIdsOnly).toHaveLength(2); @@ -321,5 +327,6 @@ describe("countStaleSessions", () => { expect(insertedIdsOnly).toContain(staleRawNullVersion); expect(insertedIdsOnly).not.toContain(currentVersion); expect(insertedIdsOnly).not.toContain(noRaw); + expect(insertedIdsOnly).not.toContain(unsupportedGame); }); });