mirror of
https://github.com/JezzWTF/vibepod.git
synced 2026-06-01 15:22:14 +00:00
3974a4cf69
Agent-Logs-Url: https://github.com/JezzWTF/vibepod/sessions/a78fcf03-e979-4777-a428-18cc8eccc095 Co-authored-by: LyAhn <27559362+LyAhn@users.noreply.github.com>
95 lines
2.7 KiB
TypeScript
95 lines
2.7 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
|
interface AudioPlayerState {
|
|
isPlaying: boolean;
|
|
currentTime: number;
|
|
duration: number;
|
|
volume: number;
|
|
}
|
|
|
|
export function useAudioPlayer(audioUrl: string | null) {
|
|
const audioRef = useRef<HTMLAudioElement | null>(null);
|
|
const [state, setState] = useState<AudioPlayerState>({
|
|
isPlaying: false,
|
|
currentTime: 0,
|
|
duration: 0,
|
|
volume: 1,
|
|
});
|
|
|
|
// Create/replace the Audio element whenever the URL changes
|
|
useEffect(() => {
|
|
if (!audioUrl) {
|
|
if (audioRef.current) {
|
|
audioRef.current.pause();
|
|
audioRef.current = null;
|
|
}
|
|
setState({ isPlaying: false, currentTime: 0, duration: 0, volume: 1 });
|
|
return;
|
|
}
|
|
|
|
const audio = new Audio(audioUrl);
|
|
audioRef.current = audio;
|
|
|
|
const onTimeUpdate = () =>
|
|
setState((prev) => ({ ...prev, currentTime: audio.currentTime }));
|
|
const onDurationChange = () =>
|
|
setState((prev) => ({ ...prev, duration: audio.duration }));
|
|
const onEnded = () =>
|
|
setState((prev) => ({ ...prev, isPlaying: false, currentTime: 0 }));
|
|
const onPlay = () => setState((prev) => ({ ...prev, isPlaying: true }));
|
|
const onPause = () => setState((prev) => ({ ...prev, isPlaying: false }));
|
|
|
|
audio.addEventListener("timeupdate", onTimeUpdate);
|
|
audio.addEventListener("durationchange", onDurationChange);
|
|
audio.addEventListener("loadedmetadata", onDurationChange);
|
|
audio.addEventListener("ended", onEnded);
|
|
audio.addEventListener("play", onPlay);
|
|
audio.addEventListener("pause", onPause);
|
|
|
|
return () => {
|
|
audio.pause();
|
|
audio.removeEventListener("timeupdate", onTimeUpdate);
|
|
audio.removeEventListener("durationchange", onDurationChange);
|
|
audio.removeEventListener("loadedmetadata", onDurationChange);
|
|
audio.removeEventListener("ended", onEnded);
|
|
audio.removeEventListener("play", onPlay);
|
|
audio.removeEventListener("pause", onPause);
|
|
};
|
|
}, [audioUrl]);
|
|
|
|
const toggle = useCallback(() => {
|
|
const audio = audioRef.current;
|
|
if (!audio) return;
|
|
if (audio.paused) {
|
|
audio.play();
|
|
} else {
|
|
audio.pause();
|
|
}
|
|
}, []);
|
|
|
|
const seek = useCallback((time: number) => {
|
|
const audio = audioRef.current;
|
|
if (!audio) return;
|
|
audio.currentTime = Math.max(0, Math.min(time, audio.duration));
|
|
}, []);
|
|
|
|
const setVolume = useCallback((v: number) => {
|
|
const audio = audioRef.current;
|
|
if (!audio) return;
|
|
audio.volume = Math.max(0, Math.min(1, v));
|
|
setState((prev) => ({ ...prev, volume: v }));
|
|
}, []);
|
|
|
|
return {
|
|
isPlaying: state.isPlaying,
|
|
currentTime: state.currentTime,
|
|
duration: state.duration,
|
|
volume: state.volume,
|
|
toggle,
|
|
seek,
|
|
setVolume,
|
|
};
|
|
}
|