From 8bcf9f8ebe627c9ef829313788e4c15d31a17d6f Mon Sep 17 00:00:00 2001 From: Kazeia Team Date: Sun, 21 Jun 2026 08:51:56 +0200 Subject: [PATCH] =?UTF-8?q?fix(voices):=20/voices=20refl=C3=A8te=20l'inven?= =?UTF-8?q?taire=20CosyVoice=20(.cvps),=20pas=20les=20vestiges=20Qwen3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit voicesCursor() marquait une voix « ready » dès que ses embeddings Qwen3 (_voice_prefix/suffix.bin) existaient — des vestiges morts depuis la bascule CosyVoice → des voix fantômes « prête » sans aucun .cvps (incident zelda). Désormais : union (WAV = « à convertir ») ∪ (cosyvoice/*.cvps = « prête ») ; les .bin Qwen3 ne sont plus comptés ; nouvelle colonne cvps_exists (additive). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../telemetry/KazeiaTelemetryProvider.kt | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/kazeia-android/app/src/main/java/com/kazeia/telemetry/KazeiaTelemetryProvider.kt b/kazeia-android/app/src/main/java/com/kazeia/telemetry/KazeiaTelemetryProvider.kt index 0f0203a..49dff1c 100644 --- a/kazeia-android/app/src/main/java/com/kazeia/telemetry/KazeiaTelemetryProvider.kt +++ b/kazeia-android/app/src/main/java/com/kazeia/telemetry/KazeiaTelemetryProvider.kt @@ -79,6 +79,11 @@ class KazeiaTelemetryProvider : ContentProvider() { get() = "${com.kazeia.KazeiaApplication.MODELS_DIR}/qwen3-tts-npu" private const val PREFIX_SUFFIX = "_voice_prefix.bin" private const val SUFFIX_SUFFIX = "_voice_suffix.bin" + // Voix CosyVoice (prod) : artefacts `.cvps` enrôlés. C'est l'inventaire réel + // depuis la bascule ; les `_voice_*.bin` ci-dessus sont des vestiges Qwen3. + private val VOICE_CVPS_DIR: String + get() = "${com.kazeia.KazeiaApplication.MODELS_DIR}/cosyvoice" + private const val CVPS_SUFFIX = ".cvps" } private val matcher = UriMatcher(UriMatcher.NO_MATCH).apply { @@ -526,26 +531,32 @@ class KazeiaTelemetryProvider : ContentProvider() { private fun voicesCursor(): Cursor { val cols = arrayOf( "id", "name", "state", - "wav_exists", "prefix_exists", "suffix_exists", + "wav_exists", "prefix_exists", "suffix_exists", "cvps_exists", "wav_size_bytes", "wav_duration_seconds", "wav_path", "created_at" ) val cursor = MatrixCursor(cols) - // Set des ids = union (wav basenames) ∪ (prefix bin basenames) + // Inventaire = réalité CosyVoice : union (WAV = « à convertir ») ∪ + // (`.cvps` = « prête »). Les embeddings Qwen3 (`_voice_*.bin`) NE sont plus + // comptés dans l'union : vestiges morts depuis la bascule, ils faisaient + // apparaître des voix « prête » fantômes sans aucun `.cvps`. val wavDir = File(VOICE_WAV_DIR) - val embDir = File(VOICE_EMB_DIR) + val cvpsDir = File(VOICE_CVPS_DIR) val wavIds = (wavDir.listFiles { f -> f.name.endsWith(".wav") } ?: emptyArray()) .associateBy { it.nameWithoutExtension.lowercase() } - val prefixIds = (embDir.listFiles { f -> f.name.endsWith(PREFIX_SUFFIX) } ?: emptyArray()) - .associateBy { it.name.removeSuffix(PREFIX_SUFFIX).lowercase() } - val ids = (wavIds.keys + prefixIds.keys).sorted() + val cvpsIds = (cvpsDir.listFiles { f -> f.name.endsWith(CVPS_SUFFIX) } ?: emptyArray()) + .associateBy { it.name.removeSuffix(CVPS_SUFFIX).lowercase() } + val ids = (wavIds.keys + cvpsIds.keys).sorted() for (id in ids) { val wav = wavIds[id] + val cvps = cvpsIds[id] val prefix = File(VOICE_EMB_DIR, "${id}$PREFIX_SUFFIX") val suffix = File(VOICE_EMB_DIR, "${id}$SUFFIX_SUFFIX") + // « ready » = `.cvps` déployé (jouable par CosyVoice) ; « recorded » = + // WAV seul (à convertir/enrôler par Kazeia-central) ; sinon « error ». val state = when { - prefix.exists() && suffix.exists() -> "ready" + cvps != null -> "ready" wav != null -> "recorded" else -> "error" } @@ -557,10 +568,11 @@ class KazeiaTelemetryProvider : ContentProvider() { if (wav != null) 1 else 0, if (prefix.exists()) 1 else 0, if (suffix.exists()) 1 else 0, + if (cvps != null) 1 else 0, wav?.length() ?: 0L, durSec, wav?.absolutePath ?: "", - wav?.lastModified() ?: prefix.lastModified() + wav?.lastModified() ?: cvps?.lastModified() ?: 0L )) } return cursor