'use client'; import { useState, useRef, useEffect } from 'react'; import { motion } from 'framer-motion'; import { Mic, Square, Loader2, Activity, Radio, RadioTower, CheckCircle, ArrowDown } from 'lucide-react'; import { useAppStore } from '@/store/useAppStore'; import { apiService } from '@/services/api'; import { scrollToSection } from '@/config/navigation'; export function LiveStreamSection() { const { recordingState, setRecordingState, liveTranscript, setLiveTranscript, socketStatus, setSocketStatus, features, setTranscription, setExtracting, setFeatures, setPrediction, setError } = useAppStore(); const [mediaRecorder, setMediaRecorder] = useState(null); const [recordedBytes, setRecordedBytes] = useState(0); const audioChunksRef = useRef([]); const sourceStreamRef = useRef(null); const stopInProgressRef = useRef(false); const transcriptEndRef = useRef(null); // Auto-scroll transcript useEffect(() => { transcriptEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [liveTranscript]); const startCapture = async () => { try { setError(null); setLiveTranscript(''); setTranscription(''); setFeatures(null); setPrediction(null); setRecordedBytes(0); audioChunksRef.current = []; stopInProgressRef.current = false; // Request microphone recording const stream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true, }, video: false }); sourceStreamRef.current = stream; const mimeType = MediaRecorder.isTypeSupported('audio/webm;codecs=opus') ? 'audio/webm;codecs=opus' : 'audio/webm'; const recorder = new MediaRecorder(stream, { mimeType }); recorder.ondataavailable = (event) => { if (event.data.size > 0) { audioChunksRef.current.push(event.data); setRecordedBytes((current) => current + event.data.size); } }; recorder.onstop = () => { void finalizeRecording(); }; recorder.start(1000); setMediaRecorder(recorder); setRecordingState('recording'); setSocketStatus('connected'); } catch (err: any) { console.error(err); // Handle microphone permission errors gracefully const isPermissionDenied = err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError'; const errMsg = isPermissionDenied ? 'Microphone permission is required to record conversations.' : (err.message || 'Failed to record audio.'); setError(errMsg); setRecordingState('idle'); setSocketStatus('disconnected'); stopCaptureDevices(); } }; const stopCaptureDevices = () => { sourceStreamRef.current?.getTracks().forEach(t => t.stop()); sourceStreamRef.current = null; }; const stopCapture = () => { if (stopInProgressRef.current) return; stopInProgressRef.current = true; if (mediaRecorder && mediaRecorder.state !== 'inactive') { mediaRecorder.stop(); mediaRecorder.stream.getTracks().forEach(t => t.stop()); stopCaptureDevices(); } else { setRecordingState('idle'); setSocketStatus('disconnected'); stopCaptureDevices(); } setMediaRecorder(null); setRecordingState('processing'); }; const finalizeRecording = async () => { const chunks = audioChunksRef.current; if (!chunks.length) { setError('No audio was recorded. Please ensure your microphone is working and try again.'); setRecordingState('idle'); setSocketStatus('disconnected'); stopInProgressRef.current = false; return; } const recordedAt = new Date().toISOString().replace(/[:.]/g, '-'); const audioBlob = new Blob(chunks, { type: 'audio/webm' }); const audioFile = new File([audioBlob], `conversation-recording-${recordedAt}.webm`, { type: 'audio/webm' }); setSocketStatus('disconnected'); setRecordingState('analyzing'); setExtracting(true); try { const result = await apiService.uploadAudio(audioFile); setLiveTranscript(result.transcription); setTranscription(result.transcription); setFeatures(result.features); setPrediction(result.prediction); useAppStore.getState().refreshFollowUpAlerts(); setExtracting(false); setRecordingState('completed'); stopInProgressRef.current = false; scrollToSection('processing'); } catch (err: any) { console.error(err); setExtracting(false); setRecordingState('idle'); stopInProgressRef.current = false; setError(err?.message || 'Failed to process the finalized recording.'); } }; const capturedSizeMb = (recordedBytes / (1024 * 1024)).toFixed(2); return (
{/* Decorative blobs */}

Record Customer Conversation

Record customer conversations through the microphone, then analyze the finalized recording.

{socketStatus === 'connected' && ( )} {socketStatus === 'connected' ? ( ) : ( )} {socketStatus === 'connected' ? 'Recording' : 'Recorder idle'}
{recordingState === 'idle' || recordingState === 'completed' ? ( ) : recordingState === 'recording' ? ( ) : ( )}
{/* Live Transcript Area */}
{recordingState === 'recording' && (
RECORDING
)} {recordingState === 'completed' ? (

Recording Complete & Analyzed!

The meeting audio has been successfully transcribed, diarized, and evaluated for business signals.

) : recordingState === 'recording' ? (
{/* Live waveform */}
{Array.from({ length: 24 }).map((_, i) => ( ))}

Recording conversation audio locally

{capturedSizeMb} MB buffered ยท Transcript appears after you stop recording.

) : recordingState === 'processing' || recordingState === 'analyzing' ? (

{recordingState === 'processing' ? 'Finalizing one complete WebM file' : 'Running Whisper and sales intelligence'}

{[0, 1, 2].map((i) => ( ))}
) : (

Ready to record

Click Start Recording to begin capturing audio from your microphone.

)}
); }