voice: harden recorder capture with mime fallback and chunked start
All checks were successful
CI / test (push) Successful in 26s

This commit is contained in:
2026-03-08 18:52:02 +03:00
parent 4d9b64973d
commit 0db741cb8e
2 changed files with 30 additions and 5 deletions

View File

@@ -59,6 +59,24 @@ function saveFavorites(key: string, values: Set<string>): void {
}
}
function pickSupportedAudioMimeType(): string | undefined {
if (typeof MediaRecorder === "undefined" || typeof MediaRecorder.isTypeSupported !== "function") {
return undefined;
}
const candidates = [
"audio/webm;codecs=opus",
"audio/webm",
"audio/mp4",
"audio/ogg;codecs=opus",
];
for (const mime of candidates) {
if (MediaRecorder.isTypeSupported(mime)) {
return mime;
}
}
return undefined;
}
export function MessageComposer() {
const activeChatId = useChatStore((s) => s.activeChatId);
const chats = useChatStore((s) => s.chats);
@@ -562,7 +580,8 @@ export function MessageComposer() {
}
}
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const recorder = new MediaRecorder(stream);
const mimeType = pickSupportedAudioMimeType();
const recorder = mimeType ? new MediaRecorder(stream, { mimeType }) : new MediaRecorder(stream);
recordingStreamRef.current = stream;
chunksRef.current = [];
sendVoiceOnStopRef.current = true;
@@ -584,13 +603,19 @@ export function MessageComposer() {
setUploadError("Voice message is too short. Minimum length is 1 second.");
return;
}
const blob = new Blob(data, { type: "audio/webm" });
const file = new File([blob], `voice-${Date.now()}.webm`, { type: "audio/webm" });
const outputMime = recorder.mimeType || mimeType || "audio/webm";
const blob = new Blob(data, { type: outputMime });
if (blob.size < 512) {
setUploadError("Voice message is empty. Please try recording again.");
return;
}
const ext = outputMime.includes("ogg") ? "ogg" : outputMime.includes("mp4") ? "m4a" : "webm";
const file = new File([blob], `voice-${Date.now()}.${ext}`, { type: outputMime });
const waveform = await buildWaveformPoints(blob, 64);
await handleUpload(file, "voice", waveform);
};
recorderRef.current = recorder;
recorder.start();
recorder.start(250);
recordingStartedAtRef.current = Date.now();
setRecordSeconds(0);
setRecordingState("recording");