handle radio metadata in discord rpc / fullscreen player (#1649)

This commit is contained in:
jeffvli
2026-02-05 19:14:30 -08:00
parent 4c256348fc
commit 027e4046a2
10 changed files with 332 additions and 124 deletions
@@ -6,6 +6,10 @@ import { generatePath, Link } from 'react-router';
import styles from './full-screen-player-image.module.css';
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
import {
useIsRadioActive,
useRadioPlayer,
} from '/@/renderer/features/radio/hooks/use-radio-player';
import { AppRoute } from '/@/renderer/router/routes';
import { useNativeAspectRatio, usePlayerData, usePlayerSong } from '/@/renderer/store';
import { Badge } from '/@/shared/components/badge/badge';
@@ -45,8 +49,9 @@ const MotionImage = motion.img;
const ImageWithPlaceholder = ({
className,
placeholderIcon = 'itemAlbum',
...props
}: HTMLMotionProps<'img'> & { placeholder?: string }) => {
}: HTMLMotionProps<'img'> & { placeholder?: string; placeholderIcon?: 'itemAlbum' | 'radio' }) => {
const nativeAspectRatio = useNativeAspectRatio();
if (!props.src) {
@@ -59,7 +64,7 @@ const ImageWithPlaceholder = ({
width: '100%',
}}
>
<Icon color="muted" icon="itemAlbum" size="25%" />
<Icon color="muted" icon={placeholderIcon} size="25%" />
</Center>
);
}
@@ -79,9 +84,14 @@ const ImageWithPlaceholder = ({
export const FullScreenPlayerImage = () => {
const mainImageRef = useRef<HTMLImageElement | null>(null);
const isRadioActive = useIsRadioActive();
const { isPlaying: isRadioPlaying, metadata: radioMetadata, stationName } = useRadioPlayer();
const currentSong = usePlayerSong();
const { nextSong } = usePlayerData();
const isPlayingRadio = isRadioActive && isRadioPlaying;
const currentImageUrl = useItemImageUrl({
id: currentSong?.imageId || undefined,
itemType: LibraryItem.SONG,
@@ -111,8 +121,11 @@ export const FullScreenPlayerImage = () => {
imageStateRef.current = imageState;
}, [imageState]);
// Update images when song or size changes
// Update images when song or size changes (skip when playing radio - no album art)
useEffect(() => {
if (isPlayingRadio) {
return;
}
if (currentSong?._uniqueId === previousSongRef.current) {
return;
}
@@ -126,7 +139,14 @@ export const FullScreenPlayerImage = () => {
});
previousSongRef.current = currentSong?._uniqueId;
}, [currentSong?._uniqueId, currentImageUrl, nextSong?._uniqueId, nextImageUrl, setImageState]);
}, [
isPlayingRadio,
currentSong?._uniqueId,
currentImageUrl,
nextSong?._uniqueId,
nextImageUrl,
setImageState,
]);
return (
<Flex
@@ -138,7 +158,7 @@ export const FullScreenPlayerImage = () => {
>
<div className={styles.imageContainer} ref={mainImageRef}>
<AnimatePresence initial={false} mode="sync">
{imageState.current === 0 && (
{!isPlayingRadio && imageState.current === 0 && (
<ImageWithPlaceholder
animate="open"
className="full-screen-player-image"
@@ -153,7 +173,7 @@ export const FullScreenPlayerImage = () => {
/>
)}
{imageState.current === 1 && (
{!isPlayingRadio && imageState.current === 1 && (
<ImageWithPlaceholder
animate="open"
className="full-screen-player-image"
@@ -167,57 +187,85 @@ export const FullScreenPlayerImage = () => {
variants={imageVariants}
/>
)}
{isPlayingRadio && (
<ImageWithPlaceholder
animate="open"
className="full-screen-player-image"
custom={{ isOpen: true }}
draggable={false}
exit="closed"
initial="closed"
key="radio"
placeholder="var(--theme-colors-foreground-muted)"
placeholderIcon="radio"
src=""
variants={imageVariants}
/>
)}
</AnimatePresence>
</div>
<Stack className={styles.metadataContainer} gap="md" maw="100%">
<Text fw={900} lh="1.2" overflow="hidden" size="4xl" w="100%">
{currentSong?.name}
</Text>
<Text
component={Link}
isLink
overflow="hidden"
size="xl"
to={generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, {
albumId: currentSong?.albumId || '',
})}
w="100%"
>
{currentSong?.album}
{isPlayingRadio
? radioMetadata?.title || stationName || 'Radio'
: currentSong?.name}
</Text>
{isPlayingRadio ? (
<Text overflow="hidden" size="xl" w="100%">
{stationName || 'Radio'}
</Text>
) : (
<Text
component={Link}
isLink
overflow="hidden"
size="xl"
to={generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, {
albumId: currentSong?.albumId || '',
})}
w="100%"
>
{currentSong?.album}
</Text>
)}
<Text key="fs-artists">
{currentSong?.artists?.map((artist, index) => (
<Fragment key={`fs-artist-${artist.id}`}>
{index > 0 && (
<Text
style={{
display: 'inline-block',
padding: '0 0.5rem',
}}
>
</Text>
)}
<Text
component={Link}
isLink
to={generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, {
albumArtistId: artist.id,
})}
>
{artist.name}
</Text>
</Fragment>
))}
{isPlayingRadio
? radioMetadata?.artist || stationName || 'Radio'
: currentSong?.artists?.map((artist, index) => (
<Fragment key={`fs-artist-${artist.id}`}>
{index > 0 && (
<Text
style={{
display: 'inline-block',
padding: '0 0.5rem',
}}
>
</Text>
)}
<Text
component={Link}
isLink
to={generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, {
albumArtistId: artist.id,
})}
>
{artist.name}
</Text>
</Fragment>
))}
</Text>
<Group justify="center" mt="sm">
{currentSong?.container && (
<Badge variant="transparent">{currentSong?.container}</Badge>
)}
{currentSong?.releaseYear && (
<Badge variant="transparent">{currentSong?.releaseYear}</Badge>
)}
</Group>
{!isPlayingRadio && (
<Group justify="center" mt="sm">
{currentSong?.container && (
<Badge variant="transparent">{currentSong?.container}</Badge>
)}
{currentSong?.releaseYear && (
<Badge variant="transparent">{currentSong?.releaseYear}</Badge>
)}
</Group>
)}
</Stack>
</Flex>
);