mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-14 20:40:21 +02:00
crossfade player enhancements, reorganize settings
This commit is contained in:
@@ -7,13 +7,17 @@ import {
|
||||
SettingsSection,
|
||||
} from '/@/renderer/features/settings/components/settings-section';
|
||||
import {
|
||||
BarAlign,
|
||||
GenreTarget,
|
||||
PlayerbarSliderType,
|
||||
SideQueueType,
|
||||
useGeneralSettings,
|
||||
usePlayerbarSlider,
|
||||
useSettingsStoreActions,
|
||||
} from '/@/renderer/store/settings.store';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { NumberInput } from '/@/shared/components/number-input/number-input';
|
||||
import { SegmentedControl } from '/@/shared/components/segmented-control/segmented-control';
|
||||
import { Select } from '/@/shared/components/select/select';
|
||||
import { Slider } from '/@/shared/components/slider/slider';
|
||||
import { Switch } from '/@/shared/components/switch/switch';
|
||||
@@ -43,6 +47,7 @@ const SIDE_QUEUE_OPTIONS = [
|
||||
export const ControlSettings = () => {
|
||||
const { t } = useTranslation();
|
||||
const settings = useGeneralSettings();
|
||||
const playerbarSlider = usePlayerbarSlider();
|
||||
const { setSettings } = useSettingsStoreActions();
|
||||
|
||||
const controlOptions: SettingOption[] = [
|
||||
@@ -621,6 +626,202 @@ export const ControlSettings = () => {
|
||||
isHidden: false,
|
||||
title: t('setting.playerbarOpenDrawer', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<SegmentedControl
|
||||
data={[
|
||||
{
|
||||
label: t('setting.playerbarSliderType', {
|
||||
context: 'optionSlider',
|
||||
postProcess: 'titleCase',
|
||||
}),
|
||||
value: PlayerbarSliderType.SLIDER,
|
||||
},
|
||||
{
|
||||
label: t('setting.playerbarSliderType', {
|
||||
context: 'optionWaveform',
|
||||
postProcess: 'titleCase',
|
||||
}),
|
||||
value: PlayerbarSliderType.WAVEFORM,
|
||||
},
|
||||
]}
|
||||
onChange={(value) => {
|
||||
setSettings({
|
||||
general: {
|
||||
...settings,
|
||||
playerbarSlider: {
|
||||
...playerbarSlider,
|
||||
type: value as PlayerbarSliderType,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
size="sm"
|
||||
value={playerbarSlider?.type || PlayerbarSliderType.WAVEFORM}
|
||||
w="100%"
|
||||
/>
|
||||
),
|
||||
description: t('setting.playerbarSlider', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: false,
|
||||
title: t('setting.playerbarSlider', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
...(playerbarSlider?.type === PlayerbarSliderType.WAVEFORM
|
||||
? [
|
||||
{
|
||||
control: (
|
||||
<SegmentedControl
|
||||
data={[
|
||||
{
|
||||
label: t('setting.playerbarWaveformAlign', {
|
||||
context: 'optionTop',
|
||||
postProcess: 'titleCase',
|
||||
}),
|
||||
value: BarAlign.TOP,
|
||||
},
|
||||
{
|
||||
label: t('setting.playerbarWaveformAlign', {
|
||||
context: 'optionCenter',
|
||||
postProcess: 'titleCase',
|
||||
}),
|
||||
value: BarAlign.CENTER,
|
||||
},
|
||||
{
|
||||
label: t('setting.playerbarWaveformAlign', {
|
||||
context: 'optionBottom',
|
||||
postProcess: 'titleCase',
|
||||
}),
|
||||
value: BarAlign.BOTTOM,
|
||||
},
|
||||
]}
|
||||
onChange={(value) => {
|
||||
setSettings({
|
||||
general: {
|
||||
...settings,
|
||||
playerbarSlider: {
|
||||
...playerbarSlider,
|
||||
barAlign: (value as BarAlign) || BarAlign.CENTER,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
size="sm"
|
||||
value={playerbarSlider?.barAlign || BarAlign.CENTER}
|
||||
w="100%"
|
||||
/>
|
||||
),
|
||||
description: t('setting.playerbarWaveformAlign', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: false,
|
||||
title: t('setting.playerbarWaveformAlign', {
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Slider
|
||||
defaultValue={playerbarSlider?.barWidth ?? 2}
|
||||
max={10}
|
||||
min={0}
|
||||
onChangeEnd={(value) => {
|
||||
setSettings({
|
||||
general: {
|
||||
...settings,
|
||||
playerbarSlider: {
|
||||
...playerbarSlider,
|
||||
barWidth: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
step={1}
|
||||
styles={{
|
||||
root: {},
|
||||
}}
|
||||
w="120px"
|
||||
/>
|
||||
),
|
||||
description: t('setting.playerbarWaveformBarWidth', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: false,
|
||||
title: t('setting.playerbarWaveformBarWidth', {
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Slider
|
||||
defaultValue={playerbarSlider?.barGap || 0}
|
||||
max={10}
|
||||
min={0}
|
||||
onChangeEnd={(value) => {
|
||||
setSettings({
|
||||
general: {
|
||||
...settings,
|
||||
playerbarSlider: {
|
||||
...playerbarSlider,
|
||||
barGap: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
step={1}
|
||||
styles={{
|
||||
root: {},
|
||||
}}
|
||||
w="120px"
|
||||
/>
|
||||
),
|
||||
description: t('setting.playerbarWaveformGap', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: false,
|
||||
title: t('setting.playerbarWaveformGap', {
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Slider
|
||||
defaultValue={playerbarSlider?.barRadius ?? 4}
|
||||
max={20}
|
||||
min={0}
|
||||
onChangeEnd={(value) => {
|
||||
setSettings({
|
||||
general: {
|
||||
...settings,
|
||||
playerbarSlider: {
|
||||
...playerbarSlider,
|
||||
barRadius: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
step={1}
|
||||
styles={{
|
||||
root: {},
|
||||
}}
|
||||
w="120px"
|
||||
/>
|
||||
),
|
||||
description: t('setting.playerbarWaveformRadius', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: false,
|
||||
title: t('setting.playerbarWaveformRadius', {
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
return <SettingsSection options={controlOptions} />;
|
||||
|
||||
@@ -6,13 +6,12 @@ import {
|
||||
SettingOption,
|
||||
SettingsSection,
|
||||
} from '/@/renderer/features/settings/components/settings-section';
|
||||
import { usePlayerActions, usePlayerProperties, usePlayerStatus } from '/@/renderer/store';
|
||||
import { usePlayerStatus } from '/@/renderer/store';
|
||||
import { usePlaybackSettings, useSettingsStoreActions } from '/@/renderer/store/settings.store';
|
||||
import { Select } from '/@/shared/components/select/select';
|
||||
import { Slider } from '/@/shared/components/slider/slider';
|
||||
import { Switch } from '/@/shared/components/switch/switch';
|
||||
import { toast } from '/@/shared/components/toast/toast';
|
||||
import { CrossfadeStyle, PlayerStatus, PlayerStyle, PlayerType } from '/@/shared/types/types';
|
||||
import { PlayerStatus, PlayerType } from '/@/shared/types/types';
|
||||
|
||||
const ipc = isElectron() ? window.api.ipc : null;
|
||||
|
||||
@@ -27,9 +26,6 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
|
||||
const { setSettings } = useSettingsStoreActions();
|
||||
const status = usePlayerStatus();
|
||||
|
||||
const { crossfadeDuration, transitionType } = usePlayerProperties();
|
||||
const { setCrossfadeDuration, setTransitionType } = usePlayerActions();
|
||||
|
||||
const [audioDevices, setAudioDevices] = useState<{ label: string; value: string }[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -98,41 +94,6 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
|
||||
isHidden: !isElectron() || settings.type !== PlayerType.WEB,
|
||||
title: t('setting.audioDevice', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Select
|
||||
data={[
|
||||
{
|
||||
label: t('setting.playbackStyle', {
|
||||
context: 'optionNormal',
|
||||
postProcess: 'titleCase',
|
||||
}),
|
||||
value: PlayerStyle.GAPLESS,
|
||||
},
|
||||
{
|
||||
label: t('setting.playbackStyle', {
|
||||
context: 'optionCrossFade',
|
||||
postProcess: 'titleCase',
|
||||
}),
|
||||
value: PlayerStyle.CROSSFADE,
|
||||
},
|
||||
]}
|
||||
defaultValue={transitionType}
|
||||
disabled={settings.type !== PlayerType.WEB || status === PlayerStatus.PLAYING}
|
||||
onChange={(e) => setTransitionType(e as PlayerStyle)}
|
||||
/>
|
||||
),
|
||||
description: t('setting.playbackStyle', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: settings.type !== PlayerType.WEB,
|
||||
note: status === PlayerStatus.PLAYING ? 'Player must be paused' : undefined,
|
||||
title: t('setting.playbackStyle', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
@@ -174,71 +135,6 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Slider
|
||||
defaultValue={crossfadeDuration}
|
||||
disabled={
|
||||
settings.type !== PlayerType.WEB ||
|
||||
settings.style !== PlayerStyle.CROSSFADE ||
|
||||
status === PlayerStatus.PLAYING
|
||||
}
|
||||
max={15}
|
||||
min={3}
|
||||
onChangeEnd={(e) => setCrossfadeDuration(e)}
|
||||
w={100}
|
||||
/>
|
||||
),
|
||||
description: t('setting.crossfadeDuration', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: settings.type !== PlayerType.WEB,
|
||||
note: status === PlayerStatus.PLAYING ? 'Player must be paused' : undefined,
|
||||
title: t('setting.crossfadeDuration', {
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Select
|
||||
data={[
|
||||
{ label: 'Linear', value: CrossfadeStyle.LINEAR },
|
||||
{ label: 'Constant Power', value: CrossfadeStyle.CONSTANT_POWER },
|
||||
{
|
||||
label: 'Constant Power (Slow cut)',
|
||||
value: CrossfadeStyle.CONSTANT_POWER_SLOW_CUT,
|
||||
},
|
||||
{
|
||||
label: 'Constant Power (Slow fade)',
|
||||
value: CrossfadeStyle.CONSTANT_POWER_SLOW_FADE,
|
||||
},
|
||||
{ label: 'Dipped', value: CrossfadeStyle.DIPPED },
|
||||
{ label: 'Equal Power', value: CrossfadeStyle.EQUALPOWER },
|
||||
]}
|
||||
defaultValue={settings.crossfadeStyle}
|
||||
disabled={
|
||||
settings.type !== PlayerType.WEB ||
|
||||
settings.style !== PlayerStyle.CROSSFADE ||
|
||||
status === PlayerStatus.PLAYING
|
||||
}
|
||||
onChange={(e) => {
|
||||
if (!e) return;
|
||||
setSettings({
|
||||
playback: { ...settings, crossfadeStyle: e as CrossfadeStyle },
|
||||
});
|
||||
}}
|
||||
width={200}
|
||||
/>
|
||||
),
|
||||
description: t('setting.crossfadeStyle', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: settings.type !== PlayerType.WEB,
|
||||
note: status === PlayerStatus.PLAYING ? 'Player must be paused' : undefined,
|
||||
title: t('setting.crossfadeStyle', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
];
|
||||
|
||||
return <SettingsSection divider={!hasFancyAudio} options={audioOptions} />;
|
||||
|
||||
Reference in New Issue
Block a user