Files
Psychotoxical-psysonic/src/cover/useLibraryCoverPrefetch.ts
T
Psychotoxical 7e9cb60763 refactor(api): co-locate server-protocol clients into lib/api
Move the Subsonic + Navidrome protocol clients and the library IPC facade
(subsonic*, navidrome*, library.ts + tests) from src/api/ into src/lib/api/,
the feature-free infra layer (plan M4, decision §10.3: shared client core → lib/).
Pure move: deep-path import specifiers @/api/* -> @/lib/api/* across ~150
consumers; no behaviour change. Domain REST (lyrics/events) and cover/analysis
infra stay in src/api/ pending their own M4 placement.

tsc 0, lint 0/0, full suite 319 files / 2353 tests green.
2026-06-30 07:53:25 +02:00

62 lines
2.2 KiB
TypeScript

import { useEffect } from 'react';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { coverPrefetchRegister } from './prefetchRegistry';
import {
resolveAlbumCoverRefsFromLibrary,
resolveArtistCoverRefsFromLibrary,
resolveTrackCoverRefsFromLibrary,
} from './resolveEntryLibrary';
import { COVER_SCOPE_ACTIVE, type CoverPrefetchPriority, type CoverServerScope, type CoverSurfaceKind } from './types';
export type LibraryCoverPrefetchBucket = {
albums?: ReadonlyArray<{ id: string; coverArt?: string | null }>;
artists?: ReadonlyArray<{ id: string; coverArt?: string | null }>;
songs?: ReadonlyArray<Pick<SubsonicSong, 'id' | 'albumId' | 'coverArt' | 'discNumber'>>;
limit?: number;
priority: CoverPrefetchPriority;
surface?: CoverSurfaceKind;
serverScope?: CoverServerScope;
};
/** Register cover prefetch after resolving refs from the local library index. */
export function useLibraryCoverPrefetch(
buckets: LibraryCoverPrefetchBucket[],
deps: readonly unknown[],
): void {
useEffect(() => {
let cancelled = false;
const unregisters: Array<() => void> = [];
void (async () => {
for (const bucket of buckets) {
const scope = bucket.serverScope ?? COVER_SCOPE_ACTIVE;
const refs = [
...(bucket.albums?.length
? await resolveAlbumCoverRefsFromLibrary(bucket.albums, scope)
: []),
...(bucket.artists?.length
? await resolveArtistCoverRefsFromLibrary(bucket.artists, scope)
: []),
...(bucket.songs?.length
? await resolveTrackCoverRefsFromLibrary(bucket.songs, scope)
: []),
];
const capped = bucket.limit != null ? refs.slice(0, bucket.limit) : refs;
if (cancelled || capped.length === 0) continue;
unregisters.push(
coverPrefetchRegister(capped, {
surface: bucket.surface ?? 'dense',
priority: bucket.priority,
}),
);
}
})();
return () => {
cancelled = true;
for (const unreg of unregisters) unreg();
};
// eslint-disable-next-line react-hooks/exhaustive-deps -- caller supplies `deps` for bucket inputs
}, deps);
}