diff --git a/app/api/test-navidrome/route.ts b/app/api/test-navidrome/route.ts new file mode 100644 index 0000000..e69de29 diff --git a/app/components/AudioPlayer.tsx b/app/components/AudioPlayer.tsx index 353c595..bd0dd56 100644 --- a/app/components/AudioPlayer.tsx +++ b/app/components/AudioPlayer.tsx @@ -84,8 +84,17 @@ export const AudioPlayer: React.FC = () => { localStorage.removeItem('navidrome-current-track-time'); } - audioCurrent.play(); - setIsPlaying(true); + // Auto-play only if the track has the autoPlay flag + if (currentTrack.autoPlay) { + audioCurrent.play().then(() => { + setIsPlaying(true); + }).catch((error) => { + console.error('Failed to auto-play:', error); + setIsPlaying(false); + }); + } else { + setIsPlaying(false); + } } }, [currentTrack]); @@ -121,11 +130,21 @@ export const AudioPlayer: React.FC = () => { lastSavedTime = audioCurrent.currentTime; } }; + + const handlePlay = () => { + setIsPlaying(true); + }; + + const handlePause = () => { + setIsPlaying(false); + }; if (audioCurrent) { audioCurrent.addEventListener('timeupdate', updateProgress); audioCurrent.addEventListener('ended', handleTrackEnd); audioCurrent.addEventListener('seeked', handleSeeked); + audioCurrent.addEventListener('play', handlePlay); + audioCurrent.addEventListener('pause', handlePause); } return () => { @@ -133,6 +152,8 @@ export const AudioPlayer: React.FC = () => { audioCurrent.removeEventListener('timeupdate', updateProgress); audioCurrent.removeEventListener('ended', handleTrackEnd); audioCurrent.removeEventListener('seeked', handleSeeked); + audioCurrent.removeEventListener('play', handlePlay); + audioCurrent.removeEventListener('pause', handlePause); } }; }, [playNextTrack, currentTrack]); @@ -213,10 +234,15 @@ export const AudioPlayer: React.FC = () => { if (audioCurrent) { if (isPlaying) { audioCurrent.pause(); + setIsPlaying(false); } else { - audioCurrent.play(); + audioCurrent.play().then(() => { + setIsPlaying(true); + }).catch((error) => { + console.error('Failed to play audio:', error); + setIsPlaying(false); + }); } - setIsPlaying(!isPlaying); } }; const handleVolumeChange = (e: React.ChangeEvent) => { diff --git a/app/components/AudioPlayerContext.tsx b/app/components/AudioPlayerContext.tsx index dee80e1..a0fcc9f 100644 --- a/app/components/AudioPlayerContext.tsx +++ b/app/components/AudioPlayerContext.tsx @@ -15,11 +15,12 @@ interface Track { coverArt?: string; albumId: string; artistId: string; + autoPlay?: boolean; // Flag to control auto-play } interface AudioPlayerContextProps { currentTrack: Track | null; - playTrack: (track: Track) => void; + playTrack: (track: Track, autoPlay?: boolean) => void; queue: Track[]; addToQueue: (track: Track) => void; playNextTrack: () => void; @@ -92,14 +93,17 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c }; }, [api]); - const playTrack = useCallback((track: Track) => { + const playTrack = useCallback((track: Track, autoPlay: boolean = false) => { // Clear saved timestamp when manually playing a track localStorage.removeItem('navidrome-current-track-time'); if (currentTrack) { setPlayedTracks((prev) => [...prev, currentTrack]); } - setCurrentTrack(track); + + // Set autoPlay flag on the track + const trackWithAutoPlay = { ...track, autoPlay }; + setCurrentTrack(trackWithAutoPlay); // Scrobble the track api.scrobble(track.id).catch(error => { @@ -126,7 +130,7 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c if (queue.length > 0) { const nextTrack = queue[0]; setQueue((prevQueue) => prevQueue.slice(1)); - playTrack(nextTrack); + playTrack(nextTrack, true); // Auto-play next track } }, [queue, playTrack]); @@ -143,9 +147,9 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c setQueue((prevQueue) => [currentTrack, ...prevQueue]); } - setCurrentTrack(previousTrack); + playTrack(previousTrack, true); // Auto-play previous track } - }, [playedTracks, currentTrack]); + }, [playedTracks, currentTrack, playTrack]); const addAlbumToQueue = useCallback(async (albumId: string) => { setIsLoading(true); diff --git a/app/components/FullScreenPlayer.tsx b/app/components/FullScreenPlayer.tsx index 8785b3a..4284fc2 100644 --- a/app/components/FullScreenPlayer.tsx +++ b/app/components/FullScreenPlayer.tsx @@ -139,53 +139,35 @@ export const FullScreenPlayer: React.FC = ({ isOpen, onCl // Sync with main audio player (improved responsiveness) useEffect(() => { - let lastUpdate = 0; - const throttleMs = 100; // Update at most every 100ms for better responsiveness - const syncWithMainPlayer = () => { - const now = Date.now(); - if (now - lastUpdate < throttleMs) return; - lastUpdate = now; - const mainAudio = document.querySelector('audio') as HTMLAudioElement; if (mainAudio && currentTrack) { const newCurrentTime = mainAudio.currentTime; const newDuration = mainAudio.duration || 0; const newIsPlaying = !mainAudio.paused; - // Always update playing state for better responsiveness - if (newIsPlaying !== isPlaying) { - setIsPlaying(newIsPlaying); - } + // Always update playing state immediately + setIsPlaying(newIsPlaying); + setCurrentTime(newCurrentTime); + setDuration(newDuration); + setVolume(mainAudio.volume); - // Only update state if values have changed significantly - if (Math.abs(newCurrentTime - currentTime) > 0.3) { - setCurrentTime(newCurrentTime); - } - if (Math.abs(newDuration - duration) > 0.1) { - setDuration(newDuration); - } if (newDuration > 0) { const newProgress = (newCurrentTime / newDuration) * 100; - if (Math.abs(newProgress - progress) > 0.1) { - setProgress(newProgress); - } - } - if (Math.abs(mainAudio.volume - volume) > 0.01) { - setVolume(mainAudio.volume); + setProgress(newProgress); } } }; - if (isOpen) { + if (isOpen && currentTrack) { // Initial sync syncWithMainPlayer(); - // Set up interval to keep syncing - more frequent for better responsiveness - const interval = setInterval(syncWithMainPlayer, 50); + // Set up interval to keep syncing + const interval = setInterval(syncWithMainPlayer, 100); return () => clearInterval(interval); } - }, [isOpen, currentTrack]); // Removed other dependencies to prevent loop + }, [isOpen, currentTrack?.id]); // React to track changes // Extract dominant color from cover art useEffect(() => {