fix(web): robuster Mikrofon-Pfad fuer iOS-Safari + mp4-Format
- getUserMedia/MediaRecorder-Support pruefen, klare Fehlermeldung mit error.name (In-App-Browser/Privat-Modus/Permission) statt pauschalem "HTTPS noetig?" - Safari liefert audio/mp4 (nicht webm): tatsaechlichen mimeType verwenden und als format mitschicken; Server (faster-whisper) erkennt das Format am Inhalt (mp4/AAC verifiziert). Asset-Version v7 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
7ebba4e755
commit
d87ebdf457
2 changed files with 26 additions and 8 deletions
|
|
@ -201,21 +201,39 @@ let recChunks = [];
|
|||
|
||||
async function startRecording() {
|
||||
if (busy) return;
|
||||
const md = navigator.mediaDevices;
|
||||
if (!md || !md.getUserMedia) {
|
||||
addMessage("system", "Mikrofon nicht freigegeben: Bitte die Seite direkt in Safari öffnen (nicht im In-App-Browser) und keinen Privat-Modus verwenden.");
|
||||
return;
|
||||
}
|
||||
if (typeof MediaRecorder === "undefined") {
|
||||
addMessage("system", "Aufnahme nicht unterstützt (iOS < 14.3?). Bitte iOS aktualisieren.");
|
||||
return;
|
||||
}
|
||||
let stream;
|
||||
try {
|
||||
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
stream = await md.getUserMedia({ audio: true });
|
||||
} catch (e) {
|
||||
addMessage("system", "Mikrofon nicht verfuegbar (HTTPS noetig?). " + e.message);
|
||||
addMessage("system", "Mikrofon-Zugriff fehlgeschlagen: " + (e.name || "") +
|
||||
". Tipp: iPhone → Einstellungen → Safari → Mikrofon = Erlauben/Fragen.");
|
||||
return;
|
||||
}
|
||||
recChunks = [];
|
||||
mediaRecorder = new MediaRecorder(stream);
|
||||
try {
|
||||
mediaRecorder = new MediaRecorder(stream);
|
||||
} catch (e) {
|
||||
stream.getTracks().forEach((t) => t.stop());
|
||||
addMessage("system", "Aufnahme nicht möglich: " + (e.name || e.message));
|
||||
return;
|
||||
}
|
||||
mediaRecorder.ondataavailable = (e) => { if (e.data.size) recChunks.push(e.data); };
|
||||
mediaRecorder.onstop = async () => {
|
||||
stream.getTracks().forEach((t) => t.stop());
|
||||
const blob = new Blob(recChunks, { type: mediaRecorder.mimeType || "audio/webm" });
|
||||
const mime = mediaRecorder.mimeType || "audio/mp4";
|
||||
const blob = new Blob(recChunks, { type: mime });
|
||||
const bytes = await blob.arrayBuffer();
|
||||
sendVoice(bytes);
|
||||
// Safari -> audio/mp4, andere -> webm; der Server erkennt das Format am Inhalt.
|
||||
sendVoice(bytes, mime.includes("mp4") || mime.includes("mpeg") ? "mp4" : "webm");
|
||||
};
|
||||
mediaRecorder.start();
|
||||
micBtn.classList.remove("bg-emerald-600");
|
||||
|
|
@ -229,13 +247,13 @@ function stopRecording() {
|
|||
micBtn.classList.add("bg-emerald-600");
|
||||
}
|
||||
|
||||
async function sendVoice(bytes) {
|
||||
async function sendVoice(bytes, fmt = "webm") {
|
||||
if (busy) return;
|
||||
busy = true;
|
||||
await ensureSession();
|
||||
statusEl.textContent = "verarbeite Sprache …";
|
||||
await runTurn("/ws/voice", (ws) => {
|
||||
ws.send(JSON.stringify({ type: "start", format: "webm", stream: true, audio_stream: true, ...overrides() }));
|
||||
ws.send(JSON.stringify({ type: "start", format: fmt, stream: true, audio_stream: true, ...overrides() }));
|
||||
ws.send(bytes);
|
||||
ws.send(JSON.stringify({ type: "end" }));
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue