Sync: guardar en el historial el trabajo de las ultimas semanas que solo vivia en el disco

Este repo local tenia origin apuntando al Gitea local (localhost:3000), que Rafa
declaro archivado el 2026-06-28 (commit 962f33a, desarrollo movido a
gitea.feadulta.com). Ese memo nunca llego a este checkout: main quedo congelado
y todo el trabajo real de las ultimas 3+ semanas se fue commiteando solo en la
rama fix/multiidioma-portada-132 (ya fusionada a main sin perdida, commit
2504666), mientras que ademas se acumulaban 78 cambios sin commitear en el
working tree que nunca llegaron a NINGUN historial de git.

Este commit consolida esos cambios sueltos: TTS multi-voz (tts_*.py), scripts
de traduccion (translate_haiku.py, pretranslate_en_haiku.py, sync_translations_to_prod.py),
mu-plugins nuevos desplegados a prod (fea-beta-feedback, fea-cloudflare-realip,
fea-legacy-redirect, fea-gsc-verification, fea-support-campaign, fea-ui, etc.),
scripts de mantenimiento de enlaces/cartas, capturas E2E (tools/e2e/shot_*.cjs)
y documentacion de sesiones recientes.

Excluido deliberadamente (no es codigo versionable): tts-voices/ (3.3GB de
muestras de audio para clonacion de voz, anadido a .gitignore), logs/ (logs de
ejecucion, anadido a .gitignore), y 2 ficheros vacios accidentales + 2 copias
duplicadas sueltas en la raiz que ya existen en su ubicacion correcta.
This commit is contained in:
2026-07-15 20:03:21 -04:00
parent 250466696b
commit 69e849d38e
72 changed files with 5413 additions and 220 deletions
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""
tts_eval.py — Genera la MISMA frase de feadulta con varias voces/modelos TTS para
compararlas (evaluación de voz, issue #76). Incluye:
- edge-tts Ximena (referencia, gratis, ya la usamos) — siempre.
- Modelos premium vía Hugging Face Inference Providers (consume crédito HF) — opcional.
Objetivo: ELEGIR voz. Para producción en masa NO se usa HF (sale caro); el modelo
abierto ganador se corre en LOCAL (RTX 5060 Ti) gratis. Ver análisis en #76.
Uso:
# Solo la referencia local (gratis):
python3 scripts/tts_eval.py
# Con modelos HF (necesita token; gasta unos céntimos del crédito):
HF_TOKEN=hf_xxx python3 scripts/tts_eval.py --hf
Salida: ./tts-eval/<nombre>.mp3 (escúchalos y elige).
"""
from __future__ import annotations
import argparse, os, subprocess, sys
from pathlib import Path
SAMPLE = (
"Bienvenido a Fe Adulta. La humanidad abriga una esperanza: verse liberada de la "
"esclavitud y alcanzar la libertad de los hijos de Dios. Una fe adulta es una fe "
"personal, valiente, sin miedos infantiles. Detente un instante y respira."
)
OUT = Path(__file__).resolve().parent.parent / "tts-eval"
EDGE = os.path.expanduser("~/.hermes/hermes-agent/venv/bin/edge-tts")
# Candidatos vía HF Inference Providers (provider, model). Verifica disponibilidad en la
# pestaña "Inference Providers" de cada modelo en huggingface.co — el routing cambia.
HF_CANDIDATES = [
("fal-ai", "fal-ai/f5-tts"),
("fal-ai", "fal-ai/chatterbox/text-to-speech"),
("hf-inference", "myshell-ai/MeloTTS-Spanish"),
]
def edge_samples():
OUT.mkdir(exist_ok=True)
for voz in ("es-ES-XimenaNeural", "es-ES-ElviraNeural", "es-MX-JorgeNeural"):
dst = OUT / f"edge-{voz}.mp3"
print(f"edge-tts {voz} ...", flush=True)
subprocess.run([EDGE, "--voice", voz, "--text", SAMPLE, "--write-media", str(dst)],
capture_output=True)
print(f" -> {OUT}")
def hf_samples():
try:
from huggingface_hub import InferenceClient
except ImportError:
sys.exit("Falta huggingface_hub: pip install huggingface_hub")
token = os.environ.get("HF_TOKEN")
if not token:
sys.exit("Define HF_TOKEN para usar --hf")
OUT.mkdir(exist_ok=True)
for provider, model in HF_CANDIDATES:
name = model.replace("/", "_")
try:
client = InferenceClient(provider=provider, api_key=token)
audio = client.text_to_speech(SAMPLE, model=model)
dst = OUT / f"hf-{name}.mp3"
dst.write_bytes(audio)
print(f"OK {provider}:{model} -> {dst.name}")
except Exception as exc: # noqa: BLE001
print(f"FALLO {provider}:{model} -> {exc}")
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--hf", action="store_true", help="También generar con modelos HF (gasta crédito).")
args = ap.parse_args()
edge_samples()
if args.hf:
hf_samples()
print("\nEscucha los .mp3 en", OUT, "y elige. Para producción: correr el modelo abierto ganador en local.")
if __name__ == "__main__":
raise SystemExit(main())