feat: OpenAI Cloud-TTS als optionale Vorlese-Engine - #10
Conversation
Ergaenzt das Vorlese-Fenster um einen optionalen OpenAI-Cloud-TTS-Anbieter neben dem lokalen Piper (Default). Synthese laeuft non-blocking in einem QThread mit 30s-Timeout; Wiedergabe ueber die bestehende paplay/aplay-Kette. - config: tts_provider (piper|openai), tts_openai_model, tts_openai_voice, tts_openai_consent inkl. Validierung + Sanitize - tts_window: CloudTtsService, _CloudTtsWorker, Anbieter-/Modell-UI, einmaliger Datenschutz-Dialog (persistente Zustimmung), secret-sichere Fehlertexte, robuster Thread-Abbruch mit referenzgehaltenem Detach (kein "QThread destroyed while running") - Tests: +14 (Speed-Mapping, Synthese, Consent-Gate, Secret-Scrub, Detach-Timeout-Pfad) - README: Anbieterwahl und neue Config-Felder dokumentiert Key kommt ausschliesslich aus OPENAI_API_KEY (secrets.env), nie aus config.json. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AHu5E1iQWEa6LViTcdo3o7
- Audio-Export transkribierter Texte als versandfreundliche Datei (OGG/Opus bzw. MP3) fuer Sprachnachrichten (WhatsApp o.Ae.) - Leichter Launch-Smoke-Test, damit CI den realen App-Start bestaetigt Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AHu5E1iQWEa6LViTcdo3o7
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c24bd669b8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| self._data["tts_openai_model"] = DEFAULTS["tts_openai_model"] | ||
| if self._data.get("tts_openai_voice") not in VALID_OPENAI_TTS_VOICES: | ||
| self._data["tts_openai_voice"] = DEFAULTS["tts_openai_voice"] | ||
| self._data["tts_openai_consent"] = bool(self._data.get("tts_openai_consent", False)) |
There was a problem hiding this comment.
Treat non-boolean TTS consent as false
When a hand-edited or migrated config contains a string such as "false" or "no", bool(...) converts it to True, so _ensure_openai_consent() will skip the privacy dialog and Cloud-TTS can send text to OpenAI without an explicit opt-in. Please only preserve the consent flag when the stored value is the literal JSON boolean true and reset all other types to False.
Useful? React with 👍 / 👎.
| def _on_cloud_finished(self, wav_path: str) -> None: | ||
| self._cleanup_cloud_state() | ||
| self._status_label.setText("Wiedergabe…") | ||
| program, args = _playback_command(wav_path) |
There was a problem hiding this comment.
Ignore stale cloud completions after Stop
If the user clicks Stop just after the worker emits finished but before this queued slot runs, _detach_cloud_thread() has already cleared the active cloud state, yet this handler still starts playback of the generated WAV. That makes Stop unreliable at the end of cloud synthesis; guard this slot against completions from a cancelled/non-current worker before switching to Wiedergabe….
Useful? React with 👍 / 👎.
…d leak, scrub_secret)
Ziel
Optionale Cloud-basierte Vorlese-Engine (OpenAI TTS) als Alternative zum lokalen Piper. Piper bleibt Default; Cloud-TTS ist opt-in und erst nach ausdrücklicher Datenschutz-Zustimmung aktiv.
Änderungen
app/config.py: drei nicht-geheime Felder + Validierung —tts_provider(piper|openai, Defaultpiper),tts_openai_model(Defaultgpt-4o-mini-tts),tts_openai_voice(Defaultmarin, Whitelist von 13 Stimmen),tts_openai_consent(bool, Defaultfalse).app/tts_window.py:CloudTtsService— Synthese viaopenai.audio.speech.create(..., timeout=30), Wiedergabe über die bestehende_playback_command()-Pipeline (paplay/aplay)._CloudTtsWorker(QThread) — nicht-blockierende Synthese; Status „Wiedergabe…" mit aktiven Pause/Stopp/Nochmal-Aktionen._ensure_openai_consent); bei Ablehnung Revert auf Piper._scrub_secret)._detached_cloud_threadsreferenziert und erst beifinishedvia_on_detached_thread_finished→deleteLaterfreigegeben (verhindert „QThread destroyed while running").README.md/ROADMAP.md: Doku zur Cloud-TTS-Option.Secret-Modell
Wie bei OpenRouter/OpenAI-LLM: nur der Env-Var-Name steht in
config.json; der Key liegt in~/.config/blitztext-linux/secrets.env(mode0600). Keine Secrets im Repo.Datenschutz
Cloud-TTS sendet den vorzulesenden Text an OpenAI. Aktivierung ausschließlich nach explizitem Consent-Dialog; Zustimmung wird persistiert, Ablehnung fällt auf Piper zurück.
Testplan
QT_QPA_PLATFORM=offscreen WHISPER_GUI_TESTS=1)openai-Paket / Thread-Detach bei hängendem Netzaufrufgpt-4o-mini-tts/ Stimmenova/ Tempo „Schnell", Playback + Pause/Stopp/Nochmal verifiziertBekannte Nachzieher (separat, nicht in diesem PR)
is_available()ignoriert fehlendesopenai-Paket (UI „bereit" irreführend);tts_openai_modelohne Whitelist; Install-Hint trennt Key-fehlt/Paket-fehlt nicht; OpenAI-Client-Init-Timeout. Sind als eigene Roadmap-Items erfasst.🤖 Generated with Claude Code