mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
Waveform playerbar improvements (#1781)
* Defer waveform loading & show default seek bar as fallback * Add configurable waveform loading delay * Add 2s default value for waveform loading delay * disable transcoding config on waveform url --------- Co-authored-by: jeffvli <jeffvictorli@gmail.com>
This commit is contained in:
@@ -1082,6 +1082,8 @@
|
||||
"volumeWheelStep": "volume wheel step",
|
||||
"volumeWidth_description": "the width of the volume slider",
|
||||
"volumeWidth": "volume slider width",
|
||||
"waveformLoadingDelay": "waveform loading delay",
|
||||
"waveformLoadingDelay_description": "delay in seconds before loading waveform. increase this value if you are experiencing stutters when using the web player.",
|
||||
"webAudio_description": "use web audio. this enables advanced features like replaygain. disable if you experience otherwise",
|
||||
"webAudio": "use web audio",
|
||||
"windowBarStyle_description": "select the style of the window bar",
|
||||
|
||||
@@ -7,10 +7,10 @@ import { CustomPlayerbarSlider } from './playerbar-slider';
|
||||
import styles from './playerbar-waveform.module.css';
|
||||
|
||||
import { useSongUrl } from '/@/renderer/features/player/audio-player/hooks/use-stream-url';
|
||||
import { PlayerbarSeekSlider } from '/@/renderer/features/player/components/playerbar-seek-slider';
|
||||
import { usePlayer } from '/@/renderer/features/player/context/player-context';
|
||||
import { BarAlign, usePlayerbarSlider, usePlayerSong, usePlayerTimestamp } from '/@/renderer/store';
|
||||
import { useAppThemeColors, useColorScheme } from '/@/renderer/themes/use-app-theme';
|
||||
import { Spinner } from '/@/shared/components/spinner/spinner';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
|
||||
export const PlayerbarWaveform = () => {
|
||||
@@ -18,6 +18,7 @@ export const PlayerbarWaveform = () => {
|
||||
const playerbarSlider = usePlayerbarSlider();
|
||||
const currentTime = usePlayerTimestamp();
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const audioElementRef = useRef<HTMLAudioElement>(document.createElement('audio'));
|
||||
const { mediaSeekToTimestamp } = usePlayer();
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
@@ -29,7 +30,7 @@ export const PlayerbarWaveform = () => {
|
||||
|
||||
const songDuration = currentSong?.duration ? currentSong.duration / 1000 : 0;
|
||||
|
||||
const streamUrl = useSongUrl(currentSong, true, { bitrate: 64, enabled: true, format: 'mp3' });
|
||||
const streamUrl = useSongUrl(currentSong, true, { bitrate: 64, enabled: false, format: 'mp3' });
|
||||
|
||||
const { color } = useAppThemeColors();
|
||||
const primaryColor = (color['--theme-colors-primary'] as string) || 'rgb(53, 116, 252)';
|
||||
@@ -56,28 +57,20 @@ export const PlayerbarWaveform = () => {
|
||||
fillParent: true,
|
||||
height: 18,
|
||||
interact: false,
|
||||
media: audioElementRef.current,
|
||||
normalize: false,
|
||||
progressColor: primaryColor,
|
||||
url: streamUrl || undefined,
|
||||
waveColor,
|
||||
});
|
||||
|
||||
// Reset loading state when stream URL changes and ensure media is muted
|
||||
useEffect(() => {
|
||||
setIsLoading(true);
|
||||
if (wavesurfer) {
|
||||
wavesurfer.setVolume(0);
|
||||
const mediaElement = wavesurfer.getMediaElement();
|
||||
if (mediaElement) {
|
||||
mediaElement.muted = true;
|
||||
mediaElement.volume = 0;
|
||||
}
|
||||
}
|
||||
}, [streamUrl, wavesurfer]);
|
||||
}, [streamUrl]);
|
||||
|
||||
// Handle waveform ready state
|
||||
useEffect(() => {
|
||||
if (!wavesurfer) return;
|
||||
if (!wavesurfer || !streamUrl) return;
|
||||
|
||||
const handleReady = () => {
|
||||
setIsLoading(false);
|
||||
@@ -90,20 +83,18 @@ export const PlayerbarWaveform = () => {
|
||||
|
||||
wavesurfer.on('ready', handleReady);
|
||||
|
||||
// Check if already loaded
|
||||
if (wavesurfer.getDuration() > 0) {
|
||||
setIsLoading(false);
|
||||
const mediaElement = wavesurfer.getMediaElement();
|
||||
if (mediaElement) {
|
||||
mediaElement.muted = true;
|
||||
mediaElement.volume = 0;
|
||||
}
|
||||
}
|
||||
const waveformTimeout = setTimeout(
|
||||
() => {
|
||||
wavesurfer.load(streamUrl);
|
||||
},
|
||||
playerbarSlider?.loadingDelay ? playerbarSlider.loadingDelay * 1000 : 2000,
|
||||
);
|
||||
|
||||
return () => {
|
||||
wavesurfer.un('ready', handleReady);
|
||||
clearTimeout(waveformTimeout);
|
||||
};
|
||||
}, [wavesurfer]);
|
||||
}, [wavesurfer, streamUrl, playerbarSlider.loadingDelay]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!wavesurfer) return;
|
||||
@@ -363,12 +354,12 @@ export const PlayerbarWaveform = () => {
|
||||
height: '100%',
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
top: 3,
|
||||
width: '100%',
|
||||
}}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<Spinner container />
|
||||
<PlayerbarSeekSlider max={songDuration} min={0} />
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
@@ -477,6 +477,36 @@ export const ControlSettings = memo(() => {
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<NumberInput
|
||||
defaultValue={playerbarSlider?.loadingDelay ?? 2}
|
||||
max={30}
|
||||
min={0}
|
||||
onBlur={(e) => {
|
||||
setSettings({
|
||||
general: {
|
||||
...settings,
|
||||
playerbarSlider: {
|
||||
...playerbarSlider,
|
||||
loadingDelay: e.currentTarget.value
|
||||
? Number(e.currentTarget.value)
|
||||
: 2,
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
rightSection={<Text size="sm">s</Text>}
|
||||
width={75}
|
||||
/>
|
||||
),
|
||||
description: t('setting.waveformLoadingDelay', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: false,
|
||||
title: t('setting.waveformLoadingDelay', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
|
||||
@@ -305,6 +305,7 @@ const PlayerbarSliderSchema = z.object({
|
||||
barGap: z.number(),
|
||||
barRadius: z.number(),
|
||||
barWidth: z.number(),
|
||||
loadingDelay: z.number(),
|
||||
type: PlayerbarSliderTypeSchema,
|
||||
});
|
||||
|
||||
@@ -1148,6 +1149,7 @@ const initialState: SettingsState = {
|
||||
barGap: 1,
|
||||
barRadius: 4,
|
||||
barWidth: 2,
|
||||
loadingDelay: 2,
|
||||
type: PlayerbarSliderType.SLIDER,
|
||||
},
|
||||
playerItems,
|
||||
|
||||
Reference in New Issue
Block a user