support artist art as artist background

This commit is contained in:
Kendall Garner 2025-10-10 18:26:28 -07:00
parent 4e4a0464d6
commit 452803fc72
No known key found for this signature in database
GPG key ID: 9355F387FE765C94
5 changed files with 77 additions and 11 deletions

View file

@ -497,6 +497,10 @@
"albumBackgroundBlur_description": "adjusts the amount of blur applied to the album background image", "albumBackgroundBlur_description": "adjusts the amount of blur applied to the album background image",
"applicationHotkeys": "application hotkeys", "applicationHotkeys": "application hotkeys",
"applicationHotkeys_description": "configure application hotkeys. toggle the checkbox to set as a global hotkey (desktop only)", "applicationHotkeys_description": "configure application hotkeys. toggle the checkbox to set as a global hotkey (desktop only)",
"artistBackground": "artist background image",
"artistBackground_description": "adds a background image for artist pages containing the artist art",
"artistBackgroundBlur": "artist background image blur size",
"artistBackgroundBlur_description": "adjusts the amount of blur applied to the artist background image",
"artistConfiguration": "album artist page configuration", "artistConfiguration": "album artist page configuration",
"artistConfiguration_description": "configure what items are shown, and in what order, on the album artist page", "artistConfiguration_description": "configure what items are shown, and in what order, on the album artist page",
"audioDevice": "audio device", "audioDevice": "audio device",

View file

