mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-15 13:00:25 +02:00
handle radio metadata in discord rpc / fullscreen player (#1649)
This commit is contained in:
@@ -4,6 +4,10 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|||||||
|
|
||||||
import { api } from '/@/renderer/api';
|
import { api } from '/@/renderer/api';
|
||||||
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
||||||
|
import {
|
||||||
|
useIsRadioActive,
|
||||||
|
useRadioPlayer,
|
||||||
|
} from '/@/renderer/features/radio/hooks/use-radio-player';
|
||||||
import {
|
import {
|
||||||
DiscordDisplayType,
|
DiscordDisplayType,
|
||||||
DiscordLinkType,
|
DiscordLinkType,
|
||||||
@@ -37,6 +41,9 @@ export const useDiscordRpc = () => {
|
|||||||
const privateMode = useAppStore((state) => state.privateMode);
|
const privateMode = useAppStore((state) => state.privateMode);
|
||||||
const [lastUniqueId, setlastUniqueId] = useState('');
|
const [lastUniqueId, setlastUniqueId] = useState('');
|
||||||
|
|
||||||
|
const isRadioActive = useIsRadioActive();
|
||||||
|
const { isPlaying: isRadioPlaying, metadata: radioMetadata, stationName } = useRadioPlayer();
|
||||||
|
|
||||||
const currentSong = usePlayerSong();
|
const currentSong = usePlayerSong();
|
||||||
const imageUrl = useItemImageUrl({
|
const imageUrl = useItemImageUrl({
|
||||||
id: currentSong?.imageId || undefined,
|
id: currentSong?.imageId || undefined,
|
||||||
@@ -67,14 +74,17 @@ export const useDiscordRpc = () => {
|
|||||||
: song !== previousSong;
|
: song !== previousSong;
|
||||||
const trackChanged = song ? lastUniqueId !== song._uniqueId : false;
|
const trackChanged = song ? lastUniqueId !== song._uniqueId : false;
|
||||||
|
|
||||||
|
const isPlayingRadio = isRadioActive && isRadioPlaying;
|
||||||
|
const hasTrackOrRadio = Boolean(current[0]) || isPlayingRadio;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!current[0] || // No track
|
!hasTrackOrRadio || // No track and not playing radio
|
||||||
(current[2] === 'paused' && !discordSettings.showPaused) // Track paused with show paused setting disabled
|
(current[2] === 'paused' && !discordSettings.showPaused) // Paused with show paused setting disabled
|
||||||
) {
|
) {
|
||||||
let reason: string;
|
let reason: string;
|
||||||
if (!current[0]) {
|
if (!hasTrackOrRadio) {
|
||||||
reason = 'no_track';
|
reason = current[0] ? 'no_track' : 'no_track_or_radio';
|
||||||
} else if (current[1] === 0) {
|
} else if (current[1] === 0 && !isPlayingRadio) {
|
||||||
reason = 'start_of_track';
|
reason = 'start_of_track';
|
||||||
} else {
|
} else {
|
||||||
reason = 'paused_with_show_paused_disabled';
|
reason = 'paused_with_show_paused_disabled';
|
||||||
@@ -90,6 +100,46 @@ export const useDiscordRpc = () => {
|
|||||||
return discordRpc?.clearActivity();
|
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) {
|
if (!song) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -306,6 +356,11 @@ export const useDiscordRpc = () => {
|
|||||||
discordSettings.linkType,
|
discordSettings.linkType,
|
||||||
lastUniqueId,
|
lastUniqueId,
|
||||||
currentSong?._uniqueId,
|
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 styles from './full-screen-player-image.module.css';
|
||||||
|
|
||||||
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
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 { AppRoute } from '/@/renderer/router/routes';
|
||||||
import { useNativeAspectRatio, usePlayerData, usePlayerSong } from '/@/renderer/store';
|
import { useNativeAspectRatio, usePlayerData, usePlayerSong } from '/@/renderer/store';
|
||||||
import { Badge } from '/@/shared/components/badge/badge';
|
import { Badge } from '/@/shared/components/badge/badge';
|
||||||
@@ -45,8 +49,9 @@ const MotionImage = motion.img;
|
|||||||
|
|
||||||
const ImageWithPlaceholder = ({
|
const ImageWithPlaceholder = ({
|
||||||
className,
|
className,
|
||||||
|
placeholderIcon = 'itemAlbum',
|
||||||
...props
|
...props
|
||||||
}: HTMLMotionProps<'img'> & { placeholder?: string }) => {
|
}: HTMLMotionProps<'img'> & { placeholder?: string; placeholderIcon?: 'itemAlbum' | 'radio' }) => {
|
||||||
const nativeAspectRatio = useNativeAspectRatio();
|
const nativeAspectRatio = useNativeAspectRatio();
|
||||||
|
|
||||||
if (!props.src) {
|
if (!props.src) {
|
||||||
@@ -59,7 +64,7 @@ const ImageWithPlaceholder = ({
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon color="muted" icon="itemAlbum" size="25%" />
|
<Icon color="muted" icon={placeholderIcon} size="25%" />
|
||||||
</Center>
|
</Center>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -79,9 +84,14 @@ const ImageWithPlaceholder = ({
|
|||||||
export const FullScreenPlayerImage = () => {
|
export const FullScreenPlayerImage = () => {
|
||||||
const mainImageRef = useRef<HTMLImageElement | null>(null);
|
const mainImageRef = useRef<HTMLImageElement | null>(null);
|
||||||
|
|
||||||
|
const isRadioActive = useIsRadioActive();
|
||||||
|
const { isPlaying: isRadioPlaying, metadata: radioMetadata, stationName } = useRadioPlayer();
|
||||||
|
|
||||||
const currentSong = usePlayerSong();
|
const currentSong = usePlayerSong();
|
||||||
const { nextSong } = usePlayerData();
|
const { nextSong } = usePlayerData();
|
||||||
|
|
||||||
|
const isPlayingRadio = isRadioActive && isRadioPlaying;
|
||||||
|
|
||||||
const currentImageUrl = useItemImageUrl({
|
const currentImageUrl = useItemImageUrl({
|
||||||
id: currentSong?.imageId || undefined,
|
id: currentSong?.imageId || undefined,
|
||||||
itemType: LibraryItem.SONG,
|
itemType: LibraryItem.SONG,
|
||||||
@@ -111,8 +121,11 @@ export const FullScreenPlayerImage = () => {
|
|||||||
imageStateRef.current = imageState;
|
imageStateRef.current = imageState;
|
||||||
}, [imageState]);
|
}, [imageState]);
|
||||||
|
|
||||||
// Update images when song or size changes
|
// Update images when song or size changes (skip when playing radio - no album art)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (isPlayingRadio) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (currentSong?._uniqueId === previousSongRef.current) {
|
if (currentSong?._uniqueId === previousSongRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -126,7 +139,14 @@ export const FullScreenPlayerImage = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
previousSongRef.current = currentSong?._uniqueId;
|
previousSongRef.current = currentSong?._uniqueId;
|
||||||
}, [currentSong?._uniqueId, currentImageUrl, nextSong?._uniqueId, nextImageUrl, setImageState]);
|
}, [
|
||||||
|
isPlayingRadio,
|
||||||
|
currentSong?._uniqueId,
|
||||||
|
currentImageUrl,
|
||||||
|
nextSong?._uniqueId,
|
||||||
|
nextImageUrl,
|
||||||
|
setImageState,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
@@ -138,7 +158,7 @@ export const FullScreenPlayerImage = () => {
|
|||||||
>
|
>
|
||||||
<div className={styles.imageContainer} ref={mainImageRef}>
|
<div className={styles.imageContainer} ref={mainImageRef}>
|
||||||
<AnimatePresence initial={false} mode="sync">
|
<AnimatePresence initial={false} mode="sync">
|
||||||
{imageState.current === 0 && (
|
{!isPlayingRadio && imageState.current === 0 && (
|
||||||
<ImageWithPlaceholder
|
<ImageWithPlaceholder
|
||||||
animate="open"
|
animate="open"
|
||||||
className="full-screen-player-image"
|
className="full-screen-player-image"
|
||||||
@@ -153,7 +173,7 @@ export const FullScreenPlayerImage = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{imageState.current === 1 && (
|
{!isPlayingRadio && imageState.current === 1 && (
|
||||||
<ImageWithPlaceholder
|
<ImageWithPlaceholder
|
||||||
animate="open"
|
animate="open"
|
||||||
className="full-screen-player-image"
|
className="full-screen-player-image"
|
||||||
@@ -167,57 +187,85 @@ export const FullScreenPlayerImage = () => {
|
|||||||
variants={imageVariants}
|
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>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
<Stack className={styles.metadataContainer} gap="md" maw="100%">
|
<Stack className={styles.metadataContainer} gap="md" maw="100%">
|
||||||
<Text fw={900} lh="1.2" overflow="hidden" size="4xl" w="100%">
|
<Text fw={900} lh="1.2" overflow="hidden" size="4xl" w="100%">
|
||||||
{currentSong?.name}
|
{isPlayingRadio
|
||||||
</Text>
|
? radioMetadata?.title || stationName || 'Radio'
|
||||||
<Text
|
: currentSong?.name}
|
||||||
component={Link}
|
|
||||||
isLink
|
|
||||||
overflow="hidden"
|
|
||||||
size="xl"
|
|
||||||
to={generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, {
|
|
||||||
albumId: currentSong?.albumId || '',
|
|
||||||
})}
|
|
||||||
w="100%"
|
|
||||||
>
|
|
||||||
{currentSong?.album}
|
|
||||||
</Text>
|
</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">
|
<Text key="fs-artists">
|
||||||
{currentSong?.artists?.map((artist, index) => (
|
{isPlayingRadio
|
||||||
<Fragment key={`fs-artist-${artist.id}`}>
|
? radioMetadata?.artist || stationName || 'Radio'
|
||||||
{index > 0 && (
|
: currentSong?.artists?.map((artist, index) => (
|
||||||
<Text
|
<Fragment key={`fs-artist-${artist.id}`}>
|
||||||
style={{
|
{index > 0 && (
|
||||||
display: 'inline-block',
|
<Text
|
||||||
padding: '0 0.5rem',
|
style={{
|
||||||
}}
|
display: 'inline-block',
|
||||||
>
|
padding: '0 0.5rem',
|
||||||
•
|
}}
|
||||||
</Text>
|
>
|
||||||
)}
|
•
|
||||||
<Text
|
</Text>
|
||||||
component={Link}
|
)}
|
||||||
isLink
|
<Text
|
||||||
to={generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, {
|
component={Link}
|
||||||
albumArtistId: artist.id,
|
isLink
|
||||||
})}
|
to={generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, {
|
||||||
>
|
albumArtistId: artist.id,
|
||||||
{artist.name}
|
})}
|
||||||
</Text>
|
>
|
||||||
</Fragment>
|
{artist.name}
|
||||||
))}
|
</Text>
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
</Text>
|
</Text>
|
||||||
<Group justify="center" mt="sm">
|
{!isPlayingRadio && (
|
||||||
{currentSong?.container && (
|
<Group justify="center" mt="sm">
|
||||||
<Badge variant="transparent">{currentSong?.container}</Badge>
|
{currentSong?.container && (
|
||||||
)}
|
<Badge variant="transparent">{currentSong?.container}</Badge>
|
||||||
{currentSong?.releaseYear && (
|
)}
|
||||||
<Badge variant="transparent">{currentSong?.releaseYear}</Badge>
|
{currentSong?.releaseYear && (
|
||||||
)}
|
<Badge variant="transparent">{currentSong?.releaseYear}</Badge>
|
||||||
</Group>
|
)}
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Flex>
|
</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 { 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 { FullScreenPlayerImage } from '/@/renderer/features/player/components/full-screen-player-image';
|
||||||
import { FullScreenPlayerQueue } from '/@/renderer/features/player/components/full-screen-player-queue';
|
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 { ListConfigMenu } from '/@/renderer/features/shared/components/list-config-menu';
|
||||||
import { useFastAverageColor } from '/@/renderer/hooks';
|
import { useFastAverageColor } from '/@/renderer/hooks';
|
||||||
import {
|
import {
|
||||||
@@ -657,6 +661,11 @@ export const FullScreenPlayer = () => {
|
|||||||
const { dynamicBackground, dynamicImageBlur, dynamicIsImage } = useFullScreenPlayerStore();
|
const { dynamicBackground, dynamicImageBlur, dynamicIsImage } = useFullScreenPlayerStore();
|
||||||
const { setStore } = useFullScreenPlayerStoreActions();
|
const { setStore } = useFullScreenPlayerStoreActions();
|
||||||
const { windowBarStyle } = useWindowSettings();
|
const { windowBarStyle } = useWindowSettings();
|
||||||
|
const isRadioActive = useIsRadioActive();
|
||||||
|
const { isPlaying: isRadioPlaying } = useRadioPlayer();
|
||||||
|
|
||||||
|
const isPlayingRadio = isRadioActive && isRadioPlaying;
|
||||||
|
const effectiveDynamicBackground = dynamicBackground && !isPlayingRadio;
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const isOpenedRef = useRef<boolean | null>(null);
|
const isOpenedRef = useRef<boolean | null>(null);
|
||||||
@@ -671,13 +680,13 @@ export const FullScreenPlayer = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PlayerContainer
|
<PlayerContainer
|
||||||
dynamicBackground={dynamicBackground}
|
dynamicBackground={effectiveDynamicBackground}
|
||||||
dynamicIsImage={dynamicIsImage}
|
dynamicIsImage={dynamicIsImage}
|
||||||
windowBarStyle={windowBarStyle}
|
windowBarStyle={windowBarStyle}
|
||||||
>
|
>
|
||||||
<Controls />
|
<Controls />
|
||||||
<BackgroundImageOverlay
|
<BackgroundImageOverlay
|
||||||
dynamicBackground={dynamicBackground}
|
dynamicBackground={effectiveDynamicBackground}
|
||||||
dynamicImageBlur={dynamicImageBlur}
|
dynamicImageBlur={dynamicImageBlur}
|
||||||
/>
|
/>
|
||||||
<div className={styles.responsiveContainer}>
|
<div className={styles.responsiveContainer}>
|
||||||
|
|||||||
@@ -50,6 +50,11 @@
|
|||||||
object-fit: var(--theme-image-fit);
|
object-fit: var(--theme-image-fit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.radio-image {
|
||||||
|
background: var(--theme-colors-surface);
|
||||||
|
border-radius: var(--theme-radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
.line-item {
|
.line-item {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ import {
|
|||||||
useSetFullScreenPlayerStore,
|
useSetFullScreenPlayerStore,
|
||||||
} from '/@/renderer/store';
|
} from '/@/renderer/store';
|
||||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||||
|
import { Center } from '/@/shared/components/center/center';
|
||||||
import { Group } from '/@/shared/components/group/group';
|
import { Group } from '/@/shared/components/group/group';
|
||||||
|
import { Icon } from '/@/shared/components/icon/icon';
|
||||||
import { Separator } from '/@/shared/components/separator/separator';
|
import { Separator } from '/@/shared/components/separator/separator';
|
||||||
import { Text } from '/@/shared/components/text/text';
|
import { Text } from '/@/shared/components/text/text';
|
||||||
import { Tooltip } from '/@/shared/components/tooltip/tooltip';
|
import { Tooltip } from '/@/shared/components/tooltip/tooltip';
|
||||||
@@ -48,7 +50,7 @@ export const LeftControls = () => {
|
|||||||
const { bindings } = useHotkeySettings();
|
const { bindings } = useHotkeySettings();
|
||||||
|
|
||||||
const isRadioMode = isRadioActive;
|
const isRadioMode = isRadioActive;
|
||||||
const hideImage = (image && !collapsed) || isRadioMode;
|
const hideImage = image && !collapsed;
|
||||||
const isSongDefined = Boolean(currentSong?.id) && !isRadioMode;
|
const isSongDefined = Boolean(currentSong?.id) && !isRadioMode;
|
||||||
const title = currentSong?.name;
|
const title = currentSong?.name;
|
||||||
const artists = currentSong?.artists;
|
const artists = currentSong?.artists;
|
||||||
@@ -116,20 +118,31 @@ export const LeftControls = () => {
|
|||||||
})}
|
})}
|
||||||
openDelay={0}
|
openDelay={0}
|
||||||
>
|
>
|
||||||
<ItemImage
|
{isRadioMode ? (
|
||||||
className={clsx(
|
<Center
|
||||||
styles.playerbarImage,
|
className={clsx(
|
||||||
PlaybackSelectors.playerCoverArt,
|
styles.playerbarImage,
|
||||||
)}
|
styles.radioImage,
|
||||||
enableDebounce={false}
|
)}
|
||||||
enableViewport={false}
|
>
|
||||||
explicitStatus={currentSong?.explicitStatus}
|
<Icon color="muted" icon="radio" size="40%" />
|
||||||
fetchPriority="high"
|
</Center>
|
||||||
id={currentSong?.imageId}
|
) : (
|
||||||
itemType={LibraryItem.SONG}
|
<ItemImage
|
||||||
serverId={currentSong?._serverId}
|
className={clsx(
|
||||||
type="table"
|
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>
|
</Tooltip>
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react
|
|||||||
import styles from './mobile-fullscreen-player.module.css';
|
import styles from './mobile-fullscreen-player.module.css';
|
||||||
|
|
||||||
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
||||||
|
import {
|
||||||
|
useIsRadioActive,
|
||||||
|
useRadioPlayer,
|
||||||
|
} from '/@/renderer/features/radio/hooks/use-radio-player';
|
||||||
import {
|
import {
|
||||||
useFullScreenPlayerStore,
|
useFullScreenPlayerStore,
|
||||||
useImageRes,
|
useImageRes,
|
||||||
@@ -44,9 +48,14 @@ const MotionImage = motion.img;
|
|||||||
|
|
||||||
const ImageWithPlaceholder = ({
|
const ImageWithPlaceholder = ({
|
||||||
className,
|
className,
|
||||||
|
placeholderIcon,
|
||||||
useImageAspectRatio,
|
useImageAspectRatio,
|
||||||
...props
|
...props
|
||||||
}: HTMLMotionProps<'img'> & { placeholder?: string; useImageAspectRatio?: boolean }) => {
|
}: HTMLMotionProps<'img'> & {
|
||||||
|
placeholder?: string;
|
||||||
|
placeholderIcon?: 'itemAlbum' | 'radio';
|
||||||
|
useImageAspectRatio?: boolean;
|
||||||
|
}) => {
|
||||||
if (!props.src) {
|
if (!props.src) {
|
||||||
return (
|
return (
|
||||||
<Center
|
<Center
|
||||||
@@ -57,7 +66,11 @@ const ImageWithPlaceholder = ({
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon color="muted" icon="itemAlbum" size="25%" />
|
<Icon
|
||||||
|
color="muted"
|
||||||
|
icon={placeholderIcon === 'radio' ? 'radio' : 'itemAlbum'}
|
||||||
|
size="25%"
|
||||||
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -80,9 +93,13 @@ export const MobileFullscreenPlayerAlbumArt = () => {
|
|||||||
|
|
||||||
const { fullScreenPlayer: albumArtRes } = useImageRes();
|
const { fullScreenPlayer: albumArtRes } = useImageRes();
|
||||||
const { useImageAspectRatio } = useFullScreenPlayerStore();
|
const { useImageAspectRatio } = useFullScreenPlayerStore();
|
||||||
|
const isRadioActive = useIsRadioActive();
|
||||||
|
const { isPlaying: isRadioPlaying } = useRadioPlayer();
|
||||||
const currentSong = usePlayerSong();
|
const currentSong = usePlayerSong();
|
||||||
const { nextSong } = usePlayerData();
|
const { nextSong } = usePlayerData();
|
||||||
|
|
||||||
|
const isPlayingRadio = isRadioActive && isRadioPlaying;
|
||||||
|
|
||||||
const currentImageUrl = useItemImageUrl({
|
const currentImageUrl = useItemImageUrl({
|
||||||
id: currentSong?.imageId || undefined,
|
id: currentSong?.imageId || undefined,
|
||||||
itemType: LibraryItem.SONG,
|
itemType: LibraryItem.SONG,
|
||||||
@@ -151,38 +168,58 @@ export const MobileFullscreenPlayerAlbumArt = () => {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<AnimatePresence initial={false} mode="sync">
|
<AnimatePresence initial={false} mode="sync">
|
||||||
{imageState.current === 0 && (
|
{isPlayingRadio ? (
|
||||||
<ImageWithPlaceholder
|
<ImageWithPlaceholder
|
||||||
animate="open"
|
animate="open"
|
||||||
className={PlaybackSelectors.playerCoverArt}
|
className={PlaybackSelectors.playerCoverArt}
|
||||||
custom={{ isOpen: imageState.current === 0 }}
|
custom={{ isOpen: true }}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
exit="closed"
|
exit="closed"
|
||||||
initial="closed"
|
initial="closed"
|
||||||
key={`top-${currentSong?._uniqueId || 'none'}`}
|
key="radio"
|
||||||
loading="eager"
|
loading="eager"
|
||||||
placeholder="var(--theme-colors-foreground-muted)"
|
placeholder="var(--theme-colors-foreground-muted)"
|
||||||
src={imageState.topImage || ''}
|
placeholderIcon="radio"
|
||||||
|
src=""
|
||||||
useImageAspectRatio={useImageAspectRatio}
|
useImageAspectRatio={useImageAspectRatio}
|
||||||
variants={imageVariants}
|
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 && (
|
{imageState.current === 1 && (
|
||||||
<ImageWithPlaceholder
|
<ImageWithPlaceholder
|
||||||
animate="open"
|
animate="open"
|
||||||
className={PlaybackSelectors.playerCoverArt}
|
className={PlaybackSelectors.playerCoverArt}
|
||||||
custom={{ isOpen: imageState.current === 1 }}
|
custom={{ isOpen: imageState.current === 1 }}
|
||||||
draggable={false}
|
draggable={false}
|
||||||
exit="closed"
|
exit="closed"
|
||||||
initial="closed"
|
initial="closed"
|
||||||
key={`bottom-${currentSong?._uniqueId || 'none'}`}
|
key={`bottom-${currentSong?._uniqueId || 'none'}`}
|
||||||
loading="eager"
|
loading="eager"
|
||||||
placeholder="var(--theme-colors-foreground-muted)"
|
placeholder="var(--theme-colors-foreground-muted)"
|
||||||
src={imageState.bottomImage || ''}
|
src={imageState.bottomImage || ''}
|
||||||
useImageAspectRatio={useImageAspectRatio}
|
useImageAspectRatio={useImageAspectRatio}
|
||||||
variants={imageVariants}
|
variants={imageVariants}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ interface MobileFullscreenPlayerMetadataProps {
|
|||||||
currentSong?: QueueSong;
|
currentSong?: QueueSong;
|
||||||
onToggleFavorite: (e: MouseEvent<HTMLButtonElement>) => void;
|
onToggleFavorite: (e: MouseEvent<HTMLButtonElement>) => void;
|
||||||
onUpdateRating: (rating: number) => void;
|
onUpdateRating: (rating: number) => void;
|
||||||
|
radioArtist?: string;
|
||||||
|
radioStationName?: string;
|
||||||
|
radioTitle?: string;
|
||||||
showRating?: boolean;
|
showRating?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,17 +27,24 @@ export const MobileFullscreenPlayerMetadata = memo(
|
|||||||
currentSong,
|
currentSong,
|
||||||
onToggleFavorite,
|
onToggleFavorite,
|
||||||
onUpdateRating,
|
onUpdateRating,
|
||||||
|
radioArtist,
|
||||||
|
radioStationName,
|
||||||
|
radioTitle,
|
||||||
showRating,
|
showRating,
|
||||||
}: MobileFullscreenPlayerMetadataProps) => {
|
}: MobileFullscreenPlayerMetadataProps) => {
|
||||||
const title = currentSong?.name;
|
const isRadio = radioTitle !== undefined || radioStationName !== undefined;
|
||||||
const artists = currentSong?.artists;
|
|
||||||
const album = currentSong?.album;
|
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 container = currentSong?.container;
|
||||||
const year = currentSong?.releaseYear;
|
const year = currentSong?.releaseYear;
|
||||||
const isFavorite = currentSong?.userFavorite;
|
const isFavorite = currentSong?.userFavorite;
|
||||||
const rating = currentSong?.userRating;
|
const rating = currentSong?.userRating;
|
||||||
|
|
||||||
const hasMetadata = container || year;
|
const hasMetadata = !isRadio && (container || year);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.metadataContainer}>
|
<div className={styles.metadataContainer}>
|
||||||
@@ -49,7 +59,7 @@ export const MobileFullscreenPlayerMetadata = memo(
|
|||||||
</TextTitle>
|
</TextTitle>
|
||||||
</div>
|
</div>
|
||||||
<Text className={clsx(PlaybackSelectors.songArtist)} size="md" truncate>
|
<Text className={clsx(PlaybackSelectors.songArtist)} size="md" truncate>
|
||||||
{artists?.map((a) => a.name).join(', ') || '—'}
|
{artistsDisplay || '—'}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className={clsx(PlaybackSelectors.songAlbum)} size="md" truncate>
|
<Text className={clsx(PlaybackSelectors.songAlbum)} size="md" truncate>
|
||||||
{album || '—'}
|
{album || '—'}
|
||||||
@@ -65,21 +75,23 @@ export const MobileFullscreenPlayerMetadata = memo(
|
|||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
)}
|
)}
|
||||||
<Group align="center" className={styles.actionsRow} gap="xs">
|
{!isRadio && (
|
||||||
<ActionIcon
|
<Group align="center" className={styles.actionsRow} gap="xs">
|
||||||
icon="favorite"
|
<ActionIcon
|
||||||
iconProps={{
|
icon="favorite"
|
||||||
fill: isFavorite ? 'primary' : undefined,
|
iconProps={{
|
||||||
size: 'md',
|
fill: isFavorite ? 'primary' : undefined,
|
||||||
}}
|
size: 'md',
|
||||||
onClick={onToggleFavorite}
|
}}
|
||||||
size="sm"
|
onClick={onToggleFavorite}
|
||||||
variant="subtle"
|
size="sm"
|
||||||
/>
|
variant="subtle"
|
||||||
{showRating && (
|
/>
|
||||||
<Rating onChange={onUpdateRating} size="sm" value={rating || 0} />
|
{showRating && (
|
||||||
)}
|
<Rating onChange={onUpdateRating} size="sm" value={rating || 0} />
|
||||||
</Group>
|
)}
|
||||||
|
</Group>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ import { MobileFullscreenPlayerControls } from '/@/renderer/features/player/comp
|
|||||||
import { MobileFullscreenPlayerHeader } from '/@/renderer/features/player/components/mobile-fullscreen-player-header';
|
import { MobileFullscreenPlayerHeader } from '/@/renderer/features/player/components/mobile-fullscreen-player-header';
|
||||||
import { MobileFullscreenPlayerMetadata } from '/@/renderer/features/player/components/mobile-fullscreen-player-metadata';
|
import { MobileFullscreenPlayerMetadata } from '/@/renderer/features/player/components/mobile-fullscreen-player-metadata';
|
||||||
import { MobileFullscreenPlayerProgress } from '/@/renderer/features/player/components/mobile-fullscreen-player-progress';
|
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 { useSetFavorite } from '/@/renderer/features/shared/hooks/use-set-favorite';
|
||||||
import { useSetRating } from '/@/renderer/features/shared/hooks/use-set-rating';
|
import { useSetRating } from '/@/renderer/features/shared/hooks/use-set-rating';
|
||||||
import { useFastAverageColor } from '/@/renderer/hooks';
|
import { useFastAverageColor } from '/@/renderer/hooks';
|
||||||
@@ -376,7 +380,12 @@ export const MobileFullscreenPlayer = () => {
|
|||||||
useFullScreenPlayerStore();
|
useFullScreenPlayerStore();
|
||||||
const currentSong = usePlayerSong();
|
const currentSong = usePlayerSong();
|
||||||
const { currentSong: currentSongData } = usePlayerData();
|
const { currentSong: currentSongData } = usePlayerData();
|
||||||
|
const isRadioActive = useIsRadioActive();
|
||||||
|
const { isPlaying: isRadioPlaying, metadata: radioMetadata, stationName } = useRadioPlayer();
|
||||||
const server = useCurrentServer();
|
const server = useCurrentServer();
|
||||||
|
|
||||||
|
const isPlayingRadio = isRadioActive && isRadioPlaying;
|
||||||
|
const effectiveDynamicBackground = dynamicBackground && !isPlayingRadio;
|
||||||
const setFavorite = useSetFavorite();
|
const setFavorite = useSetFavorite();
|
||||||
const { showRatings: showRatingsSetting } = useGeneralSettings();
|
const { showRatings: showRatingsSetting } = useGeneralSettings();
|
||||||
const setRating = useSetRating();
|
const setRating = useSetRating();
|
||||||
@@ -443,11 +452,11 @@ export const MobileFullscreenPlayer = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MobilePlayerContainer
|
<MobilePlayerContainer
|
||||||
dynamicBackground={dynamicBackground}
|
dynamicBackground={effectiveDynamicBackground}
|
||||||
dynamicIsImage={dynamicIsImage}
|
dynamicIsImage={dynamicIsImage}
|
||||||
>
|
>
|
||||||
<BackgroundImageOverlay
|
<BackgroundImageOverlay
|
||||||
dynamicBackground={dynamicBackground}
|
dynamicBackground={effectiveDynamicBackground}
|
||||||
dynamicImageBlur={dynamicImageBlur}
|
dynamicImageBlur={dynamicImageBlur}
|
||||||
/>
|
/>
|
||||||
<motion.div
|
<motion.div
|
||||||
@@ -470,6 +479,9 @@ export const MobileFullscreenPlayer = () => {
|
|||||||
currentSong={currentSong}
|
currentSong={currentSong}
|
||||||
onToggleFavorite={handleToggleFavorite}
|
onToggleFavorite={handleToggleFavorite}
|
||||||
onUpdateRating={handleUpdateRating}
|
onUpdateRating={handleUpdateRating}
|
||||||
|
radioArtist={isPlayingRadio ? (radioMetadata?.artist ?? undefined) : undefined}
|
||||||
|
radioStationName={isPlayingRadio ? (stationName ?? undefined) : undefined}
|
||||||
|
radioTitle={isPlayingRadio ? (radioMetadata?.title ?? undefined) : undefined}
|
||||||
showRating={showRating}
|
showRating={showRating}
|
||||||
/>
|
/>
|
||||||
<MobileFullscreenPlayerProgress currentSong={currentSong} />
|
<MobileFullscreenPlayerProgress currentSong={currentSong} />
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { useTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
import styles from './server-selector.module.css';
|
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 JellyfinLogo from '/@/renderer/features/servers/assets/jellyfin.png';
|
||||||
import NavidromeLogo from '/@/renderer/features/servers/assets/navidrome.png';
|
import NavidromeLogo from '/@/renderer/features/servers/assets/navidrome.png';
|
||||||
import OpenSubsonicLogo from '/@/renderer/features/servers/assets/opensubsonic.png';
|
import OpenSubsonicLogo from '/@/renderer/features/servers/assets/opensubsonic.png';
|
||||||
@@ -26,8 +25,7 @@ export const ServerSelector = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const currentServer = useCurrentServer();
|
const currentServer = useCurrentServer();
|
||||||
const sidebarImageEnabled = useAppStore((state) => state.sidebar.image);
|
const sidebarImageEnabled = useAppStore((state) => state.sidebar.image);
|
||||||
const isRadioPlaying = useRadioStore((state) => state.isPlaying);
|
const showImage = sidebarImageEnabled;
|
||||||
const showImage = sidebarImageEnabled && !isRadioPlaying;
|
|
||||||
|
|
||||||
const { data: musicFolders } = useQuery(
|
const { data: musicFolders } = useQuery(
|
||||||
currentServer
|
currentServer
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ import styles from './sidebar.module.css';
|
|||||||
|
|
||||||
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
|
||||||
import { ContextMenuController } from '/@/renderer/features/context-menu/context-menu-controller';
|
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 { ActionBar } from '/@/renderer/features/sidebar/components/action-bar';
|
||||||
import { ServerSelector } from '/@/renderer/features/sidebar/components/server-selector';
|
import { ServerSelector } from '/@/renderer/features/sidebar/components/server-selector';
|
||||||
import { SidebarCollectionList } from '/@/renderer/features/sidebar/components/sidebar-collection-list';
|
import { SidebarCollectionList } from '/@/renderer/features/sidebar/components/sidebar-collection-list';
|
||||||
@@ -32,7 +35,9 @@ import {
|
|||||||
} from '/@/renderer/store/settings.store';
|
} from '/@/renderer/store/settings.store';
|
||||||
import { Accordion } from '/@/shared/components/accordion/accordion';
|
import { Accordion } from '/@/shared/components/accordion/accordion';
|
||||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||||
|
import { Center } from '/@/shared/components/center/center';
|
||||||
import { Group } from '/@/shared/components/group/group';
|
import { Group } from '/@/shared/components/group/group';
|
||||||
|
import { Icon } from '/@/shared/components/icon/icon';
|
||||||
import { ImageUnloader } from '/@/shared/components/image/image';
|
import { ImageUnloader } from '/@/shared/components/image/image';
|
||||||
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
|
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
|
||||||
import { Text } from '/@/shared/components/text/text';
|
import { Text } from '/@/shared/components/text/text';
|
||||||
@@ -68,8 +73,7 @@ export const Sidebar = () => {
|
|||||||
const sidebarItems = useSidebarItems();
|
const sidebarItems = useSidebarItems();
|
||||||
const { windowBarStyle } = useWindowSettings();
|
const { windowBarStyle } = useWindowSettings();
|
||||||
const sidebarImageEnabled = useAppStore((state) => state.sidebar.image);
|
const sidebarImageEnabled = useAppStore((state) => state.sidebar.image);
|
||||||
const isRadioPlaying = useRadioStore((state) => state.isPlaying);
|
const showImage = sidebarImageEnabled;
|
||||||
const showImage = sidebarImageEnabled && !isRadioPlaying;
|
|
||||||
|
|
||||||
const sidebarItemsWithRoute: SidebarItemType[] = useMemo(() => {
|
const sidebarItemsWithRoute: SidebarItemType[] = useMemo(() => {
|
||||||
if (!sidebarItems) return [];
|
if (!sidebarItems) return [];
|
||||||
@@ -161,6 +165,8 @@ const SidebarImage = () => {
|
|||||||
const leftWidth = useAppStore((state) => state.sidebar.leftWidth);
|
const leftWidth = useAppStore((state) => state.sidebar.leftWidth);
|
||||||
const { setSideBar } = useAppStoreActions();
|
const { setSideBar } = useAppStoreActions();
|
||||||
const currentSong = usePlayerSong();
|
const currentSong = usePlayerSong();
|
||||||
|
const isRadioActive = useIsRadioActive();
|
||||||
|
const { isPlaying: isRadioPlaying } = useRadioPlayer();
|
||||||
|
|
||||||
const imageUrl = useItemImageUrl({
|
const imageUrl = useItemImageUrl({
|
||||||
id: currentSong?.imageId || undefined,
|
id: currentSong?.imageId || undefined,
|
||||||
@@ -169,6 +175,7 @@ const SidebarImage = () => {
|
|||||||
type: 'sidebar',
|
type: 'sidebar',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isPlayingRadio = isRadioActive && isRadioPlaying;
|
||||||
const isSongDefined = Boolean(currentSong?.id);
|
const isSongDefined = Boolean(currentSong?.id);
|
||||||
|
|
||||||
const setFullScreenPlayerStore = useSetFullScreenPlayerStore();
|
const setFullScreenPlayerStore = useSetFullScreenPlayerStore();
|
||||||
@@ -181,7 +188,7 @@ const SidebarImage = () => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
if (!currentSong) {
|
if (!currentSong || isPlayingRadio) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +222,19 @@ const SidebarImage = () => {
|
|||||||
postProcess: 'sentenceCase',
|
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} />
|
<img className={styles.sidebarImage} loading="eager" src={imageUrl} />
|
||||||
) : (
|
) : (
|
||||||
<ImageUnloader icon="emptySongImage" />
|
<ImageUnloader icon="emptySongImage" />
|
||||||
|
|||||||
Reference in New Issue
Block a user