mirror of
https://github.com/siteboon/claudecodeui.git
synced 2025-12-15 13:39:39 +00:00
109 lines
2.8 KiB
JavaScript
Executable File
109 lines
2.8 KiB
JavaScript
Executable File
import { useState, useRef, useCallback } from 'react';
|
|
|
|
export function useAudioRecorder() {
|
|
const [isRecording, setRecording] = useState(false);
|
|
const [audioBlob, setAudioBlob] = useState(null);
|
|
const [error, setError] = useState(null);
|
|
const mediaRecorderRef = useRef(null);
|
|
const streamRef = useRef(null);
|
|
const chunksRef = useRef([]);
|
|
|
|
const start = useCallback(async () => {
|
|
try {
|
|
setError(null);
|
|
setAudioBlob(null);
|
|
chunksRef.current = [];
|
|
|
|
// Request microphone access
|
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
audio: {
|
|
echoCancellation: true,
|
|
noiseSuppression: true,
|
|
sampleRate: 16000,
|
|
}
|
|
});
|
|
|
|
streamRef.current = stream;
|
|
|
|
// Determine supported MIME type
|
|
const mimeType = MediaRecorder.isTypeSupported('audio/webm')
|
|
? 'audio/webm'
|
|
: 'audio/mp4';
|
|
|
|
// Create media recorder
|
|
const recorder = new MediaRecorder(stream, { mimeType });
|
|
mediaRecorderRef.current = recorder;
|
|
|
|
// Set up event handlers
|
|
recorder.ondataavailable = (e) => {
|
|
if (e.data.size > 0) {
|
|
chunksRef.current.push(e.data);
|
|
}
|
|
};
|
|
|
|
recorder.onstop = () => {
|
|
// Create blob from chunks
|
|
const blob = new Blob(chunksRef.current, { type: mimeType });
|
|
setAudioBlob(blob);
|
|
|
|
// Clean up stream
|
|
if (streamRef.current) {
|
|
streamRef.current.getTracks().forEach(track => track.stop());
|
|
streamRef.current = null;
|
|
}
|
|
};
|
|
|
|
recorder.onerror = (event) => {
|
|
console.error('MediaRecorder error:', event);
|
|
setError('Recording failed');
|
|
setRecording(false);
|
|
};
|
|
|
|
// Start recording
|
|
recorder.start();
|
|
setRecording(true);
|
|
console.log('Recording started');
|
|
} catch (err) {
|
|
console.error('Failed to start recording:', err);
|
|
setError(err.message || 'Failed to start recording');
|
|
setRecording(false);
|
|
}
|
|
}, []);
|
|
|
|
const stop = useCallback(() => {
|
|
console.log('Stop called, recorder state:', mediaRecorderRef.current?.state);
|
|
|
|
try {
|
|
if (mediaRecorderRef.current && mediaRecorderRef.current.state === 'recording') {
|
|
mediaRecorderRef.current.stop();
|
|
console.log('Recording stopped');
|
|
}
|
|
} catch (err) {
|
|
console.error('Error stopping recorder:', err);
|
|
}
|
|
|
|
// Always update state
|
|
setRecording(false);
|
|
|
|
// Clean up stream if still active
|
|
if (streamRef.current) {
|
|
streamRef.current.getTracks().forEach(track => track.stop());
|
|
streamRef.current = null;
|
|
}
|
|
}, []);
|
|
|
|
const reset = useCallback(() => {
|
|
setAudioBlob(null);
|
|
setError(null);
|
|
chunksRef.current = [];
|
|
}, []);
|
|
|
|
return {
|
|
isRecording,
|
|
audioBlob,
|
|
error,
|
|
start,
|
|
stop,
|
|
reset
|
|
};
|
|
} |