fix(playback): surface stream errors as toasts, sandbox-aware for ALSA

The player has recorded a user-readable message on stream-open failure
since the Tauri era, but the drain lived in Tauri's polling loop and
was never ported to the Slint poll loop — record_stream_error had ZERO
consumers, so ALSA Direct/exclusive failures showed as a bar frozen at
0:00 with no explanation (#508, #534, #500). Drain the message each
tick and toast it. When running inside Flatpak/Snap and the failure is
ALSA-shaped, explain that direct hw: access is blocked by the sandbox
and point at PipeWire/System Default instead of showing the raw error.
2 new msgids translated in all 7 locales.

cc
This commit is contained in:
vicrodh committed 2026-07-05 12:12:10 -06:00
1 parent ceb25cd1d1
commit f83eadb398
8 files changed
+67

No files matched your search

@@ -4364,3 +4364,9 @@ msgstr "Renderer-Einstellung auf Automatisch zurückgesetzt — der letzte Start
msgid "Renderer switched to GPU (compatibility) — the GPU renderer failed to start"
msgstr "Renderer auf GPU (Kompatibilität) umgestellt — der GPU-Renderer konnte nicht starten"
msgid "Audio output error"
msgstr "Audioausgabefehler"
msgid "Direct ALSA device access is blocked inside Flatpak/Snap — switch the audio backend to PipeWire or System Default"
msgstr "Direkter ALSA-Gerätezugriff ist in Flatpak/Snap blockiert — Audio-Backend auf PipeWire oder Systemstandard umstellen"
@@ -103,3 +103,9 @@ msgstr "Renderer setting reverted to Auto — the previous start didn't finish"
msgid "Renderer switched to GPU (compatibility) — the GPU renderer failed to start"
msgstr "Renderer switched to GPU (compatibility) — the GPU renderer failed to start"
msgid "Audio output error"
msgstr "Audio output error"
msgid "Direct ALSA device access is blocked inside Flatpak/Snap — switch the audio backend to PipeWire or System Default"
msgstr "Direct ALSA device access is blocked inside Flatpak/Snap — switch the audio backend to PipeWire or System Default"
@@ -4394,3 +4394,9 @@ msgstr "El ajuste de renderizador volvió a Automático — el arranque anterior
msgid "Renderer switched to GPU (compatibility) — the GPU renderer failed to start"
msgstr "El renderizador cambió a GPU (compatibilidad) — el renderizador GPU no pudo iniciar"
msgid "Audio output error"
msgstr "Error de salida de audio"
msgid "Direct ALSA device access is blocked inside Flatpak/Snap — switch the audio backend to PipeWire or System Default"
msgstr "El acceso directo a dispositivos ALSA está bloqueado dentro de Flatpak/Snap — cambia el backend de audio a PipeWire o al predeterminado del sistema"
@@ -4364,3 +4364,9 @@ msgstr "Réglage du moteur de rendu revenu sur Automatique — le démarrage pr
msgid "Renderer switched to GPU (compatibility) — the GPU renderer failed to start"
msgstr "Moteur de rendu basculé sur GPU (compatibilité) — le rendu GPU n'a pas pu démarrer"
msgid "Audio output error"
msgstr "Erreur de sortie audio"
msgid "Direct ALSA device access is blocked inside Flatpak/Snap — switch the audio backend to PipeWire or System Default"
msgstr "L'accès direct aux périphériques ALSA est bloqué dans Flatpak/Snap — passez le backend audio sur PipeWire ou la sortie système"
@@ -4353,3 +4353,9 @@ msgstr "レンダラー設定を自動に戻しました — 前回の起動が
msgid "Renderer switched to GPU (compatibility) — the GPU renderer failed to start"
msgstr "レンダラーをGPU(互換)に切り替えました — GPUレンダラーを起動できませんでした"
msgid "Audio output error"
msgstr "オーディオ出力エラー"
msgid "Direct ALSA device access is blocked inside Flatpak/Snap — switch the audio backend to PipeWire or System Default"
msgstr "Flatpak/Snap 内では ALSA デバイスへの直接アクセスがブロックされます — オーディオバックエンドを PipeWire またはシステム既定に切り替えてください"
@@ -4364,3 +4364,9 @@ msgstr "Configuração do renderizador revertida para Automático — a iniciali
msgid "Renderer switched to GPU (compatibility) — the GPU renderer failed to start"
msgstr "Renderizador alterado para GPU (compatibilidade) — o renderizador GPU não conseguiu iniciar"
msgid "Audio output error"
msgstr "Erro de saída de áudio"
msgid "Direct ALSA device access is blocked inside Flatpak/Snap — switch the audio backend to PipeWire or System Default"
msgstr "O acesso direto a dispositivos ALSA está bloqueado no Flatpak/Snap — mude o backend de áudio para PipeWire ou o padrão do sistema"
@@ -4377,3 +4377,9 @@ msgstr "Настройка рендерера возвращена на «Авт
msgid "Renderer switched to GPU (compatibility) — the GPU renderer failed to start"
msgstr "Рендерер переключён на GPU (совместимость) — GPU-рендерер не смог запуститься"
msgid "Audio output error"
msgstr "Ошибка аудиовывода"
msgid "Direct ALSA device access is blocked inside Flatpak/Snap — switch the audio backend to PipeWire or System Default"
msgstr "Прямой доступ к устройствам ALSA заблокирован в Flatpak/Snap — переключите аудиобэкенд на PipeWire или системный вывод"
+25
View File
@@ -3833,6 +3833,31 @@ pub fn start_poll_loop(
last_remote_ui_push = None;
}
// Surface audio-stream failures as a toast (#508/#534/#500): the
// player records a user-readable message when a stream fails to
// open, but the drain lived in Tauri's polling loop and was never
// ported — ALSA Direct failures left the bar frozen at 0:00 with
// no explanation. take_stream_error_message() drains exactly once
// per recorded error, so each failed attempt toasts once. Inside a
// Flatpak/Snap sandbox direct hw: access is blocked by design —
// when the failure looks ALSA-shaped, say that instead of the raw
// error.
if let Some(msg) = runtime.core().player().take_stream_error_message() {
let sandboxed = !matches!(
qbz_audio::health::detect_sandbox(),
qbz_audio::health::Sandbox::None
);
let lower = msg.to_lowercase();
let text = if sandboxed && (lower.contains("alsa") || lower.contains("hw:")) {
qbz_i18n::t(
"Direct ALSA device access is blocked inside Flatpak/Snap — switch the audio backend to PipeWire or System Default",
)
} else {
format!("{}: {}", qbz_i18n::t("Audio output error"), msg)
};
crate::toast::error_weak(&weak, text);
}
// --- QConnect CONTROLLER mode: peer-state reflection ----------
// When QBZ is CONTROLLING a peer renderer, the event sink stops the
// LOCAL player, so `get_playback_event()` reports track_id == 0 / not