"""Transfert de fichiers voix entre la tablette et Kazeia-central (adb). Py3.14-safe (pas de torch) : seulement des opérations adb. Le chemin des fichiers est **device-dépendant** → on part toujours de la colonne `wav_path` que le provider `/voices` renvoie (autorité), jamais d'un chemin codé en dur (cf. découverte du test : WAV sous `/data/local/tmp/kazeia/…` en dev, `Android/data/com.kazeia/…` en prod). """ from __future__ import annotations import posixpath from ..adb import Adb, AdbError # Le provider expose VOICE_WAV_DIR = "${MODELS_DIR}/../voix/voix" → ce marqueur # permet d'extraire MODELS_DIR depuis un wav_path, donc le dossier cosyvoice cible. _WAV_MARKER = "/../voix/voix/" def cosyvoice_dir_for(wav_path: str) -> str: """Dossier device des `.cvps` (`/cosyvoice`) dérivé d'un `wav_path`.""" if _WAV_MARKER in wav_path: models = wav_path.split(_WAV_MARKER)[0] return posixpath.join(models, "cosyvoice") # Fallback : …/kazeia/voix/voix/.wav → …/kazeia/models/cosyvoice voix_dir = posixpath.dirname(wav_path) kazeia = posixpath.dirname(posixpath.dirname(voix_dir)) return posixpath.join(kazeia, "models", "cosyvoice") def deployed_cvps(adb: Adb, serial: str, cosyvoice_dir: str) -> set[str]: """Ensemble des voice_id ayant un `.cvps` déjà déployé sur la tablette.""" out = adb.shell(f"ls {cosyvoice_dir} 2>/dev/null", serial=serial) return {line[:-5].strip() for line in out.splitlines() if line.strip().endswith(".cvps")} def pull_wav(adb: Adb, serial: str, wav_path: str, local_path: str) -> None: adb.pull(wav_path, local_path, serial=serial) def push_cvps(adb: Adb, serial: str, local_cvps: str, cosyvoice_dir: str, voice_id: str) -> str: """Pousse le `.cvps` dans le dossier cosyvoice de la tablette. Renvoie le chemin device. Crée le dossier au besoin.""" adb.shell(f"mkdir -p {cosyvoice_dir}", serial=serial) remote = posixpath.join(cosyvoice_dir, f"{voice_id}.cvps") adb.push(local_cvps, remote, serial=serial) return remote def delete_device_wav(adb: Adb, serial: str, wav_path: str) -> None: """Supprime le WAV d'origine de la tablette (après archivage côté PC). Vérifie le retour : `rm` adb échoue silencieusement (§9).""" out = adb.shell(f"rm -f {wav_path} && echo OK || echo FAIL", serial=serial) if "OK" not in out: raise AdbError(f"suppression WAV échouée: {wav_path} ({out.strip()})")