mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
c88649836e
The two parallel maps that bound the per-track loudness backfill retries (`analysisBackfillInFlightByTrackId`, `analysisBackfillAttemptsByTrackId`) plus the `MAX_BACKFILL_ATTEMPTS_PER_TRACK` constant and the `resetLoudnessBackfillStateForTrackId` reseed helper move into `src/store/loudnessBackfillState.ts`. The new module exposes a thin API: - `isBackfillInFlight` / `getBackfillAttempts` (reads) - `markBackfillInFlight(trackId, nextAttempt)` (atomic flag + counter) - `clearBackfillInFlight` (after promise settles) - `resetBackfillAttempts` (after refresh-hit) - `resetLoudnessBackfillStateForTrackId` (full reset across both id forms) playerStore's five direct-access sites inside `refreshLoudnessForTrack` become API calls; the mutables are no longer reachable from outside the module. 13 focused tests pin atomicity, independence between tracks, the partial-clear shapes (flag-only / counter-only), and the two-form reseed expansion. playerStore 3263 → 3261 LOC (small line delta because the inflight flag + counter setup collapses to one call but the reset helper is no longer inline).
57 lines
2.4 KiB
TypeScript
57 lines
2.4 KiB
TypeScript
import { loudnessCacheStateKeysForTrackId } from './loudnessGainCache';
|
|
|
|
/**
|
|
* Bounded retry state for the per-track loudness backfill: each `refresh:miss`
|
|
* for a track in loudness mode enqueues an `analysis_enqueue_seed_from_url`
|
|
* job, but only if (a) no enqueue is already inflight for that id and
|
|
* (b) the per-track attempt counter is below `MAX_BACKFILL_ATTEMPTS_PER_TRACK`.
|
|
* A `refresh:hit` resets the counter so the next miss starts fresh.
|
|
*
|
|
* Both maps stay keyed by the raw track id passed by the caller — the
|
|
* `loudnessCacheStateKeysForTrackId` expansion only matters when clearing
|
|
* during a reseed (`resetLoudnessBackfillStateForTrackId`).
|
|
*/
|
|
|
|
export const MAX_BACKFILL_ATTEMPTS_PER_TRACK = 2;
|
|
|
|
const analysisBackfillInFlightByTrackId: Record<string, true> = {};
|
|
const analysisBackfillAttemptsByTrackId: Record<string, number> = {};
|
|
|
|
export function isBackfillInFlight(trackId: string): boolean {
|
|
return Boolean(analysisBackfillInFlightByTrackId[trackId]);
|
|
}
|
|
|
|
export function getBackfillAttempts(trackId: string): number {
|
|
return analysisBackfillAttemptsByTrackId[trackId] ?? 0;
|
|
}
|
|
|
|
/** Atomic: flag the track inflight AND bump the attempt counter to `nextAttempt`. */
|
|
export function markBackfillInFlight(trackId: string, nextAttempt: number): void {
|
|
analysisBackfillInFlightByTrackId[trackId] = true;
|
|
analysisBackfillAttemptsByTrackId[trackId] = nextAttempt;
|
|
}
|
|
|
|
/** Clear the inflight flag (called from the `.finally` of the enqueue promise). */
|
|
export function clearBackfillInFlight(trackId: string): void {
|
|
delete analysisBackfillInFlightByTrackId[trackId];
|
|
}
|
|
|
|
/** Reset the attempt counter to 0 — called after a `refresh:hit`. */
|
|
export function resetBackfillAttempts(trackId: string): void {
|
|
analysisBackfillAttemptsByTrackId[trackId] = 0;
|
|
}
|
|
|
|
/** Full reset for both maps across the bare + `stream:` id forms — used during a reseed. */
|
|
export function resetLoudnessBackfillStateForTrackId(trackId: string): void {
|
|
for (const k of loudnessCacheStateKeysForTrackId(trackId)) {
|
|
delete analysisBackfillInFlightByTrackId[k];
|
|
analysisBackfillAttemptsByTrackId[k] = 0;
|
|
}
|
|
}
|
|
|
|
/** Test-only: wipe both maps so each spec starts clean. */
|
|
export function _resetBackfillStateForTest(): void {
|
|
for (const k of Object.keys(analysisBackfillInFlightByTrackId)) delete analysisBackfillInFlightByTrackId[k];
|
|
for (const k of Object.keys(analysisBackfillAttemptsByTrackId)) delete analysisBackfillAttemptsByTrackId[k];
|
|
}
|