import React, { useEffect, useRef, useState } from "react"; import WaveSurfer from "wavesurfer.js"; interface AudioWaveformProps { audioUrl: string; height?: number; className?: string; } const AudioWaveform: React.FC = ({ audioUrl, height = 32, className = "", }) => { const waveformRef = useRef(null); const wavesurfer = useRef(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(false); useEffect(() => { let mounted = true; const initWaveSurfer = async () => { if (!waveformRef.current || !audioUrl) return; try { // Clean up any existing instance if (wavesurfer.current) { try { wavesurfer.current.destroy(); } catch (e) { // Silently ignore destroy errors } wavesurfer.current = null; } wavesurfer.current = WaveSurfer.create({ container: waveformRef.current, waveColor: "rgba(255, 255, 255, 0.6)", progressColor: "rgba(255, 255, 255, 0.9)", cursorColor: "transparent", barWidth: 2, barGap: 1, height, normalize: true, interact: false, }); // Event listeners wavesurfer.current.on("ready", () => { if (mounted) { setIsLoading(false); setError(false); } }); wavesurfer.current.on("error", (err) => { console.error("WaveSurfer error:", err); if (mounted) { setError(true); setIsLoading(false); } }); await wavesurfer.current.load(audioUrl); } catch (err) { console.error("Failed to initialize WaveSurfer:", err); if (mounted) { setError(true); setIsLoading(false); } } }; initWaveSurfer(); return () => { mounted = false; if (wavesurfer.current) { try { wavesurfer.current.destroy(); } catch (e) { // Silently ignore destroy errors } wavesurfer.current = null; } }; }, [audioUrl, height]); if (error) { return (
Audio unavailable
); } return (
{isLoading && (
Loading...
)}
); }; export default AudioWaveform;