mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 14:35:41 +00:00
perf(fullscreen): fix dynamic accent color delay via direct fetch + cache
- Bypass ImageCache 5-slot queue: fetch cover art directly, create blob URL for canvas extraction, revoke immediately after use - Module-level cache (artKey → accent) avoids re-fetching within the same album — same-cover tracks get the color instantly - Keep previous color visible on cache miss instead of flashing to theme color Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -245,6 +245,10 @@ interface FullscreenPlayerProps {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Module-level cache: artKey → accent color string.
|
||||||
|
// Survives track changes so same-album songs reuse the extracted color instantly.
|
||||||
|
const coverAccentCache = new Map<string, string>();
|
||||||
|
|
||||||
export default function FullscreenPlayer({ onClose }: FullscreenPlayerProps) {
|
export default function FullscreenPlayer({ onClose }: FullscreenPlayerProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const currentTrack = usePlayerStore(s => s.currentTrack);
|
const currentTrack = usePlayerStore(s => s.currentTrack);
|
||||||
@@ -291,18 +295,35 @@ export default function FullscreenPlayer({ onClose }: FullscreenPlayerProps) {
|
|||||||
// Reset to null on track change so the previous color doesn't linger while
|
// Reset to null on track change so the previous color doesn't linger while
|
||||||
// the new one is being extracted.
|
// the new one is being extracted.
|
||||||
const [dynamicAccent, setDynamicAccent] = useState<string | null>(null);
|
const [dynamicAccent, setDynamicAccent] = useState<string | null>(null);
|
||||||
// Reset immediately on track change so the previous color doesn't linger.
|
|
||||||
useEffect(() => { setDynamicAccent(null); }, [artKey]);
|
// On cover change: hit cache for instant result, or fetch → extract → cache.
|
||||||
// Extract as soon as the display blob is ready — reuses resolvedCoverUrl so
|
// Cache hit avoids re-fetching for same-album tracks. Reset only when uncached.
|
||||||
// no redundant network request for a separate cover size.
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!resolvedCoverUrl) return;
|
if (!artKey || !artUrl) { setDynamicAccent(null); return; }
|
||||||
|
const cached = coverAccentCache.get(artKey);
|
||||||
|
if (cached) { setDynamicAccent(cached); return; }
|
||||||
|
// No cache hit — keep the previous color visible until extraction completes.
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
extractCoverColors(resolvedCoverUrl).then(colors => {
|
let blobUrl = '';
|
||||||
if (!cancelled && colors.accent) setDynamicAccent(colors.accent);
|
(async () => {
|
||||||
});
|
try {
|
||||||
|
const resp = await fetch(artUrl);
|
||||||
|
if (cancelled) return;
|
||||||
|
const blob = await resp.blob();
|
||||||
|
if (cancelled) return;
|
||||||
|
blobUrl = URL.createObjectURL(blob);
|
||||||
|
const colors = await extractCoverColors(blobUrl);
|
||||||
|
if (cancelled) return;
|
||||||
|
if (colors.accent) {
|
||||||
|
coverAccentCache.set(artKey, colors.accent);
|
||||||
|
setDynamicAccent(colors.accent);
|
||||||
|
}
|
||||||
|
} catch { /* ignore */ } finally {
|
||||||
|
if (blobUrl) URL.revokeObjectURL(blobUrl);
|
||||||
|
}
|
||||||
|
})();
|
||||||
return () => { cancelled = true; };
|
return () => { cancelled = true; };
|
||||||
}, [resolvedCoverUrl]);
|
}, [artKey]);
|
||||||
|
|
||||||
// Artist image → portrait on right. Falls back to cover art.
|
// Artist image → portrait on right. Falls back to cover art.
|
||||||
const [artistBgUrl, setArtistBgUrl] = useState<string>('');
|
const [artistBgUrl, setArtistBgUrl] = useState<string>('');
|
||||||
|
|||||||
Reference in New Issue
Block a user