import React, { useState, useRef, useMemo, useCallback, useEffect } from 'react'
// All 24 key+mode combinations
const ALL_KEYS_WITH_MODES = [
'C major', 'C minor', 'C# major', 'C# minor',
'D major', 'D minor', 'D# major', 'D# minor',
'E major', 'E minor', 'F major', 'F minor',
'F# major', 'F# minor', 'G major', 'G minor',
'G# major', 'G# minor', 'A major', 'A minor',
'A# major', 'A# minor', 'B major', 'B minor'
]
const KEY_NAMES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
function formatTime(seconds) {
if (!seconds || !isFinite(seconds)) return '0:00'
const mins = Math.floor(seconds / 60)
const secs = Math.floor(seconds % 60)
return `${mins}:${secs.toString().padStart(2, '0')}`
}
function ContinuationPlayer({ sessionId }) {
const audioRef = useRef(null)
const rafRef = useRef(null)
const [playing, setPlaying] = useState(false)
const [cTime, setCTime] = useState(0)
const [dur, setDur] = useState(0)
// Stable URL — only changes when sessionId changes, not on every render
const src = useMemo(
() => `/api/stem/${sessionId}/_continuation?processed=false&t=${Date.now()}`,
[sessionId]
)
// Try to read duration from the audio element (WAV streams may delay reporting it)
const tryReadDuration = useCallback(() => {
const audio = audioRef.current
if (!audio) return
const d = audio.duration
if (d && isFinite(d) && d > 0) {
setDur(d)
}
}, [])
// Smooth animation loop — reads currentTime every frame while playing
useEffect(() => {
if (!playing) {
if (rafRef.current) cancelAnimationFrame(rafRef.current)
return
}
const tick = () => {
if (audioRef.current) {
setCTime(audioRef.current.currentTime)
tryReadDuration()
}
rafRef.current = requestAnimationFrame(tick)
}
rafRef.current = requestAnimationFrame(tick)
return () => {
if (rafRef.current) cancelAnimationFrame(rafRef.current)
}
}, [playing, tryReadDuration])
const toggle = () => {
if (!audioRef.current) return
if (playing) {
audioRef.current.pause()
} else {
audioRef.current.play()
}
}
const handleSeek = (e) => {
const d = dur || audioRef.current?.duration
if (!audioRef.current || !d || !isFinite(d)) return
const rect = e.currentTarget.getBoundingClientRect()
const pct = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width))
audioRef.current.currentTime = pct * d
setCTime(audioRef.current.currentTime)
}
return (