#!/usr/bin/env python3
"""voz_kokoro_vivo.py - a voz local do modo voz ao vivo: o Kokoro-82M fica CARREGADO e fala frase por frase.

Entrada (uma linha de JSON por pedido): {"id": 1, "texto": "Oi.", "saida": "/tmp/x.wav", "voz": "pf_dora"}
Saída (uma linha por resposta):          {"id": 1, "ok": true, "arquivo": "/tmp/x.wav", "segundos": 1.2}
Primeira linha de saída: {"pronto": true}. Só GERA arquivo WAV; nunca toca som. Roda pelo uv (lib/motor/fala-viva.js monta o comando).
"""
import json
import re
import sys


def main():
    import numpy as np
    import soundfile as sf
    from kokoro import KPipeline

    pipe = KPipeline(lang_code="p")
    # uma passada curta aquece o modelo: a primeira frase de verdade já não paga a carga
    try:
        for _ in pipe("Oi.", voice="pf_dora"):
            pass
    except Exception:
        pass
    print(json.dumps({"pronto": True}), flush=True)
    for linha in sys.stdin:
        linha = linha.strip()
        if not linha:
            continue
        pedido = {}
        try:
            pedido = json.loads(linha)
            texto = re.sub(r"\s+", " ", str(pedido.get("texto") or "")).strip()
            if not texto:
                print(json.dumps({"id": pedido.get("id"), "ok": False, "error": "texto vazio"}), flush=True)
                continue
            partes = [np.asarray(a, dtype=np.float32) for _, _, a in pipe(texto, voice=pedido.get("voz") or "pf_dora")]
            audio = np.concatenate(partes)
            sf.write(str(pedido["saida"]), audio, 24000)
            print(json.dumps({"id": pedido.get("id"), "ok": True, "arquivo": pedido["saida"], "segundos": round(len(audio) / 24000, 3)}), flush=True)
        except Exception as e:
            print(json.dumps({"id": pedido.get("id"), "ok": False, "error": str(e)[:300]}), flush=True)


if __name__ == "__main__":
    main()
