03-oct: superguardado automático

This commit is contained in:
minguezsanzjuanjose
2026-10-03 19:56:53 +02:00
parent 5446db6a2b
commit 34806538d5

108
scripts/voz_mensaje.py Normal file
View File

@@ -0,0 +1,108 @@
#!/usr/bin/env python3
"""voz_mensaje.py — Enrutador de voz para los mensajes del asistente (OpenCode).
Genera y reproduce un mensaje hablado con VoiceStudio (http://127.0.0.1:3900)
eligiendo el perfil de voz segun el TIPO de mensaje:
default -> Carolina (10466680) Mensajes trascendentes de OpenCode (por defecto)
biblioteca -> Juanjo (4f18d2df) Mensajes de la biblioteca Prolongo
critico -> Abuelo (9f83c527) Mensajes importantes / criticos
Uso:
python voz_mensaje.py "texto a decir" [--tipo default|biblioteca|critico]
[--idioma es] [--no-play]
Proyecto: INFRAESTRUCTURA. Perfiles de voz locales de VoiceStudio.
"""
from __future__ import annotations
import argparse
import json
import os
import sys
import urllib.error
import urllib.parse
import urllib.request
BASE = os.environ.get("VOICESTUDIO_BASE", "http://127.0.0.1:3900")
# tipo -> (profile_id, nombre)
PROFILES = {
"default": ("10466680", "Carolina"),
"biblioteca": ("4f18d2df", "Juanjo"),
"critico": ("9f83c527", "Abuelo"),
}
def _get_profile(pid: str) -> dict:
"""Metadatos del perfil (para reutilizar su ref_text al generar)."""
try:
with urllib.request.urlopen(f"{BASE}/profiles/{pid}", timeout=30) as r:
return json.loads(r.read())
except Exception:
return {}
def generar(texto: str, tipo: str = "default", idioma: str = "es") -> str:
"""Sintetiza `texto` con el perfil del `tipo` y devuelve la ruta del WAV."""
pid, nombre = PROFILES.get(tipo, PROFILES["default"])
prof = _get_profile(pid)
campos = {
"text": texto,
"language": idioma,
"profile_id": pid,
"stream": "false",
}
# Los perfiles clone necesitan su transcripcion de referencia para generar.
if prof.get("ref_text"):
campos["ref_text"] = prof["ref_text"]
data = urllib.parse.urlencode(campos).encode()
req = urllib.request.Request(
f"{BASE}/generate", data=data,
headers={"Content-Type": "application/x-www-form-urlencoded"},
)
with urllib.request.urlopen(req, timeout=300) as r:
wav = r.read()
out_dir = os.path.join(os.path.expanduser("~"), "Documents", "voz_mensajes")
os.makedirs(out_dir, exist_ok=True)
out = os.path.join(out_dir, f"msg_{tipo}.wav")
with open(out, "wb") as f:
f.write(wav)
print(f"[voz_mensaje] {nombre} ({tipo}) -> {out}")
return out
def reproducir(path: str) -> None:
"""Reproduce el WAV de forma sincrona (Windows)."""
import winsound
winsound.PlaySound(path, winsound.SND_FILENAME)
def main() -> int:
ap = argparse.ArgumentParser(description="Enrutador de voz para mensajes del asistente.")
ap.add_argument("texto", help="Texto a decir")
ap.add_argument("--tipo", choices=list(PROFILES), default="default")
ap.add_argument("--idioma", default="es")
ap.add_argument("--no-play", action="store_true", help="Solo generar, no reproducir")
args = ap.parse_args()
try:
path = generar(args.texto, args.tipo, args.idioma)
except Exception as e: # noqa: BLE001
print(f"[voz_mensaje] ERROR: {e}", file=sys.stderr)
return 1
if not args.no_play:
try:
reproducir(path)
except Exception as e: # noqa: BLE001
print(f"[voz_mensaje] ERROR reproduciendo: {e}", file=sys.stderr)
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())