'use client'; import React, { useEffect, useRef, useState } from 'react'; import Image from 'next/image'; import { useAudioPlayer } from '@/app/components/AudioPlayerContext'; import { FaPlay, FaPause, FaVolumeHigh, FaForward, FaBackward } from "react-icons/fa6"; import ColorThief from '@neutrixs/colorthief'; import { Progress } from '@/components/ui/progress'; import { useToast } from '@/hooks/use-toast'; export const AudioPlayer: React.FC = () => { const { currentTrack, playPreviousTrack, addToQueue, playNextTrack, clearQueue } = useAudioPlayer(); const audioRef = useRef(null); const [progress, setProgress] = useState(0); const [isPlaying, setIsPlaying] = useState(false); const [showVolumeSlider, setShowVolumeSlider] = useState(false); const [volume, setVolume] = useState(1); const [isClient, setIsClient] = useState(false); const audioCurrent = audioRef.current; const { toast } = useToast(); useEffect(() => { setIsClient(true); }, []); // Save position when component unmounts or track changes useEffect(() => { return () => { const audioCurrent = audioRef.current; if (audioCurrent && currentTrack && audioCurrent.currentTime > 10) { localStorage.setItem(`navidrome-track-time-${currentTrack.id}`, audioCurrent.currentTime.toString()); } }; }, [currentTrack?.id]); useEffect(() => { const audioCurrent = audioRef.current; if (currentTrack && audioCurrent && audioCurrent.src !== currentTrack.url) { audioCurrent.src = currentTrack.url; // Check for saved timestamp (only restore if more than 10 seconds in) const savedTime = localStorage.getItem(`navidrome-track-time-${currentTrack.id}`); if (savedTime) { const time = parseFloat(savedTime); // Only restore if we were at least 10 seconds in and not near the end if (time > 10 && time < (currentTrack.duration - 30)) { const restorePosition = () => { if (audioCurrent.readyState >= 2) { // HAVE_CURRENT_DATA audioCurrent.currentTime = time; audioCurrent.removeEventListener('loadeddata', restorePosition); } }; if (audioCurrent.readyState >= 2) { audioCurrent.currentTime = time; } else { audioCurrent.addEventListener('loadeddata', restorePosition); } } } audioCurrent.play(); setIsPlaying(true); } }, [currentTrack?.id, currentTrack?.url]); useEffect(() => { const audioCurrent = audioRef.current; let lastSavedTime = 0; const updateProgress = () => { if (audioCurrent && currentTrack) { setProgress((audioCurrent.currentTime / audioCurrent.duration) * 100); // Save current time every 10 seconds, but only if we've moved forward significantly const currentTime = audioCurrent.currentTime; if (Math.abs(currentTime - lastSavedTime) >= 10 && currentTime > 10) { localStorage.setItem(`navidrome-track-time-${currentTrack.id}`, currentTime.toString()); lastSavedTime = currentTime; } } }; const handleTrackEnd = () => { if (currentTrack) { // Clear saved time when track ends localStorage.removeItem(`navidrome-track-time-${currentTrack.id}`); } playNextTrack(); }; const handleSeeked = () => { if (audioCurrent && currentTrack) { // Save immediately when user seeks localStorage.setItem(`navidrome-track-time-${currentTrack.id}`, audioCurrent.currentTime.toString()); lastSavedTime = audioCurrent.currentTime; } }; if (audioCurrent) { audioCurrent.addEventListener('timeupdate', updateProgress); audioCurrent.addEventListener('ended', handleTrackEnd); audioCurrent.addEventListener('seeked', handleSeeked); } return () => { if (audioCurrent) { audioCurrent.removeEventListener('timeupdate', updateProgress); audioCurrent.removeEventListener('ended', handleTrackEnd); audioCurrent.removeEventListener('seeked', handleSeeked); } }; }, [playNextTrack, currentTrack]); const handleProgressClick = (e: React.MouseEvent) => { if (audioCurrent && currentTrack) { const rect = e.currentTarget.getBoundingClientRect(); const clickX = e.clientX - rect.left; const newTime = (clickX / rect.width) * audioCurrent.duration; audioCurrent.currentTime = newTime; // Save the new position immediately localStorage.setItem(`navidrome-track-time-${currentTrack.id}`, newTime.toString()); } }; const togglePlayPause = () => { if (audioCurrent) { if (isPlaying) { audioCurrent.pause(); } else { audioCurrent.play(); } setIsPlaying(!isPlaying); } }; const handleVolumeChange = (e: React.ChangeEvent) => { const newVolume = parseFloat(e.target.value); setVolume(newVolume); if (audioCurrent) { audioCurrent.volume = newVolume; } }; function formatTime(seconds: number): string { if (isNaN(seconds) || seconds < 0) { return "0:00"; } const minutes = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60).toString().padStart(2, "0"); return `${minutes}:${secs}`; } if (!isClient) { return null; } return (
{currentTrack ? (
{/* Left side - Album art and track info */}
{currentTrack.name}

{currentTrack.name}

{currentTrack.artist}

{/* Center - Control buttons and progress bar */}
{/* Control buttons */}
{/* Progress bar below buttons - full width of this section */}
{formatTime(audioCurrent?.currentTime ?? 0)} {formatTime(audioCurrent?.duration ?? 0)}
{/* Right side - Extra space for balance */}
) : (

No track playing

)}
); };