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
@@ -4,6 +4,10 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
import { api } from '/@/renderer/api';
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
import {
useIsRadioActive,
useRadioPlayer,
} from '/@/renderer/features/radio/hooks/use-radio-player';
import {
DiscordDisplayType,
DiscordLinkType,
@@ -37,6 +41,9 @@ export const useDiscordRpc = () => {
const privateMode = useAppStore((state) => state.privateMode);
const [lastUniqueId, setlastUniqueId] = useState('');
const isRadioActive = useIsRadioActive();
const { isPlaying: isRadioPlaying, metadata: radioMetadata, stationName } = useRadioPlayer();
const currentSong = usePlayerSong();
const imageUrl = useItemImageUrl({
id: currentSong?.imageId || undefined,
@@ -67,14 +74,17 @@ export const useDiscordRpc = () => {
: song !== previousSong;
const trackChanged = song ? lastUniqueId !== song._uniqueId : false;
const isPlayingRadio = isRadioActive && isRadioPlaying;
const hasTrackOrRadio = Boolean(current[0]) || isPlayingRadio;
if (
!current[0] || // No track
(current[2] === 'paused' && !discordSettings.showPaused) // Track paused with show paused setting disabled
!hasTrackOrRadio || // No track and not playing radio
(current[2] === 'paused' && !discordSettings.showPaused) // Paused with show paused setting disabled
) {
let reason: string;
if (!current[0]) {
reason = 'no_track';
} else if (current[1] === 0) {
if (!hasTrackOrRadio) {
reason = current[0] ? 'no_track' : 'no_track_or_radio';
} else if (current[1] === 0 && !isPlayingRadio) {
reason = 'start_of_track';
} else {
reason = 'paused_with_show_paused_disabled';
@@ -90,6 +100,46 @@ export const useDiscordRpc = () => {
return discordRpc?.clearActivity();
}
if (isPlayingRadio) {
const title = radioMetadata?.title || stationName || 'Radio';
const artist = radioMetadata?.artist || stationName || '';
const activity: SetActivity = {
details: truncate(title),
instance: false,
largeImageKey: 'icon',
largeImageText: truncate(stationName || 'Radio'),
smallImageKey: current[2] === PlayerStatus.PLAYING ? 'playing' : 'paused',
smallImageText: sentenceCase(current[2]),
state: truncate(artist),
statusDisplayType: StatusDisplayType.STATE,
type: discordSettings.showAsListening ? 2 : 0,
};
const isConnected = await discordRpc?.isConnected();
if (!isConnected) {
logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcInitialized, {
category: LogCategory.EXTERNAL,
meta: { clientId: discordSettings.clientId },
});
previousEnabledRef.current = true;
await discordRpc?.initialize(discordSettings.clientId);
}
logFn.debug(logMsg[LogCategory.EXTERNAL].discordRpcSetActivity, {
category: LogCategory.EXTERNAL,
meta: {
currentStatus: current[2],
reason: 'radio',
showAsListening: discordSettings.showAsListening,
stationName: stationName || 'Radio',
title,
},
});
discordRpc?.setActivity(activity);
return;
}
if (!song) {
return;
}
@@ -306,6 +356,11 @@ export const useDiscordRpc = () => {
discordSettings.linkType,
lastUniqueId,
currentSong?._uniqueId,
isRadioActive,
isRadioPlaying,
radioMetadata?.artist,
radioMetadata?.title,
stationName,
],
);
@@ -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>
);
@@ -17,6 +17,10 @@ import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
import { SONG_TABLE_COLUMNS } from '/@/renderer/components/item-list/item-table-list/default-columns';
import { FullScreenPlayerImage } from '/@/renderer/features/player/components/full-screen-player-image';
import { FullScreenPlayerQueue } from '/@/renderer/features/player/components/full-screen-player-queue';
import {
useIsRadioActive,
useRadioPlayer,
} from '/@/renderer/features/radio/hooks/use-radio-player';
import { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu';
import { useFastAverageColor } from '/@/renderer/hooks';
import {
@@ -657,6 +661,11 @@ export const FullScreenPlayer = () => {
const { dynamicBackground, dynamicImageBlur, dynamicIsImage } = useFullScreenPlayerStore();
const { setStore } = useFullScreenPlayerStoreActions();
const { windowBarStyle } = useWindowSettings();
const isRadioActive = useIsRadioActive();
const { isPlaying: isRadioPlaying } = useRadioPlayer();
const isPlayingRadio = isRadioActive && isRadioPlaying;
const effectiveDynamicBackground = dynamicBackground && !isPlayingRadio;
const location = useLocation();
const isOpenedRef = useRef<boolean | null>(null);
@@ -671,13 +680,13 @@ export const FullScreenPlayer = () => {
return (
<PlayerContainer
dynamicBackground={dynamicBackground}
dynamicBackground={effectiveDynamicBackground}
dynamicIsImage={dynamicIsImage}
windowBarStyle={windowBarStyle}
>
<Controls />
<BackgroundImageOverlay
dynamicBackground={dynamicBackground}
dynamicBackground={effectiveDynamicBackground}
dynamicImageBlur={dynamicImageBlur}
/>
<div className={styles.responsiveContainer}>
@@ -50,6 +50,11 @@
object-fit: var(--theme-image-fit);
}
.radio-image {
background: var(--theme-colors-surface);
border-radius: var(--theme-radius-md);
}
.line-item {
display: inline-block;
width: 100%;
@@ -21,7 +21,9 @@ import {
useSetFullScreenPlayerStore,
} from '/@/renderer/store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Center } from '/@/shared/components/center/center';
import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon';
import { Separator } from '/@/shared/components/separator/separator';
import { Text } from '/@/shared/components/text/text';
import { Tooltip } from '/@/shared/components/tooltip/tooltip';
@@ -48,7 +50,7 @@ export const LeftControls = () => {
const { bindings } = useHotkeySettings();
const isRadioMode = isRadioActive;
const hideImage = (image && !collapsed) || isRadioMode;
const hideImage = image && !collapsed;
const isSongDefined = Boolean(currentSong?.id) && !isRadioMode;
const title = currentSong?.name;
const artists = currentSong?.artists;
@@ -116,20 +118,31 @@ export const LeftControls = () => {
})}
openDelay={0}
>
<ItemImage
className={clsx(
styles.playerbarImage,
PlaybackSelectors.playerCoverArt,
)}
enableDebounce={false}
enableViewport={false}
explicitStatus={currentSong?.explicitStatus}
fetchPriority="high"
id={currentSong?.imageId}
itemType={LibraryItem.SONG}
serverId={currentSong?._serverId}
type="table"
/>
{isRadioMode ? (
<Center
className={clsx(
styles.playerbarImage,
styles.radioImage,
)}
>
<Icon color="muted" icon="radio" size="40%" />
</Center>
) : (
<ItemImage
className={clsx(
styles.playerbarImage,
PlaybackSelectors.playerCoverArt,
)}
enableDebounce={false}
enableViewport={false}
explicitStatus={currentSong?.explicitStatus}
fetchPriority="high"
id={currentSong?.imageId}
itemType={LibraryItem.SONG}
serverId={currentSong?._serverId}
type="table"
/>
)}
</Tooltip>
{!collapsed && (
<ActionIcon
@@ -5,6 +5,10 @@ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react
import styles from './mobile-fullscreen-player.module.css';
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
import {
useIsRadioActive,
useRadioPlayer,
} from '/@/renderer/features/radio/hooks/use-radio-player';
import {
useFullScreenPlayerStore,
useImageRes,
@@ -44,9 +48,14 @@ const MotionImage = motion.img;
const ImageWithPlaceholder = ({
className,
placeholderIcon,
useImageAspectRatio,
...props
}: HTMLMotionProps<'img'> & { placeholder?: string; useImageAspectRatio?: boolean }) => {
}: HTMLMotionProps<'img'> & {
placeholder?: string;
placeholderIcon?: 'itemAlbum' | 'radio';
useImageAspectRatio?: boolean;
}) => {
if (!props.src) {
return (
<Center
@@ -57,7 +66,11 @@ const ImageWithPlaceholder = ({
width: '100%',
}}
>
<Icon color="muted" icon="itemAlbum" size="25%" />
<Icon
color="muted"
icon={placeholderIcon === 'radio' ? 'radio' : 'itemAlbum'}
size="25%"
/>
</Center>
);
}
@@ -80,9 +93,13 @@ export const MobileFullscreenPlayerAlbumArt = () => {
const { fullScreenPlayer: albumArtRes } = useImageRes();
const { useImageAspectRatio } = useFullScreenPlayerStore();
const isRadioActive = useIsRadioActive();
const { isPlaying: isRadioPlaying } = useRadioPlayer();
const currentSong = usePlayerSong();
const { nextSong } = usePlayerData();
const isPlayingRadio = isRadioActive && isRadioPlaying;
const currentImageUrl = useItemImageUrl({
id: currentSong?.imageId || undefined,
itemType: LibraryItem.SONG,
@@ -151,38 +168,58 @@ export const MobileFullscreenPlayerAlbumArt = () => {
})}
>
<AnimatePresence initial={false} mode="sync">
{imageState.current === 0 && (
{isPlayingRadio ? (
<ImageWithPlaceholder
animate="open"
className={PlaybackSelectors.playerCoverArt}
custom={{ isOpen: imageState.current === 0 }}
custom={{ isOpen: true }}
draggable={false}
exit="closed"
initial="closed"
key={`top-${currentSong?._uniqueId || 'none'}`}
key="radio"
loading="eager"
placeholder="var(--theme-colors-foreground-muted)"
src={imageState.topImage || ''}
placeholderIcon="radio"
src=""
useImageAspectRatio={useImageAspectRatio}
variants={imageVariants}
/>
)}
) : (
<>
{imageState.current === 0 && (
<ImageWithPlaceholder
animate="open"
className={PlaybackSelectors.playerCoverArt}
custom={{ isOpen: imageState.current === 0 }}
draggable={false}
exit="closed"
initial="closed"
key={`top-${currentSong?._uniqueId || 'none'}`}
loading="eager"
placeholder="var(--theme-colors-foreground-muted)"
src={imageState.topImage || ''}
useImageAspectRatio={useImageAspectRatio}
variants={imageVariants}
/>
)}
{imageState.current === 1 && (
<ImageWithPlaceholder
animate="open"
className={PlaybackSelectors.playerCoverArt}
custom={{ isOpen: imageState.current === 1 }}
draggable={false}
exit="closed"
initial="closed"
key={`bottom-${currentSong?._uniqueId || 'none'}`}
loading="eager"
placeholder="var(--theme-colors-foreground-muted)"
src={imageState.bottomImage || ''}
useImageAspectRatio={useImageAspectRatio}
variants={imageVariants}
/>
{imageState.current === 1 && (
<ImageWithPlaceholder
animate="open"
className={PlaybackSelectors.playerCoverArt}
custom={{ isOpen: imageState.current === 1 }}
draggable={false}
exit="closed"
initial="closed"
key={`bottom-${currentSong?._uniqueId || 'none'}`}
loading="eager"
placeholder="var(--theme-colors-foreground-muted)"
src={imageState.bottomImage || ''}
useImageAspectRatio={useImageAspectRatio}
variants={imageVariants}
/>
)}
</>
)}
</AnimatePresence>
</div>
@@ -16,6 +16,9 @@ interface MobileFullscreenPlayerMetadataProps {
currentSong?: QueueSong;
onToggleFavorite: (e: MouseEvent<HTMLButtonElement>) => void;
onUpdateRating: (rating: number) => void;
radioArtist?: string;
radioStationName?: string;
radioTitle?: string;
showRating?: boolean;
}
@@ -24,17 +27,24 @@ export const MobileFullscreenPlayerMetadata = memo(
currentSong,
onToggleFavorite,
onUpdateRating,
radioArtist,
radioStationName,
radioTitle,
showRating,
}: MobileFullscreenPlayerMetadataProps) => {
const title = currentSong?.name;
const artists = currentSong?.artists;
const album = currentSong?.album;
const isRadio = radioTitle !== undefined || radioStationName !== undefined;
const title = isRadio ? radioTitle || radioStationName || 'Radio' : currentSong?.name;
const artistsDisplay = isRadio
? radioArtist || radioStationName || '—'
: currentSong?.artists?.map((a) => a.name).join(', ');
const album = isRadio ? radioStationName || '—' : currentSong?.album;
const container = currentSong?.container;
const year = currentSong?.releaseYear;
const isFavorite = currentSong?.userFavorite;
const rating = currentSong?.userRating;
const hasMetadata = container || year;
const hasMetadata = !isRadio && (container || year);
return (
<div className={styles.metadataContainer}>
@@ -49,7 +59,7 @@ export const MobileFullscreenPlayerMetadata = memo(
</TextTitle>
</div>
<Text className={clsx(PlaybackSelectors.songArtist)} size="md" truncate>
{artists?.map((a) => a.name).join(', ') || '—'}
{artistsDisplay || '—'}
</Text>
<Text className={clsx(PlaybackSelectors.songAlbum)} size="md" truncate>
{album || '—'}
@@ -65,21 +75,23 @@ export const MobileFullscreenPlayerMetadata = memo(
)}
</Group>
)}
<Group align="center" className={styles.actionsRow} gap="xs">
<ActionIcon
icon="favorite"
iconProps={{
fill: isFavorite ? 'primary' : undefined,
size: 'md',
}}
onClick={onToggleFavorite}
size="sm"
variant="subtle"
/>
{showRating && (
<Rating onChange={onUpdateRating} size="sm" value={rating || 0} />
)}
</Group>
{!isRadio && (
<Group align="center" className={styles.actionsRow} gap="xs">
<ActionIcon
icon="favorite"
iconProps={{
fill: isFavorite ? 'primary' : undefined,
size: 'md',
}}
onClick={onToggleFavorite}
size="sm"
variant="subtle"
/>
{showRating && (
<Rating onChange={onUpdateRating} size="sm" value={rating || 0} />
)}
</Group>
)}
</div>
);
},
@@ -24,6 +24,10 @@ import { MobileFullscreenPlayerControls } from '/@/renderer/features/player/comp
import { MobileFullscreenPlayerHeader } from '/@/renderer/features/player/components/mobile-fullscreen-player-header';
import { MobileFullscreenPlayerMetadata } from '/@/renderer/features/player/components/mobile-fullscreen-player-metadata';
import { MobileFullscreenPlayerProgress } from '/@/renderer/features/player/components/mobile-fullscreen-player-progress';
import {
useIsRadioActive,
useRadioPlayer,
} from '/@/renderer/features/radio/hooks/use-radio-player';
import { useSetFavorite } from '/@/renderer/features/shared/hooks/use-set-favorite';
import { useSetRating } from '/@/renderer/features/shared/hooks/use-set-rating';
import { useFastAverageColor } from '/@/renderer/hooks';
@@ -376,7 +380,12 @@ export const MobileFullscreenPlayer = () => {
useFullScreenPlayerStore();
const currentSong = usePlayerSong();
const { currentSong: currentSongData } = usePlayerData();
const isRadioActive = useIsRadioActive();
const { isPlaying: isRadioPlaying, metadata: radioMetadata, stationName } = useRadioPlayer();
const server = useCurrentServer();
const isPlayingRadio = isRadioActive && isRadioPlaying;
const effectiveDynamicBackground = dynamicBackground && !isPlayingRadio;
const setFavorite = useSetFavorite();
const { showRatings: showRatingsSetting } = useGeneralSettings();
const setRating = useSetRating();
@@ -443,11 +452,11 @@ export const MobileFullscreenPlayer = () => {
return (
<MobilePlayerContainer
dynamicBackground={dynamicBackground}
dynamicBackground={effectiveDynamicBackground}
dynamicIsImage={dynamicIsImage}
>
<BackgroundImageOverlay
dynamicBackground={dynamicBackground}
dynamicBackground={effectiveDynamicBackground}
dynamicImageBlur={dynamicImageBlur}
/>
<motion.div
@@ -470,6 +479,9 @@ export const MobileFullscreenPlayer = () => {
currentSong={currentSong}
onToggleFavorite={handleToggleFavorite}
onUpdateRating={handleUpdateRating}
radioArtist={isPlayingRadio ? (radioMetadata?.artist ?? undefined) : undefined}
radioStationName={isPlayingRadio ? (stationName ?? undefined) : undefined}
radioTitle={isPlayingRadio ? (radioMetadata?.title ?? undefined) : undefined}
showRating={showRating}
/>
<MobileFullscreenPlayerProgress currentSong={currentSong} />
@@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next';
import styles from './server-selector.module.css';
import { useRadioStore } from '/@/renderer/features/radio/hooks/use-radio-player';
import JellyfinLogo from '/@/renderer/features/servers/assets/jellyfin.png';
import NavidromeLogo from '/@/renderer/features/servers/assets/navidrome.png';
import OpenSubsonicLogo from '/@/renderer/features/servers/assets/opensubsonic.png';
@@ -26,8 +25,7 @@ export const ServerSelector = () => {
const { t } = useTranslation();
const currentServer = useCurrentServer();
const sidebarImageEnabled = useAppStore((state) => state.sidebar.image);
const isRadioPlaying = useRadioStore((state) => state.isPlaying);
const showImage = sidebarImageEnabled && !isRadioPlaying;
const showImage = sidebarImageEnabled;
const { data: musicFolders } = useQuery(
currentServer
@@ -7,7 +7,10 @@ import styles from './sidebar.module.css';
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
import { ContextMenuController } from '/@/renderer/features/context-menu/context-menu-controller';
import { useRadioStore } from '/@/renderer/features/radio/hooks/use-radio-player';
import {
useIsRadioActive,
useRadioPlayer,
} from '/@/renderer/features/radio/hooks/use-radio-player';
import { ActionBar } from '/@/renderer/features/sidebar/components/action-bar';
import { ServerSelector } from '/@/renderer/features/sidebar/components/server-selector';
import { SidebarCollectionList } from '/@/renderer/features/sidebar/components/sidebar-collection-list';
@@ -32,7 +35,9 @@ import {
} from '/@/renderer/store/settings.store';
import { Accordion } from '/@/shared/components/accordion/accordion';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Center } from '/@/shared/components/center/center';
import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon';
import { ImageUnloader } from '/@/shared/components/image/image';
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
import { Text } from '/@/shared/components/text/text';
@@ -68,8 +73,7 @@ export const Sidebar = () => {
const sidebarItems = useSidebarItems();
const { windowBarStyle } = useWindowSettings();
const sidebarImageEnabled = useAppStore((state) => state.sidebar.image);
const isRadioPlaying = useRadioStore((state) => state.isPlaying);
const showImage = sidebarImageEnabled && !isRadioPlaying;
const showImage = sidebarImageEnabled;
const sidebarItemsWithRoute: SidebarItemType[] = useMemo(() => {
if (!sidebarItems) return [];
@@ -161,6 +165,8 @@ const SidebarImage = () => {
const leftWidth = useAppStore((state) => state.sidebar.leftWidth);
const { setSideBar } = useAppStoreActions();
const currentSong = usePlayerSong();
const isRadioActive = useIsRadioActive();
const { isPlaying: isRadioPlaying } = useRadioPlayer();
const imageUrl = useItemImageUrl({
id: currentSong?.imageId || undefined,
@@ -169,6 +175,7 @@ const SidebarImage = () => {
type: 'sidebar',
});
const isPlayingRadio = isRadioActive && isRadioPlaying;
const isSongDefined = Boolean(currentSong?.id);
const setFullScreenPlayerStore = useSetFullScreenPlayerStore();
@@ -181,7 +188,7 @@ const SidebarImage = () => {
e.preventDefault();
e.stopPropagation();
if (!currentSong) {
if (!currentSong || isPlayingRadio) {
return;
}
@@ -215,7 +222,19 @@ const SidebarImage = () => {
postProcess: 'sentenceCase',
})}
>
{imageUrl ? (
{isPlayingRadio ? (
<Center
className={styles.sidebarImage}
style={{
background: 'var(--theme-colors-surface)',
borderRadius: 'var(--theme-card-default-radius)',
height: '100%',
width: '100%',
}}
>
<Icon color="muted" icon="radio" size="40%" />
</Center>
) : imageUrl ? (
<img className={styles.sidebarImage} loading="eager" src={imageUrl} />
) : (
<ImageUnloader icon="emptySongImage" />