"use client"; import { DownloadIcon, PauseIcon, PlayIcon, Volume2Icon, VolumeXIcon } from "lucide-react"; import Image from "next/image"; import { useCallback, useEffect, useRef, useState } from "react"; import { Button } from "@/components/ui/button"; import { Slider } from "@/components/ui/slider"; import { cn } from "@/lib/utils"; interface AudioProps { id: string; assetId?: string; src: string; title: string; description?: string; artwork?: string; durationMs?: number; className?: string; } function formatTime(seconds: number): string { if (!Number.isFinite(seconds) || seconds < 0) return "0:00"; const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins}:${secs.toString().padStart(2, "0")}`; } export function Audio({ id, src, title, description, artwork, durationMs, className }: AudioProps) { const audioRef = useRef(null); const [isPlaying, setIsPlaying] = useState(false); const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(durationMs ? durationMs / 1000 : 0); const [volume, setVolume] = useState(1); const [isMuted, setIsMuted] = useState(false); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); // Handle play/pause const togglePlayPause = useCallback(() => { const audio = audioRef.current; if (!audio) return; if (isPlaying) { audio.pause(); } else { audio.play().catch((err) => { console.error("Error playing audio:", err); setError("Failed to play audio"); }); } }, [isPlaying]); // Handle seek const handleSeek = useCallback((value: number[]) => { const audio = audioRef.current; if (!audio || !Number.isFinite(value[0])) return; audio.currentTime = value[0]; setCurrentTime(value[0]); }, []); // Handle volume change const handleVolumeChange = useCallback((value: number[]) => { const audio = audioRef.current; if (!audio || !Number.isFinite(value[0])) return; const newVolume = value[0]; audio.volume = newVolume; setVolume(newVolume); setIsMuted(newVolume === 0); }, []); // Toggle mute const toggleMute = useCallback(() => { const audio = audioRef.current; if (!audio) return; if (isMuted) { audio.volume = volume || 1; setIsMuted(false); } else { audio.volume = 0; setIsMuted(true); } }, [isMuted, volume]); // Handle download const handleDownload = useCallback(async () => { try { const response = await fetch(src); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `${title.replace(/[^a-zA-Z0-9]/g, "_")}.mp3`; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); } catch (err) { console.error("Error downloading audio:", err); } }, [src, title]); // Set up audio event listeners useEffect(() => { const audio = audioRef.current; if (!audio) return; const handleLoadedMetadata = () => { setDuration(audio.duration); setIsLoading(false); }; const handleTimeUpdate = () => { setCurrentTime(audio.currentTime); }; const handlePlay = () => setIsPlaying(true); const handlePause = () => setIsPlaying(false); const handleEnded = () => { setIsPlaying(false); setCurrentTime(0); }; const handleError = () => { setError("Failed to load audio"); setIsLoading(false); }; const handleCanPlay = () => setIsLoading(false); audio.addEventListener("loadedmetadata", handleLoadedMetadata); audio.addEventListener("timeupdate", handleTimeUpdate); audio.addEventListener("play", handlePlay); audio.addEventListener("pause", handlePause); audio.addEventListener("ended", handleEnded); audio.addEventListener("error", handleError); audio.addEventListener("canplay", handleCanPlay); return () => { audio.removeEventListener("loadedmetadata", handleLoadedMetadata); audio.removeEventListener("timeupdate", handleTimeUpdate); audio.removeEventListener("play", handlePlay); audio.removeEventListener("pause", handlePause); audio.removeEventListener("ended", handleEnded); audio.removeEventListener("error", handleError); audio.removeEventListener("canplay", handleCanPlay); }; }, []); if (error) { return (

{title}

{error}

); } return (
{/* Hidden audio element */}
{/* Artwork */}
{artwork ? ( {title} ) : (
)}
{/* Content */}
{/* Title and description */}

{title}

{description && (

{description}

)}
{/* Progress bar */}
{formatTime(currentTime)} {formatTime(duration)}
{/* Controls */}
{/* Play/Pause button */} {/* Volume control */}
{/* Custom volume bar - visually distinct from progress slider */}
handleVolumeChange([Number.parseFloat(e.target.value)])} className="absolute inset-0 h-full w-full cursor-pointer opacity-0" aria-label="Volume" />
{/* Download button */}
); }