mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 06:25:41 +00:00
fix(fullscreen): preflight artist portrait URL so broken-img glyph never shows (#748)
Navidrome / Subsonic `getArtistInfo2` often returns a `largeImageUrl` that 404s when the artist has no scraped image. The hook returned the URL as-is, the FullscreenPlayer's `artistBgUrl || coverUrl` chain saw a non-empty string and skipped the cover-art fallback, and FsPortrait rendered an `<img>` whose `src` failed — leaving the browser's default broken-image glyph in the middle of the screen. Preflight the URL via an `Image()` probe and only expose it once it actually loads; on error leave the hook's state empty so the cover-art fallback in the caller takes effect.
This commit is contained in:
committed by
GitHub
parent
4916c4a36f
commit
ebba3dfa09
@@ -3,17 +3,32 @@ import { getArtistInfo } from '../api/subsonicArtists';
|
|||||||
|
|
||||||
/** Fetches the large artist image for the given artist id, returning '' until
|
/** Fetches the large artist image for the given artist id, returning '' until
|
||||||
* the request resolves (or when there is no artist id). Falls through silently
|
* the request resolves (or when there is no artist id). Falls through silently
|
||||||
* on network failures — the caller should layer a cover-art fallback on top. */
|
* on network failures — the caller should layer a cover-art fallback on top.
|
||||||
|
*
|
||||||
|
* Navidrome / Subsonic backends often return a `largeImageUrl` that 404s when
|
||||||
|
* the artist has no scraped image (Last.fm placeholder), so the URL is
|
||||||
|
* preflighted via an Image() probe and only exposed when it actually loads.
|
||||||
|
* Otherwise the caller would render a broken-img glyph (`?`) on top of the
|
||||||
|
* fullscreen background — see zunoz report on the Psysonic Discord. */
|
||||||
export function useFsArtistPortrait(artistId: string | undefined): string {
|
export function useFsArtistPortrait(artistId: string | undefined): string {
|
||||||
const [artistBgUrl, setArtistBgUrl] = useState<string>('');
|
const [artistBgUrl, setArtistBgUrl] = useState<string>('');
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setArtistBgUrl('');
|
setArtistBgUrl('');
|
||||||
if (!artistId) return;
|
if (!artistId) return;
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
|
let probe: HTMLImageElement | null = null;
|
||||||
getArtistInfo(artistId).then(info => {
|
getArtistInfo(artistId).then(info => {
|
||||||
if (!cancelled && info.largeImageUrl) setArtistBgUrl(info.largeImageUrl);
|
if (cancelled || !info.largeImageUrl) return;
|
||||||
|
const url = info.largeImageUrl;
|
||||||
|
probe = new Image();
|
||||||
|
probe.onload = () => { if (!cancelled) setArtistBgUrl(url); };
|
||||||
|
probe.onerror = () => { /* leave empty so caller falls back to cover */ };
|
||||||
|
probe.src = url;
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
return () => { cancelled = true; };
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
if (probe) probe.onload = probe.onerror = null;
|
||||||
|
};
|
||||||
}, [artistId]);
|
}, [artistId]);
|
||||||
return artistBgUrl;
|
return artistBgUrl;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user