Image Resolution Setting (#492)
* Add customizable resolution for the fullscreen player image --------- Co-authored-by: iiPython <ben@iipython.dev> Co-authored-by: Benjamin <iipython@proton.me>
This commit is contained in:
parent
f796a35f5c
commit
6e677d7454
4 changed files with 39 additions and 7 deletions
|
@ -415,12 +415,12 @@
|
||||||
"audioExclusiveMode_description": "enable exclusive output mode. In this mode, the system is usually locked out, and only mpv will be able to output audio",
|
"audioExclusiveMode_description": "enable exclusive output mode. In this mode, the system is usually locked out, and only mpv will be able to output audio",
|
||||||
"audioPlayer": "audio player",
|
"audioPlayer": "audio player",
|
||||||
"audioPlayer_description": "select the audio player to use for playback",
|
"audioPlayer_description": "select the audio player to use for playback",
|
||||||
"buttonSize": "Player bar button size",
|
"buttonSize": "player bar button size",
|
||||||
"buttonSize_description": "The size of the player bar buttons",
|
"buttonSize_description": "the size of the player bar buttons",
|
||||||
"clearCache": "Clear browser cache",
|
"clearCache": "clear browser cache",
|
||||||
"clearCache_description": "A 'hard clear' of Feishin. In addition to clearing Feishin's cache, empty the browser cache (saved images and other assets). Server credentials and settings are preserved.",
|
"clearCache_description": "a 'hard clear' of feishin. in addition to clearing feishin's cache, empty the browser cache (saved images and other assets). server credentials and settings are preserved",
|
||||||
"clearQueryCache": "Clear Feishin cache",
|
"clearQueryCache": "clear feishin cache",
|
||||||
"clearQueryCache_description": "A 'soft clear' of Feishin. This will refresh playlists, track metadata, and reset saved lyrics. Settings, server credentials and cached images are preserved.",
|
"clearQueryCache_description": "a 'soft clear' of feishin. this will refresh playlists, track metadata, and reset saved lyrics. settings, server credentials and cached images are preserved",
|
||||||
"clearCacheSuccess": "cache cleared successfully",
|
"clearCacheSuccess": "cache cleared successfully",
|
||||||
"crossfadeDuration": "crossfade duration",
|
"crossfadeDuration": "crossfade duration",
|
||||||
"crossfadeDuration_description": "sets the duration of the crossfade effect",
|
"crossfadeDuration_description": "sets the duration of the crossfade effect",
|
||||||
|
@ -524,6 +524,8 @@
|
||||||
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
|
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
|
||||||
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
|
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
|
||||||
"playButtonBehavior_optionPlay": "$t(player.play)",
|
"playButtonBehavior_optionPlay": "$t(player.play)",
|
||||||
|
"playerAlbumArtResolution": "player album art resolution",
|
||||||
|
"playerAlbumArtResolution_description": "the resolution for the large player's album art preview. larger makes it look more crisp, but may slow loading down. defaults to 0, meaning auto",
|
||||||
"remotePassword": "remote control server password",
|
"remotePassword": "remote control server password",
|
||||||
"remotePassword_description": "sets the password for the remote control server. These credentials are by default transferred insecurely, so you should use a unique password that you do not care about",
|
"remotePassword_description": "sets the password for the remote control server. These credentials are by default transferred insecurely, so you should use a unique password that you do not care about",
|
||||||
"remotePort": "remote control server port",
|
"remotePort": "remote control server port",
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
usePlayerData,
|
usePlayerData,
|
||||||
usePlayerStore,
|
usePlayerStore,
|
||||||
} from '/@/renderer/store';
|
} from '/@/renderer/store';
|
||||||
|
import { useSettingsStore } from '/@/renderer/store/settings.store';
|
||||||
|
|
||||||
const Image = styled(motion.img)<{ $useAspectRatio: boolean }>`
|
const Image = styled(motion.img)<{ $useAspectRatio: boolean }>`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -130,6 +131,8 @@ export const FullScreenPlayerImage = () => {
|
||||||
const mainImageRef = useRef<HTMLImageElement | null>(null);
|
const mainImageRef = useRef<HTMLImageElement | null>(null);
|
||||||
const [mainImageDimensions, setMainImageDimensions] = useState({ idealSize: 1 });
|
const [mainImageDimensions, setMainImageDimensions] = useState({ idealSize: 1 });
|
||||||
|
|
||||||
|
const albumArtRes = useSettingsStore((store) => store.general.albumArtRes);
|
||||||
|
|
||||||
const { queue } = usePlayerData();
|
const { queue } = usePlayerData();
|
||||||
const { opacity, useImageAspectRatio } = useFullScreenPlayerStore();
|
const { opacity, useImageAspectRatio } = useFullScreenPlayerStore();
|
||||||
const currentSong = queue.current;
|
const currentSong = queue.current;
|
||||||
|
@ -149,6 +152,7 @@ export const FullScreenPlayerImage = () => {
|
||||||
if (mainImageRef.current) {
|
if (mainImageRef.current) {
|
||||||
setMainImageDimensions({
|
setMainImageDimensions({
|
||||||
idealSize:
|
idealSize:
|
||||||
|
albumArtRes ||
|
||||||
Math.ceil((mainImageRef.current as HTMLDivElement).offsetHeight / 100) * 100,
|
Math.ceil((mainImageRef.current as HTMLDivElement).offsetHeight / 100) * 100,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -158,7 +162,7 @@ export const FullScreenPlayerImage = () => {
|
||||||
topImage: scaleImageUrl(mainImageDimensions.idealSize, queue.current?.imageUrl),
|
topImage: scaleImageUrl(mainImageDimensions.idealSize, queue.current?.imageUrl),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [mainImageDimensions.idealSize, queue, setImageState]);
|
}, [mainImageDimensions.idealSize, queue, setImageState, albumArtRes]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
updateImageSize();
|
updateImageSize();
|
||||||
|
|
|
@ -65,6 +65,30 @@ export const ControlSettings = () => {
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
title: t('setting.buttonSize', { postProcess: 'sentenceCase' }),
|
title: t('setting.buttonSize', { postProcess: 'sentenceCase' }),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
control: (
|
||||||
|
<NumberInput
|
||||||
|
defaultValue={settings.albumArtRes || undefined}
|
||||||
|
max={2500}
|
||||||
|
min={175}
|
||||||
|
placeholder="0"
|
||||||
|
rightSection="px"
|
||||||
|
width={75}
|
||||||
|
onBlur={(e) => {
|
||||||
|
const newVal = e.currentTarget.value
|
||||||
|
? Math.min(Math.max(Number(e.currentTarget.value), 175), 2500)
|
||||||
|
: null;
|
||||||
|
setSettings({ general: { ...settings, albumArtRes: newVal } });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
description: t('setting.playerAlbumArtResolution', {
|
||||||
|
context: 'description',
|
||||||
|
postProcess: 'sentenceCase',
|
||||||
|
}),
|
||||||
|
isHidden: false,
|
||||||
|
title: t('setting.playerAlbumArtResolution', { postProcess: 'sentenceCase' }),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
control: (
|
control: (
|
||||||
<Switch
|
<Switch
|
||||||
|
|
|
@ -187,6 +187,7 @@ export interface SettingsState {
|
||||||
};
|
};
|
||||||
general: {
|
general: {
|
||||||
accent: string;
|
accent: string;
|
||||||
|
albumArtRes?: number | null;
|
||||||
buttonSize: number;
|
buttonSize: number;
|
||||||
defaultFullPlaylist: boolean;
|
defaultFullPlaylist: boolean;
|
||||||
externalLinks: boolean;
|
externalLinks: boolean;
|
||||||
|
@ -304,6 +305,7 @@ const initialState: SettingsState = {
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
accent: 'rgb(53, 116, 252)',
|
accent: 'rgb(53, 116, 252)',
|
||||||
|
albumArtRes: undefined,
|
||||||
buttonSize: 20,
|
buttonSize: 20,
|
||||||
defaultFullPlaylist: true,
|
defaultFullPlaylist: true,
|
||||||
externalLinks: true,
|
externalLinks: true,
|
||||||
|
|
Reference in a new issue