Add image URL generation at runtime to allow for dynamic image sizes (#1439)

* add getImageUrl to domain endpoints

* add new ItemImage component and hooks to generate image url

* add configuration for image resolution based on types
This commit is contained in:
Jeff
2025-12-23 20:18:52 -08:00
committed by GitHub
parent 96f38e597c
commit 25bfb65b6d
39 changed files with 823 additions and 670 deletions
@@ -6,6 +6,7 @@ import { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import i18n, { languages } from '/@/i18n/i18n';
import { ImageResolutionSettings } from '/@/renderer/features/settings/components/general/art-resolution-settings';
import { ArtistSettings } from '/@/renderer/features/settings/components/general/artist-settings';
import { HomeSettings } from '/@/renderer/features/settings/components/general/home-settings';
import {
@@ -575,37 +576,13 @@ export const ApplicationSettings = () => {
isHidden: false,
title: t('setting.playerbarOpenDrawer', { postProcess: 'sentenceCase' }),
},
{
control: (
<NumberInput
defaultValue={settings.albumArtRes || undefined}
hideControls={false}
max={2500}
onBlur={(e) => {
const newVal =
e.currentTarget.value !== '0'
? Math.min(Math.max(Number(e.currentTarget.value), 175), 2500)
: null;
setSettings({ general: { ...settings, albumArtRes: newVal } });
}}
placeholder="0"
value={settings.albumArtRes ?? 0}
width={75}
/>
),
description: t('setting.playerAlbumArtResolution', {
context: 'description',
postProcess: 'sentenceCase',
}),
isHidden: false,
title: t('setting.playerAlbumArtResolution', { postProcess: 'sentenceCase' }),
},
];
return (
<SettingsSection
extra={
<>
<ImageResolutionSettings />
<HomeSettings />
<ArtistSettings />
</>
@@ -0,0 +1,111 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import i18n from '/@/i18n/i18n';
import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option';
import { useGeneralSettings, useSettingsStoreActions } from '/@/renderer/store';
import { Button } from '/@/shared/components/button/button';
import { NumberInput } from '/@/shared/components/number-input/number-input';
import { Table } from '/@/shared/components/table/table';
import { Text } from '/@/shared/components/text/text';
const options = [
{
label: i18n.t('setting.imageResolution_optionTable', { postProcess: 'sentenceCase' }),
value: 'table',
},
{
label: i18n.t('setting.imageResolution_optionItemCard', { postProcess: 'sentenceCase' }),
value: 'itemCard',
},
{
label: i18n.t('setting.imageResolution_optionSidebar', { postProcess: 'sentenceCase' }),
value: 'sidebar',
},
{
label: i18n.t('setting.imageResolution_optionHeader', { postProcess: 'sentenceCase' }),
value: 'header',
},
{
label: i18n.t('setting.imageResolution_optionFullScreenPlayer', {
postProcess: 'sentenceCase',
}),
value: 'fullScreenPlayer',
},
];
export const ImageResolutionSettings = () => {
const { t } = useTranslation();
const { setSettings } = useSettingsStoreActions();
const settings = useGeneralSettings();
const [open, setOpen] = useState(false);
const descriptionText = t('setting.imageResolution', {
context: 'description',
postProcess: 'sentenceCase',
});
const titleText = t('setting.imageResolution', { postProcess: 'sentenceCase' });
return (
<>
<SettingsOptions
control={
<>
<Button
onClick={() => setOpen(!open)}
size="compact-md"
variant={open ? 'subtle' : 'filled'}
>
{t(open ? 'common.close' : 'common.edit', { postProcess: 'titleCase' })}
</Button>
</>
}
description={descriptionText}
title={titleText}
/>
{open && (
<Table withRowBorders={false}>
<Table.Tbody>
{options.map((option) => (
<Table.Tr key={option.value}>
<Table.Th key={option.value}>
<Text>{option.label}</Text>
</Table.Th>
<Table.Td align="right" key={option.value}>
<NumberInput
max={2000}
min={0}
onChange={(e) => {
if (!e) return;
if (typeof e === 'string') return;
setSettings({
general: {
...settings,
imageRes: {
...settings.imageRes,
[option.value]: e,
},
},
});
}}
rightSection={
<Text isMuted isNoSelect pr="lg" size="sm">
px
</Text>
}
value={settings.imageRes[option.value]}
width={90}
/>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
)}
</>
);
};