import clsx from 'clsx'; import { AnimatePresence, HTMLMotionProps, motion, Variants } from 'motion/react'; import { Fragment, useEffect, useRef } from 'react'; 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 { useGeneralSettings, useNativeAspectRatio, usePlayerData, usePlayerSong, } from '/@/renderer/store'; import { Badge } from '/@/shared/components/badge/badge'; import { Center } from '/@/shared/components/center/center'; import { Flex } from '/@/shared/components/flex/flex'; import { Group } from '/@/shared/components/group/group'; import { Icon } from '/@/shared/components/icon/icon'; import { Stack } from '/@/shared/components/stack/stack'; import { Text } from '/@/shared/components/text/text'; import { useSetState } from '/@/shared/hooks/use-set-state'; import { ExplicitStatus, LibraryItem } from '/@/shared/types/domain-types'; const imageVariants: Variants = { closed: { opacity: 0, transition: { duration: 0.8, ease: 'linear', }, }, initial: { opacity: 0, }, open: (custom) => { const { isOpen } = custom; return { opacity: isOpen ? 1 : 0, transition: { duration: 0.4, ease: 'linear', }, }; }, }; const MotionImage = motion.img; const ImageWithPlaceholder = ({ className, explicit, placeholderIcon = 'itemAlbum', ...props }: HTMLMotionProps<'img'> & { explicit?: boolean; placeholder?: string; placeholderIcon?: 'itemAlbum' | 'radio'; }) => { const nativeAspectRatio = useNativeAspectRatio(); if (!props.src) { return (
); } return ( ); }; export const FullScreenPlayerImage = () => { const mainImageRef = useRef(null); const isRadioActive = useIsRadioActive(); const { isPlaying: isRadioPlaying, metadata: radioMetadata, stationName } = useRadioPlayer(); const currentSong = usePlayerSong(); const { nextSong } = usePlayerData(); const { blurExplicitImages } = useGeneralSettings(); const isPlayingRadio = isRadioActive && isRadioPlaying; const currentImageUrl = useItemImageUrl({ id: currentSong?.imageId || undefined, itemType: LibraryItem.SONG, serverId: currentSong?._serverId, type: 'fullScreenPlayer', }); const nextImageUrl = useItemImageUrl({ id: nextSong?.imageId || undefined, itemType: LibraryItem.SONG, serverId: nextSong?._serverId, type: 'fullScreenPlayer', }); const [imageState, setImageState] = useSetState({ bottomExplicit: nextSong?.explicitStatus === ExplicitStatus.EXPLICIT, bottomImage: nextImageUrl, current: 0, topExplicit: currentSong?.explicitStatus === ExplicitStatus.EXPLICIT, topImage: currentImageUrl, }); // Track previous song to detect changes const previousSongRef = useRef(currentSong?._uniqueId); const imageStateRef = useRef(imageState); // Keep ref in sync useEffect(() => { imageStateRef.current = imageState; }, [imageState]); // Update images when song or size changes (skip when playing radio - no album art) useEffect(() => { if (isPlayingRadio) { return; } if (currentSong?._uniqueId === previousSongRef.current) { return; } const isTop = imageStateRef.current.current === 0; setImageState({ bottomExplicit: (isTop ? currentSong?.explicitStatus : nextSong?.explicitStatus) === ExplicitStatus.EXPLICIT, bottomImage: isTop ? currentImageUrl : nextImageUrl, current: isTop ? 1 : 0, topExplicit: (isTop ? nextSong?.explicitStatus : currentSong?.explicitStatus) === ExplicitStatus.EXPLICIT, topImage: isTop ? nextImageUrl : currentImageUrl, }); previousSongRef.current = currentSong?._uniqueId; }, [ isPlayingRadio, currentSong?._uniqueId, currentImageUrl, nextSong?._uniqueId, nextImageUrl, setImageState, currentSong?.explicitStatus, nextSong?.explicitStatus, ]); return (
{!isPlayingRadio && imageState.current === 0 && ( )} {!isPlayingRadio && imageState.current === 1 && ( )} {isPlayingRadio && ( )}
{isPlayingRadio ? radioMetadata?.title || stationName || 'Radio' : currentSong?.name} {isPlayingRadio ? ( {stationName || 'Radio'} ) : ( {currentSong?.album} )} {isPlayingRadio ? radioMetadata?.artist || stationName || 'Radio' : currentSong?.artists?.map((artist, index) => ( {index > 0 && ( )} {artist.name} ))} {!isPlayingRadio && ( {currentSong?.container && ( {currentSong?.container} )} {currentSong?.releaseYear && ( {currentSong?.releaseYear} )} )}
); };