From 6d6b1baa6206b31cc6173c044cdca7f051cd0cab Mon Sep 17 00:00:00 2001 From: angel Date: Fri, 20 Jun 2025 02:47:12 +0000 Subject: [PATCH] feat: implement volume persistence and auto-play control in AudioPlayer and AudioPlayerContext --- app/components/AudioPlayer.tsx | 26 +++++++++++++++++++++++--- app/components/AudioPlayerContext.tsx | 25 +++++++++++++++---------- app/components/FullScreenPlayer.tsx | 2 +- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/app/components/AudioPlayer.tsx b/app/components/AudioPlayer.tsx index bd0dd56..00b5bb3 100644 --- a/app/components/AudioPlayer.tsx +++ b/app/components/AudioPlayer.tsx @@ -31,6 +31,19 @@ export const AudioPlayer: React.FC = () => { useEffect(() => { setIsClient(true); + // Load saved volume + const savedVolume = localStorage.getItem('navidrome-volume'); + if (savedVolume) { + try { + const volumeValue = parseFloat(savedVolume); + if (volumeValue >= 0 && volumeValue <= 1) { + setVolume(volumeValue); + } + } catch (error) { + console.error('Failed to parse saved volume:', error); + } + } + // Clean up old localStorage entries with track IDs const keysToRemove: string[] = []; for (let i = 0; i < localStorage.length; i++) { @@ -42,6 +55,16 @@ export const AudioPlayer: React.FC = () => { keysToRemove.forEach(key => localStorage.removeItem(key)); }, []); + // Apply volume to audio element when volume changes + useEffect(() => { + const audioCurrent = audioRef.current; + if (audioCurrent) { + audioCurrent.volume = volume; + } + // Save volume to localStorage + localStorage.setItem('navidrome-volume', volume.toString()); + }, [volume]); + // Save position when component unmounts or track changes useEffect(() => { const audioCurrent = audioRef.current; @@ -248,9 +271,6 @@ export const AudioPlayer: React.FC = () => { const handleVolumeChange = (e: React.ChangeEvent) => { const newVolume = parseFloat(e.target.value); setVolume(newVolume); - if (audioCurrent) { - audioCurrent.volume = newVolume; - } }; function formatTime(seconds: number): string { diff --git a/app/components/AudioPlayerContext.tsx b/app/components/AudioPlayerContext.tsx index 51c8197..9996465 100644 --- a/app/components/AudioPlayerContext.tsx +++ b/app/components/AudioPlayerContext.tsx @@ -69,7 +69,10 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c const savedCurrentTrack = localStorage.getItem('navidrome-currentTrack'); if (savedCurrentTrack) { try { - setCurrentTrack(JSON.parse(savedCurrentTrack)); + const track = JSON.parse(savedCurrentTrack); + // Clear autoPlay flag when loading from localStorage to prevent auto-play on refresh + track.autoPlay = false; + setCurrentTrack(track); } catch (error) { console.error('Failed to parse saved current track:', error); } @@ -78,7 +81,9 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c useEffect(() => { if (currentTrack) { - localStorage.setItem('navidrome-currentTrack', JSON.stringify(currentTrack)); + // Remove autoPlay flag when saving to localStorage + const { autoPlay, ...trackToSave } = currentTrack; + localStorage.setItem('navidrome-currentTrack', JSON.stringify(trackToSave)); } else { localStorage.removeItem('navidrome-currentTrack'); } @@ -282,11 +287,11 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c } // Play the first shuffled track and set the rest as queue - playTrack(shuffledTracks[0]); + playTrack(shuffledTracks[0], true); // Enable autoplay setQueue(shuffledTracks.slice(1)); } else { // Normal order: play first track and set the rest as queue - playTrack(tracks[0]); + playTrack(tracks[0], true); // Enable autoplay setQueue(tracks.slice(1)); } } @@ -332,11 +337,11 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c } setQueue(remainingTracks); - playTrack(tracks[startingIndex]); + playTrack(tracks[startingIndex], true); // Enable autoplay } else { // Normal order: set the remaining tracks after the starting track as queue setQueue(tracks.slice(startingIndex + 1)); - playTrack(tracks[startingIndex]); + playTrack(tracks[startingIndex], true); // Enable autoplay } toast({ @@ -360,8 +365,8 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c const targetTrack = queue[index]; // Remove all tracks before the target track (including the target track) setQueue((prevQueue) => prevQueue.slice(index + 1)); - // Play the target track - playTrack(targetTrack); + // Play the target track with autoplay enabled + playTrack(targetTrack, true); } }, [queue, playTrack]); @@ -445,11 +450,11 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c } // Play the first shuffled track and set the rest as queue - playTrack(shuffledTracks[0]); + playTrack(shuffledTracks[0], true); // Enable autoplay setQueue(shuffledTracks.slice(1)); } else { // Normal order: play first track and set the rest as queue - playTrack(allTracks[0]); + playTrack(allTracks[0], true); // Enable autoplay setQueue(allTracks.slice(1)); } } diff --git a/app/components/FullScreenPlayer.tsx b/app/components/FullScreenPlayer.tsx index b9bbac8..99f205d 100644 --- a/app/components/FullScreenPlayer.tsx +++ b/app/components/FullScreenPlayer.tsx @@ -334,7 +334,7 @@ export const FullScreenPlayer: React.FC = ({ isOpen, onCl

{currentTrack.name}

- + {currentTrack.artist}