fix: preserve recorder blob without object-url refetch
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 35s
All checks were successful
Build & Deploy to K3s / build-and-deploy (push) Successful in 35s
This commit is contained in:
parent
6dd7d2c4e5
commit
800214976f
@ -9,12 +9,12 @@ import { useState, useCallback, useEffect, useRef } from "react";
|
||||
export function useAudioRecorder() {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isRecording, setIsRecording] = useState(false);
|
||||
const [recordings, setRecordings] = useState<string[]>([]);
|
||||
const [currentRecording, setCurrentRecording] = useState<string | null>(null);
|
||||
const [currentRecording, setCurrentRecording] = useState<Blob | null>(null);
|
||||
|
||||
const mediaRecorderRef = useRef<MediaRecorder | null>(null);
|
||||
const chunksRef = useRef<Blob[]>([]);
|
||||
const streamRef = useRef<MediaStream | null>(null);
|
||||
const stopResolverRef = useRef<((value: { blob: Blob }) => void) | null>(null);
|
||||
|
||||
/** Choisit le meilleur mimeType supporté par le navigateur. */
|
||||
const getMimeType = useCallback(() => {
|
||||
@ -54,7 +54,6 @@ export function useAudioRecorder() {
|
||||
const mimeType = getMimeType();
|
||||
const recorder = new MediaRecorder(stream, {
|
||||
mimeType: mimeType || undefined,
|
||||
audioBitsPerSecond: 128000,
|
||||
});
|
||||
|
||||
recorder.ondataavailable = (e) => {
|
||||
@ -66,10 +65,11 @@ export function useAudioRecorder() {
|
||||
recorder.onstop = () => {
|
||||
const actualMime = recorder.mimeType || mimeType || "audio/webm";
|
||||
const blob = new Blob(chunksRef.current, { type: actualMime });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
setRecordings((prev) => [...prev, url]);
|
||||
setCurrentRecording(url);
|
||||
setCurrentRecording(blob);
|
||||
stopResolverRef.current?.({ blob });
|
||||
stopResolverRef.current = null;
|
||||
mediaRecorderRef.current = null;
|
||||
chunksRef.current = [];
|
||||
|
||||
// Arrête les pistes micro
|
||||
stream.getTracks().forEach((t) => t.stop());
|
||||
@ -92,26 +92,15 @@ export function useAudioRecorder() {
|
||||
if (!isRecording || !mediaRecorderRef.current) return;
|
||||
|
||||
setIsLoading(true);
|
||||
return new Promise<{ blob: Blob; url: string }>((resolve) => {
|
||||
return new Promise<{ blob: Blob }>((resolve) => {
|
||||
const recorder = mediaRecorderRef.current!;
|
||||
const mimeType = recorder.mimeType || getMimeType() || "audio/webm";
|
||||
const ext = getExtension(mimeType);
|
||||
|
||||
const originalOnStop = recorder.onstop;
|
||||
recorder.onstop = (ev) => {
|
||||
// Appelle le handler de base (qui crée le blob + currentRecording)
|
||||
if (originalOnStop) originalOnStop.call(recorder, ev);
|
||||
|
||||
const blob = new Blob(chunksRef.current, { type: mimeType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
stopResolverRef.current = resolve;
|
||||
recorder.requestData();
|
||||
recorder.stop();
|
||||
setIsRecording(false);
|
||||
setIsLoading(false);
|
||||
resolve({ blob, url });
|
||||
};
|
||||
|
||||
recorder.stop();
|
||||
});
|
||||
}, [isRecording, getMimeType, getExtension]);
|
||||
}, [isRecording]);
|
||||
|
||||
const toggleRecording = useCallback(async () => {
|
||||
if (isRecording) {
|
||||
@ -122,25 +111,21 @@ export function useAudioRecorder() {
|
||||
}, [isRecording, startRecording, stopRecording]);
|
||||
|
||||
const clearRecordings = useCallback(() => {
|
||||
recordings.forEach((url) => URL.revokeObjectURL(url));
|
||||
setRecordings([]);
|
||||
setCurrentRecording(null);
|
||||
}, [recordings]);
|
||||
}, []);
|
||||
|
||||
// Cleanup au démontage
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
recordings.forEach((url) => URL.revokeObjectURL(url));
|
||||
if (streamRef.current) {
|
||||
streamRef.current.getTracks().forEach((t) => t.stop());
|
||||
}
|
||||
};
|
||||
}, [recordings]);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
isRecording,
|
||||
recordings,
|
||||
currentRecording,
|
||||
startRecording,
|
||||
stopRecording,
|
||||
|
||||
@ -109,20 +109,16 @@ export default function RecipeForm() {
|
||||
return () => clearInterval(id)
|
||||
}, [pageState])
|
||||
|
||||
// Convertit l'enregistrement en File
|
||||
// Convertit l'enregistrement en File sans repasser par une blob URL,
|
||||
// pour éviter toute altération ou perte lors du fetch() local.
|
||||
useEffect(() => {
|
||||
if (!currentRecording) return
|
||||
fetch(currentRecording)
|
||||
.then((res) => res.blob())
|
||||
.then((blob) => {
|
||||
// Détecte le format réel du blob (webm, mp4, ogg…)
|
||||
const mime = blob.type || "audio/webm"
|
||||
const mime = currentRecording.type || "audio/webm"
|
||||
const ext = mime.includes("mp4") ? "m4a" : mime.includes("ogg") ? "ogg" : "webm"
|
||||
const file = new File([blob], `recording.${ext}`, { type: mime })
|
||||
const file = new File([currentRecording], `recording.${ext}`, { type: mime })
|
||||
setAudioFile(file)
|
||||
setPageState("review")
|
||||
setError("")
|
||||
})
|
||||
}, [currentRecording])
|
||||
|
||||
// Timer
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user