chrysopedia/frontend/src/components/AudioWaveform.tsx
jlightner 96608a3d8f feat: Installed wavesurfer.js, created AudioWaveform component with sha…
- "frontend/src/components/AudioWaveform.tsx"
- "frontend/src/hooks/useMediaSync.ts"
- "frontend/src/pages/WatchPage.tsx"
- "frontend/src/App.css"
- "frontend/package.json"

GSD-Task: S05/T02
2026-04-04 05:49:40 +00:00

58 lines
1.6 KiB
TypeScript

import { useEffect, useRef } from "react";
import WaveSurfer from "wavesurfer.js";
import type { MediaSyncState } from "../hooks/useMediaSync";
interface AudioWaveformProps {
mediaSync: MediaSyncState;
src: string;
}
/**
* Audio-only waveform visualiser powered by wavesurfer.js.
* Renders a hidden <audio> element owned by useMediaSync and an
* interactive waveform. Used when no video URL is available.
*/
export default function AudioWaveform({ mediaSync, src }: AudioWaveformProps) {
const containerRef = useRef<HTMLDivElement | null>(null);
const wsRef = useRef<WaveSurfer | null>(null);
useEffect(() => {
const container = containerRef.current;
const audio = mediaSync.videoRef.current as HTMLAudioElement | null;
if (!container || !audio) return;
const ws = WaveSurfer.create({
container,
media: audio,
height: 128,
waveColor: "rgba(34, 211, 238, 0.4)",
progressColor: "rgba(34, 211, 238, 0.8)",
cursorColor: "#22d3ee",
barWidth: 2,
barGap: 1,
barRadius: 2,
backend: "MediaElement",
});
wsRef.current = ws;
return () => {
ws.destroy();
wsRef.current = null;
};
// Re-create when src changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [src]);
return (
<div className="audio-waveform">
<audio
ref={mediaSync.videoRef as React.RefObject<HTMLAudioElement>}
src={src}
preload="metadata"
style={{ display: "none" }}
/>
<div ref={containerRef} className="audio-waveform__canvas" />
</div>
);
}