fix(voice): robust microphone fallback for WebKitGTK "Invalid constraint"
All checks were successful
Build AppImage / build (push) Has been skipped

WebKitGTK throws various error types (TypeError, OverconstrainedError,
etc.) when getUserMedia is called. Instead of only catching specific
error names, now ALL errors trigger device-enumeration fallback chain:
1. { audio: true }
2. Explicit deviceId from enumerateDevices()
3. { audio: {} } as last resort

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eddy 2026-04-20 22:17:52 +02:00
parent 5d72b85bf4
commit e0653ddcab
2 changed files with 30 additions and 15 deletions

View file

@ -261,28 +261,29 @@
try { try {
stream = await navigator.mediaDevices.getUserMedia({ audio: true }); stream = await navigator.mediaDevices.getUserMedia({ audio: true });
} catch (initialErr) { } catch (initialErr) {
// OverconstrainedError oder NotFoundError — auf WebKitGTK/Tauri kann // WebKitGTK wirft verschiedene Fehler: OverconstrainedError, NotFoundError,
// selbst { audio: true } fehlschlagen. Versuche explizites Device. // TypeError("Invalid constraint"), etc. Bei JEDEM Fehler Fallback versuchen.
if (initialErr instanceof DOMException && console.warn('getUserMedia({audio:true}) fehlgeschlagen, versuche Fallback:', initialErr);
(initialErr.name === 'OverconstrainedError' || initialErr.name === 'NotFoundError')) {
console.warn('getUserMedia fehlgeschlagen, versuche Device-Enumeration:', initialErr);
try {
const devices = await navigator.mediaDevices.enumerateDevices(); const devices = await navigator.mediaDevices.enumerateDevices();
const audioInput = devices.find(d => d.kind === 'audioinput'); const audioInput = devices.find(d => d.kind === 'audioinput');
if (audioInput) { if (audioInput) {
console.log('Audio-Device gefunden:', audioInput.label || audioInput.deviceId); console.log('Audio-Device gefunden:', audioInput.label || audioInput.deviceId);
stream = await navigator.mediaDevices.getUserMedia({ stream = await navigator.mediaDevices.getUserMedia({
audio: { deviceId: audioInput.deviceId } audio: { deviceId: { exact: audioInput.deviceId } }
}); });
} else { } else {
throw new Error( // Letzter Versuch: komplett ohne Constraints
'Kein Mikrofon gefunden. Unter WebKitGTK (Tauri/Linux) wird ' + stream = await navigator.mediaDevices.getUserMedia({ audio: {} });
'PipeWire oder PulseAudio mit gst-plugin-pipewire benötigt.'
);
} }
} else { } catch (fallbackErr) {
throw initialErr; throw new Error(
'Kein Mikrofon verfügbar. Unter WebKitGTK (Tauri/Linux) wird ' +
'PipeWire oder PulseAudio mit gst-plugin-pipewire benötigt. ' +
`Original: ${initialErr instanceof Error ? initialErr.message : initialErr}`
);
} }
} }

View file

@ -77,10 +77,24 @@
}); });
console.log('🎤 Mikrofon mit optimalen Constraints geöffnet'); console.log('🎤 Mikrofon mit optimalen Constraints geöffnet');
} catch (constraintErr) { } catch (constraintErr) {
console.warn('Mikrofon-Constraints fehlgeschlagen, versuche Fallback:', constraintErr); // WebKitGTK wirft diverse Fehler (OverconstrainedError, TypeError "Invalid constraint", etc.)
mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true }); console.warn('Mikrofon-Constraints fehlgeschlagen, versuche Fallbacks:', constraintErr);
try {
mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
} catch (_) {
// Auch { audio: true } fehlgeschlagen — versuche explizites Device
const devices = await navigator.mediaDevices.enumerateDevices();
const audioInput = devices.find(d => d.kind === 'audioinput');
if (audioInput) {
mediaStream = await navigator.mediaDevices.getUserMedia({
audio: { deviceId: { exact: audioInput.deviceId } }
});
} else {
mediaStream = await navigator.mediaDevices.getUserMedia({ audio: {} });
}
}
usedFallback = true; usedFallback = true;
console.log('🎤 Mikrofon mit Fallback { audio: true } geöffnet'); console.log('🎤 Mikrofon mit Fallback geöffnet');
} }
// Audio-Kontext für Visualisierung // Audio-Kontext für Visualisierung