fix: Mikrofon hängt — PipeWire-Fallback + getUserMedia Timeout [appimage]
All checks were successful
Build AppImage / build (push) Successful in 8m22s
All checks were successful
Build AppImage / build (push) Successful in 8m22s
Wenn PipeWire nicht läuft (z.B. PulseAudio/xRDP), wird pipewiresrc auf Rank 0 gesetzt damit WebKitGTK auf pulsesrc zurückfällt. getUserMedia bekommt 5s Timeout statt endlos zu hängen. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a01959a55c
commit
6e1a3c41f2
2 changed files with 38 additions and 6 deletions
|
|
@ -120,6 +120,12 @@ pkgs.stdenv.mkDerivation {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# GStreamer/PipeWire: Wenn PipeWire NICHT laeuft, pipewiresrc runterranken
|
||||||
|
# damit WebKitGTK auf pulsesrc zurueckfaellt (sonst haengt getUserMedia endlos)
|
||||||
|
if ! pgrep -x pipewire >/dev/null 2>&1; then
|
||||||
|
export GST_PLUGIN_FEATURE_RANK="pipewiresrc:0,pipewiresink:0"
|
||||||
|
fi
|
||||||
|
|
||||||
exec "$BIN" "$@"
|
exec "$BIN" "$@"
|
||||||
LAUNCHER
|
LAUNCHER
|
||||||
chmod +x $out/bin/claude-desktop
|
chmod +x $out/bin/claude-desktop
|
||||||
|
|
|
||||||
|
|
@ -366,15 +366,39 @@
|
||||||
|
|
||||||
// ============ Voice Interface ============
|
// ============ Voice Interface ============
|
||||||
|
|
||||||
|
// getUserMedia mit Timeout — WebKitGTK hängt manchmal endlos statt zu fehlern
|
||||||
|
function getUserMediaWithTimeout(constraints: MediaStreamConstraints, timeoutMs = 5000): Promise<MediaStream> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
reject(new Error(`getUserMedia Timeout nach ${timeoutMs}ms — GStreamer-Pipeline hängt`));
|
||||||
|
}, timeoutMs);
|
||||||
|
|
||||||
|
navigator.mediaDevices.getUserMedia(constraints)
|
||||||
|
.then((stream) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
resolve(stream);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function startRecording() {
|
async function startRecording() {
|
||||||
try {
|
try {
|
||||||
let stream: MediaStream;
|
let stream: MediaStream;
|
||||||
|
|
||||||
|
// Prüfe zuerst ob mediaDevices überhaupt verfügbar ist
|
||||||
|
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
||||||
|
throw new Error('MediaDevices API nicht verfügbar — WebKitGTK braucht GStreamer + PipeWire');
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
stream = await getUserMediaWithTimeout({ audio: true });
|
||||||
} catch (initialErr) {
|
} catch (initialErr) {
|
||||||
// WebKitGTK wirft verschiedene Fehler: OverconstrainedError, NotFoundError,
|
// WebKitGTK wirft verschiedene Fehler: OverconstrainedError, NotFoundError,
|
||||||
// TypeError("Invalid constraint"), etc. Bei JEDEM Fehler Fallback versuchen.
|
// TypeError("Invalid constraint"), Timeout. Bei JEDEM Fehler Fallback versuchen.
|
||||||
console.warn('getUserMedia({audio:true}) fehlgeschlagen, versuche Fallback:', initialErr);
|
console.warn('getUserMedia({audio:true}) fehlgeschlagen, versuche Fallback:', initialErr);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -383,17 +407,19 @@
|
||||||
|
|
||||||
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 getUserMediaWithTimeout({
|
||||||
audio: { deviceId: { exact: audioInput.deviceId } }
|
audio: { deviceId: { exact: audioInput.deviceId } }
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Letzter Versuch: komplett ohne Constraints
|
// Letzter Versuch: komplett ohne Constraints
|
||||||
stream = await navigator.mediaDevices.getUserMedia({ audio: {} });
|
stream = await getUserMediaWithTimeout({ audio: {} });
|
||||||
}
|
}
|
||||||
} catch (fallbackErr) {
|
} catch (fallbackErr) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Kein Mikrofon verfügbar. Unter WebKitGTK (Tauri/Linux) wird ' +
|
'Mikrofon-Zugriff fehlgeschlagen. Mögliche Ursachen:\n' +
|
||||||
'PipeWire oder PulseAudio mit gst-plugin-pipewire benötigt. ' +
|
'• GStreamer-Plugins fehlen (gst-plugin-pipewire)\n' +
|
||||||
|
'• PipeWire läuft nicht\n' +
|
||||||
|
'• App wurde nicht über den Nix-Wrapper gestartet\n' +
|
||||||
`Original: ${initialErr instanceof Error ? initialErr.message : initialErr}`
|
`Original: ${initialErr instanceof Error ? initialErr.message : initialErr}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue