From bb53b2465587adb55c6298cecc043f6611ac754f Mon Sep 17 00:00:00 2001 From: Bruno Deanoz Date: Wed, 24 Jun 2026 01:33:23 +0200 Subject: [PATCH] fix: TTS read-aloud downloads audio via OkHttp before playing MediaPlayer.setDataSource(url) with HTTPS URLs can silently fail due to certificate issues, redirect handling, or listener race conditions (listeners were set after prepareAsync). Fix: download the WAV file via OkHttp (same client as all other network calls) to a temp file, set listeners before prepareAsync, play from local path. Temp file cleaned up on completion/error. --- .../java/dev/kaizen/app/chat/ChatScreen.kt | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt b/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt index 7790a00..4140867 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt @@ -370,26 +370,42 @@ fun ChatScreen( } fun playTtsUrl(url: String, messageId: Long) { - try { - val player = MediaPlayer().apply { - setDataSource(url) - prepareAsync() - setOnPreparedListener { start() } - setOnCompletionListener { - release() - ttsPlayerRef.value = null - playingTtsForId = null + scope.launch { + try { + val tmpFile = kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) { + val req = okhttp3.Request.Builder().url(url).build() + KaizenApi.imageClient.newCall(req).execute().use { resp -> + if (!resp.isSuccessful) return@withContext null + val bytes = resp.body!!.bytes() + java.io.File.createTempFile("tts_", ".wav", context.cacheDir).also { it.writeBytes(bytes) } + } } - setOnErrorListener { _, _, _ -> - release() - ttsPlayerRef.value = null + if (tmpFile == null) { playingTtsForId = null - true + return@launch } + val player = MediaPlayer().apply { + setOnPreparedListener { start() } + setOnCompletionListener { + release() + ttsPlayerRef.value = null + playingTtsForId = null + tmpFile.delete() + } + setOnErrorListener { _, _, _ -> + release() + ttsPlayerRef.value = null + playingTtsForId = null + tmpFile.delete() + true + } + setDataSource(tmpFile.absolutePath) + prepareAsync() + } + ttsPlayerRef.value = player + } catch (_: Exception) { + playingTtsForId = null } - ttsPlayerRef.value = player - } catch (_: Exception) { - playingTtsForId = null } }