@ -14,12 +14,15 @@ import { Text } from '/@/shared/components/text/text';
import { LibraryItem, ServerType } from '/@/shared/types/domain-types'; import { LibraryItem, ServerType } from '/@/shared/types/domain-types';
interface AlbumArtistDetailHeaderProps { interface AlbumArtistDetailHeaderProps {
background: {
background?: string; background?: string;
blur: number;
loading: boolean; loading: boolean;
};
} }
export const AlbumArtistDetailHeader = forwardRef( export const AlbumArtistDetailHeader = forwardRef(
({ background, loading }: AlbumArtistDetailHeaderProps, ref: Ref<HTMLDivElement>) => { ({ background }: AlbumArtistDetailHeaderProps, ref: Ref<HTMLDivElement>) => {
const { albumArtistId, artistId } = useParams() as { const { albumArtistId, artistId } = useParams() as {
albumArtistId?: string; albumArtistId?: string;
artistId?: string; artistId?: string;
@ -76,12 +79,11 @@ export const AlbumArtistDetailHeader = forwardRef(
return ( return (
<LibraryHeader <LibraryHeader
background={background}
imageUrl={detailQuery?.data?.imageUrl} imageUrl={detailQuery?.data?.imageUrl}
item={{ route: AppRoute.LIBRARY_ALBUM_ARTISTS, type: LibraryItem.ALBUM_ARTIST }} item={{ route: AppRoute.LIBRARY_ALBUM_ARTISTS, type: LibraryItem.ALBUM_ARTIST }}
loading={loading}
ref={ref} ref={ref}
title={detailQuery?.data?.name || ''} title={detailQuery?.data?.name || ''}
{...background}
> >
<Stack> <Stack>
<Group> <Group>

View file

@ -9,13 +9,14 @@ import { usePlayQueueAdd } from '/@/renderer/features/player';
import { AnimatedPage, LibraryHeaderBar } from '/@/renderer/features/shared'; import { AnimatedPage, LibraryHeaderBar } from '/@/renderer/features/shared';
import { useFastAverageColor } from '/@/renderer/hooks'; import { useFastAverageColor } from '/@/renderer/hooks';
import { useCurrentServer } from '/@/renderer/store'; import { useCurrentServer } from '/@/renderer/store';
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store'; import { useGeneralSettings, usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { LibraryItem } from '/@/shared/types/domain-types'; import { LibraryItem } from '/@/shared/types/domain-types';
const AlbumArtistDetailRoute = () => { const AlbumArtistDetailRoute = () => {
const scrollAreaRef = useRef<HTMLDivElement>(null); const scrollAreaRef = useRef<HTMLDivElement>(null);
const headerRef = useRef<HTMLDivElement>(null); const headerRef = useRef<HTMLDivElement>(null);
const server = useCurrentServer(); const server = useCurrentServer();
const { artistBackground, artistBackgroundBlur } = useGeneralSettings();
const { albumArtistId, artistId } = useParams() as { const { albumArtistId, artistId } = useParams() as {
albumArtistId?: string; albumArtistId?: string;
@ -30,12 +31,16 @@ const AlbumArtistDetailRoute = () => {
query: { id: routeId }, query: { id: routeId },
serverId: server?.id, serverId: server?.id,
}); });
const { background, colorId } = useFastAverageColor({
id: routeId, const { background: backgroundColor, colorId } = useFastAverageColor({
id: artistId,
src: detailQuery.data?.imageUrl, src: detailQuery.data?.imageUrl,
srcLoaded: !detailQuery.isLoading, srcLoaded: !detailQuery.isLoading,
}); });
const backgroundUrl = detailQuery.data?.imageUrl || '';
const background = (artistBackground && `url(${backgroundUrl})`) || backgroundColor;
const handlePlay = () => { const handlePlay = () => {
handlePlayQueueAdd?.({ handlePlayQueueAdd?.({
byItemType: { byItemType: {
@ -65,8 +70,11 @@ const AlbumArtistDetailRoute = () => {
ref={scrollAreaRef} ref={scrollAreaRef}
> >
<AlbumArtistDetailHeader <AlbumArtistDetailHeader
background={background} background={{
loading={!background || colorId !== routeId} background,
blur: (artistBackground && artistBackgroundBlur) || 0,
loading: !backgroundColor || colorId !== artistId,
}}
ref={headerRef} ref={headerRef}
/> />
<AlbumArtistDetailContent background={background} /> <AlbumArtistDetailContent background={background} />

View file

@ -548,9 +548,57 @@ export const ControlSettings = () => {
context: 'description', context: 'description',
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
isHidden: false, isHidden: !settings.albumBackground,
title: t('setting.albumBackgroundBlur', { postProcess: 'sentenceCase' }), title: t('setting.albumBackgroundBlur', { postProcess: 'sentenceCase' }),
}, },
{
control: (
<Switch
aria-label={t('setting.artistBackground', { postProcess: 'sentenceCase' })}
defaultChecked={settings.artistBackground}
onChange={(e) =>
setSettings({
general: {
...settings,
artistBackground: e.currentTarget.checked,
},
})
}
/>
),
description: t('setting.artistBackground', {
context: 'description',
postProcess: 'sentenceCase',
}),
isHidden: false,
title: t('setting.artistBackground', { postProcess: 'sentenceCase' }),
},
{
control: (
<Slider
defaultValue={settings.artistBackgroundBlur}
label={(e) => `${e} rem`}
max={6}
min={0}
onChangeEnd={(e) => {
setSettings({
general: {
...settings,
artistBackgroundBlur: e,
},
});
}}
step={0.5}
w={100}
/>
),
description: t('setting.artistBackgroundBlur', {
context: 'description',
postProcess: 'sentenceCase',
}),
isHidden: !settings.artistBackground,
title: t('setting.artistBackgroundBlur', { postProcess: 'sentenceCase' }),
},
{ {
control: ( control: (
<Switch <Switch

View file

@ -231,6 +231,8 @@ export interface SettingsState {
albumArtRes?: null | number; albumArtRes?: null | number;
albumBackground: boolean; albumBackground: boolean;
albumBackgroundBlur: number; albumBackgroundBlur: number;
artistBackground: boolean;
artistBackgroundBlur: number;
artistItems: SortableItem<ArtistItem>[]; artistItems: SortableItem<ArtistItem>[];
buttonSize: number; buttonSize: number;
disabledContextMenu: { [k in ContextMenuItemType]?: boolean }; disabledContextMenu: { [k in ContextMenuItemType]?: boolean };
@ -389,6 +391,8 @@ const initialState: SettingsState = {
albumArtRes: undefined, albumArtRes: undefined,
albumBackground: false, albumBackground: false,
albumBackgroundBlur: 6, albumBackgroundBlur: 6,
artistBackground: false,
artistBackgroundBlur: 6,
artistItems, artistItems,
buttonSize: 15, buttonSize: 15,
disabledContextMenu: {}, disabledContextMenu: {},