diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index 92119000..6d72a7ff 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -8,8 +8,7 @@ import { Play, ListPlus, ChevronLeft, ChevronRight } from 'lucide-react'; import { CoverArtImage } from '../cover/CoverArtImage'; import { useAlbumCoverRef } from '../cover/useLibraryCoverRef'; import { useArtistBanner, useArtistFanart } from '../cover/useArtistFanart'; -import { usePlaybackCoverArt } from '../cover/usePlaybackCoverArt'; -import { artistCoverRef } from '../cover/ref'; +import { useCoverArt } from '../cover/useCoverArt'; import { useHeroBackdrop } from '../cover/useHeroBackdrop'; import { useCachedUrl } from './CachedImage'; import { usePlayerStore } from '../store/playerStore'; @@ -343,11 +342,14 @@ export default function Hero({ albums: albumsProp }: HeroProps = {}) { const heroCoverRef = useAlbumCoverRef(album?.id, album?.coverArt); const albumId = album?.id; - // Mainstage hero backdrop — the album artist's fanart, resolved exactly like - // the artist-detail header (banner → 16:9 fanart → Navidrome artist cover). + // Mainstage hero backdrop — the album artist's fanart (banner → 16:9 fanart), + // but its LAST fallback is the album's own Navidrome cover, not the artist + // image: the hero frames an album, so its base layer stays the album cover + // (the same backdrop shown when the feature is off). The artist-detail header + // keeps the artist cover as its last fallback — that surface frames an artist. // Fed entirely from the album already in hand (artist id + name + album title), // so there is no getArtist/getAlbum round-trip: the MBID lookup + fanart fetch - // live Rust-side in cover_cache, and the artist cover ref needs only the id. + // live Rust-side in cover_cache. const heroArtist = useMemo( () => (album ? deriveAlbumArtistRefs(album)[0] : undefined), [album], @@ -361,15 +363,13 @@ export default function Hero({ albums: albumsProp }: HeroProps = {}) { artistName: heroArtist?.name, albumTitle: album?.name, }); - const heroArtistCoverRef = useMemo( - () => (heroArtistId ? artistCoverRef(heroArtistId) : undefined), - [heroArtistId], - ); - const ndArtist = usePlaybackCoverArt(heroArtistCoverRef, HERO_BG_CSS_PX, { fullRes: true }); - const ndArtistUrl = useCachedUrl(ndArtist.src, ndArtist.cacheKey, true); + // Last-fallback layer: the album's own Navidrome cover (HERO_BG_CSS_PX, full + // res), resolved scope-true from the album cover ref already in hand. + const ndAlbum = useCoverArt(heroCoverRef, HERO_BG_CSS_PX, { surface: 'sparse', fullRes: true }); + const ndAlbumUrl = useCachedUrl(ndAlbum.src, ndAlbum.cacheKey, true); const heroBackdrop = useHeroBackdrop( mainstageBackdrop.sources, - { banner: heroBanner, fanart: heroFanart, navidrome: ndArtistUrl }, + { banner: heroBanner, fanart: heroFanart, navidrome: ndAlbumUrl }, albumId, ); const showHeroBackdrop = diff --git a/src/store/themeStore.ts b/src/store/themeStore.ts index c2754196..a00da4cb 100644 --- a/src/store/themeStore.ts +++ b/src/store/themeStore.ts @@ -15,13 +15,14 @@ const allEnabled = (sources: BackdropSource[]): BackdropSourcePref[] => sources.map((source) => ({ source, enabled: true })); /** - * Defaults preserve today's behaviour: both heroes resolve banner → fanart → - * Navidrome; the fullscreen player has no banner (a wide strip suits neither a - * portrait nor a square stage), so it offers fanart → Navidrome only. + * Defaults: both heroes resolve fanart → Navidrome → banner — the artist image + * leads, with the wide banner as a last resort. The fullscreen player has no + * banner (a wide strip suits neither a portrait nor a square stage), so it + * offers fanart → Navidrome only. All user-reorderable per surface. */ const DEFAULT_BACKDROPS: Record = { - mainstageHero: { enabled: true, sources: allEnabled(['banner', 'fanart', 'navidrome']) }, - artistDetailHero: { enabled: true, sources: allEnabled(['banner', 'fanart', 'navidrome']) }, + mainstageHero: { enabled: true, sources: allEnabled(['fanart', 'navidrome', 'banner']) }, + artistDetailHero: { enabled: true, sources: allEnabled(['fanart', 'navidrome', 'banner']) }, fullscreenPlayer: { enabled: true, sources: allEnabled(['fanart', 'navidrome']) }, };