import React, { useEffect, useRef, useState } from 'react'; import { Button } from '../ui/controls'; import styles from './PlaylistPlayerBar.module.css'; function formatTime(sec: number): string { if (!Number.isFinite(sec) || sec < 0) return '0:00'; const s = Math.floor(sec); const m = Math.floor(s / 60); const r = s % 60; return `${String(m)}:${String(r).padStart(2, '0')}`; } function clampGain(v: number): number { if (!Number.isFinite(v)) return 1; return Math.max(0, Math.min(1, v)); } export type PlaylistPlayerBarProps = { title: string; subtitle?: string; audioEl: HTMLAudioElement | null; playing: boolean; gain: number; onGainChange: (gain: number) => void; onTogglePlay: () => void; onPrev: () => void; onNext: () => void; playLabel: string; pauseLabel: string; prevLabel: string; nextLabel: string; volumeLabel: string; scrubLabel: string; emptyLabel: string; }; export function PlaylistPlayerBar({ title, subtitle, audioEl, playing, gain, onGainChange, onTogglePlay, onPrev, onNext, playLabel, pauseLabel, prevLabel, nextLabel, volumeLabel, scrubLabel, emptyLabel, }: PlaylistPlayerBarProps) { const [current, setCurrent] = useState(0); const [duration, setDuration] = useState(0); const scrubbing = useRef(false); useEffect(() => { if (!audioEl) { setCurrent(0); setDuration(0); return; } const sync = () => { if (!scrubbing.current) setCurrent(audioEl.currentTime); setDuration(Number.isFinite(audioEl.duration) ? audioEl.duration : 0); }; sync(); audioEl.addEventListener('timeupdate', sync); audioEl.addEventListener('loadedmetadata', sync); audioEl.addEventListener('durationchange', sync); return () => { audioEl.removeEventListener('timeupdate', sync); audioEl.removeEventListener('loadedmetadata', sync); audioEl.removeEventListener('durationchange', sync); }; }, [audioEl]); useEffect(() => { if (audioEl) audioEl.volume = clampGain(gain); }, [audioEl, gain]); const hasTrack = Boolean(audioEl); return (
{hasTrack ? title : emptyLabel}
{hasTrack && subtitle ?
{subtitle}
: null}
{formatTime(current)} { scrubbing.current = true; }} onMouseUp={(e) => { scrubbing.current = false; if (audioEl) audioEl.currentTime = Number(e.currentTarget.value); }} onChange={(e) => { const v = Number(e.currentTarget.value); setCurrent(v); if (audioEl && !scrubbing.current) audioEl.currentTime = v; }} /> {formatTime(duration)}
); }