diff --git a/src/app/layout.tsx b/src/app/layout.tsx index e7ad6f0..fd9eae2 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,6 +4,9 @@ import { Inter } from "next/font/google"; import "./globals.css"; import { ServiceWrapper } from "@/components/ServiceWrapper"; import Tag from "@/tag/Tag"; +import { AudioPlayer } from "@/components/AudioPlayer"; + +// Create new file: src/components/AudioPlayer.tsx const notoSans = Noto_Sans({ variable: "--font-noto-sans", subsets: ["latin"], @@ -28,6 +31,7 @@ export default function RootLayout({ + {children} diff --git a/src/components/AudioPlayer.tsx b/src/components/AudioPlayer.tsx new file mode 100644 index 0000000..c4641d2 --- /dev/null +++ b/src/components/AudioPlayer.tsx @@ -0,0 +1,162 @@ +'use client'; + +import { useState, useRef, useEffect } from 'react'; +import { Play, Pause, Volume2, VolumeX } from 'lucide-react'; + +interface AudioPlayerProps {} + +export function AudioPlayer({}: AudioPlayerProps) { + const audioRef = useRef(null); + const [isPlaying, setIsPlaying] = useState(false); + const [volume, setVolume] = useState(0.5); + const [currentTime, setCurrentTime] = useState(0); + const [duration, setDuration] = useState(0); + const [isMuted, setIsMuted] = useState(false); + const [previousVolume, setPreviousVolume] = useState(0.5); + + useEffect(() => { + const audio = audioRef.current; + if (!audio) return; + + const updateTime = () => setCurrentTime(audio.currentTime); + const updateDuration = () => setDuration(audio.duration); + const handleEnded = () => setIsPlaying(false); + + audio.addEventListener('timeupdate', updateTime); + audio.addEventListener('loadedmetadata', updateDuration); + audio.addEventListener('ended', handleEnded); + + return () => { + audio.removeEventListener('timeupdate', updateTime); + audio.removeEventListener('loadedmetadata', updateDuration); + audio.removeEventListener('ended', handleEnded); + }; + }, []); + + useEffect(() => { + if (audioRef.current) { + audioRef.current.volume = isMuted ? 0 : volume; + } + }, [volume, isMuted]); + + const togglePlayPause = () => { + if (audioRef.current) { + if (isPlaying) { + audioRef.current.pause(); + } else { + audioRef.current.play(); + } + setIsPlaying(!isPlaying); + } + }; + + const toggleMute = () => { + if (isMuted) { + setIsMuted(false); + setVolume(previousVolume); + } else { + setPreviousVolume(volume); + setIsMuted(true); + } + }; + + const handleVolumeChange = (e: React.ChangeEvent) => { + const newVolume = parseFloat(e.target.value); + setVolume(newVolume); + if (isMuted && newVolume > 0) { + setIsMuted(false); + } + }; + + const handleProgressChange = (e: React.ChangeEvent) => { + const newTime = parseFloat(e.target.value); + setCurrentTime(newTime); + if (audioRef.current) { + audioRef.current.currentTime = newTime; + } + }; + + const formatTime = (time: number) => { + if (!time || isNaN(time)) return '0:00'; + const minutes = Math.floor(time / 60); + const seconds = Math.floor(time % 60); + return `${minutes}:${seconds.toString().padStart(2, '0')}`; + }; + + return ( +
+
+ ); +} \ No newline at end of file