Podcast player: say why it cannot speak, with a way out

"The podcasts don't even work on my Linux setup on Firefox." Firefox on
Linux speaks through speech-dispatcher; without it speechSynthesis is present
but empty and silent - getVoices() is empty, speak() does nothing, no events
- so the player looked broken.

- pressing play with zero installed voices (or a start that never arrives
  within 1.7s) raises a banner naming the cause, with the Linux fix
  (sudo apt install speech-dispatcher) and the iOS one where it applies;
- the banner offers "Use the natural voice" when a provider key exists (it
  switches the source, keeps playing and remembers it), "Add a provider key"
  when not (opens Settings) and a link into the picker;
- natural-voice errors (refused autoplay, provider refusal) show in that same
  banner instead of hiding inside the picker.

podcast-test fakes a present-but-empty speechSynthesis and pins the banner,
the Linux line and the one-tap switch. 48 checks. Version 0.7.8, SW v100.
This commit is contained in:
Martin Dvořák 2026-10-16 18:32:36 +00:00
commit 08a145b087
6 changed files with 147 additions and 8 deletions

View file

@ -1492,6 +1492,20 @@ Things I chose while building, with the reasoning:
into view as you type. No strings, so no i18n; the additive change sits on top of the merged
scene-heat rework unchanged.
106. **When the browser has no speech engine at all.** "The podcasts don't even work on my Linux
setup on Firefox": Firefox on Linux speaks through speech-dispatcher, and without it
`speechSynthesis` is *present* (so `speechAvailable()` says yes) but empty and silent —
`getVoices()` returns nothing and `speak()` does nothing, with no events. The player used to
sit there looking broken. Now the failure is named and has a way out: pressing play with zero
installed voices (or a start that never arrives within 1.7 s) raises a banner that says what is
missing — *"On Firefox/Linux install speech-dispatcher (sudo apt install speech-dispatcher) and
reopen this page"* — with **Use the natural voice** right there when a provider key exists (it
switches the source, starts playing and remembers the choice), **Add a provider key** when not
(it opens Settings), and a link into the voice picker. Natural-voice errors (a refused
autoplay, a provider refusal) are surfaced in the same place instead of hiding inside the
picker. The suite fakes a present-but-empty `speechSynthesis` and pins the banner, the Linux
line and the one-tap switch (48 checks).
105. **The play button that only worked once.** "It only plays once, and when you pause it you can't
unpause." WebKit's quirk, faithfully: `speechSynthesis.cancel()` leaves the synthesis **paused**,
and every later `speak()` is queued into that paused state — silently. Three fixes in the