# Guía de llamadas con curl

Todas las llamadas están probadas contra el servidor local (`http://localhost:5055`).
Cambia `HOST` por la dirección del servidor y, si tiene `API_TOKEN`, añade
`-H "Authorization: Bearer TU_TOKEN"` a cada llamada (o `?token=TU_TOKEN` en la URL WebSocket).

```bash
HOST=http://localhost:5055
```

> **WebSocket y curl.** curl ≥ 7.86 puede *abrir* una conexión WebSocket y mostrar lo que envía
> el servidor, pero **no puede enviarle mensajes**. Por eso hay dos formas de trabajar:
>
> - **Solo curl (HTTP + REST):** se abre la locución con `POST /v1/tts/stream` (el audio llega en la
>   propia respuesta) y se controla con `/v1/streams/:id/...`. Se explica en las secciones 1-8.
> - **WebSocket:** se abre con un cliente WS (`websocat`, `wscat` o el cliente JS/Node incluido), y la pausa,
>   la reanudación y la cancelación se pueden hacer igualmente con curl por REST (sección 9).
>
> En Windows usa `curl.exe` (en PowerShell `curl` es un alias de `Invoke-WebRequest`). El curl que trae
> Windows 10/11 (8.0) no soporta `ws://`; el de Git for Windows sí. Ver «Windows» al final.

---

## 0. Conexión

### Comprobar que el servidor responde

```bash
curl -s $HOST/v1/config
```
```json
{"protocol":"…","audio":{"sample_rate":24000,"channels":1,"bit_depth":16,"encoding":"pcm_s16le"},
 "auth_required":false,"limits":{"max_upstream_concurrency":3,"max_streams":64,"max_streams_per_session":16,"max_input_chars_per_request":10000}}
```

### Abrir la conexión WebSocket (handshake)

```bash
curl -s -N ws://localhost:5055/v1/stream
```
```json
{"type":"ready","session_id":"ses_8011037fbe","audio":{"sample_rate":24000,"channels":1,"bit_depth":16,"encoding":"pcm_s16le"},
 "binary_header":{"bytes":8,"layout":["sid:uint32le","seq:uint32le"]},"limits":{...}}
```
La conexión queda abierta (Ctrl+C para cerrarla). Sirve para verificar conectividad, proxies y token.

## 1. Health: ¿está el motor disponible?

```bash
curl -s -w "\nHTTP %{http_code}\n" $HOST/v1/health
```
- `200` y `"status":"ok"`: listo para locutar.
- `503` y `"status":"starting"`: el motor está cargando el modelo (~15-20 s tras arrancar).
- `503` y `"status":"degraded"`: el motor no responde o no está instalado (ver `engine.process.last_error`).
- Sin respuesta: el servidor no está en marcha.

Comprobación para scripts (código de salida ≠ 0 si no está listo):

```bash
curl -sf $HOST/v1/health > /dev/null && echo "LISTO" || echo "NO DISPONIBLE"
```

Otros datos útiles:

```bash
curl -s $HOST/v1/stats            # streams activos, peticiones en curso/en cola, totales
curl -s $HOST/v1/voices           # catálogo: voces del modelo (Maider…) y perfiles clonados
curl -s $HOST/v1/config           # formato de audio, voz por defecto, límites
```

## 2. Locutar un texto (respuesta en streaming)

El audio llega **según se genera**; `-N` desactiva el buffer de curl.

```bash
curl -N -X POST $HOST/v1/tts/stream \
  -H "Content-Type: application/json" \
  -d '{"id":"loc1","text":"Kaixo, hau da lehenengo esaldia. Eta hau bigarrena.","voice":"Maider","format":"wav"}' \
  -o locucion.wav
```

- `format`: `wav` (reproducible directamente) o `pcm` (int16 LE, 24 kHz, mono).
- `id` es opcional, pero ponerlo permite pausar o cancelar desde otra terminal sin esperar a las cabeceras.
- Cabeceras de respuesta: `X-Stream-Id`, `X-Audio-Sample-Rate`… y, al terminar, el trailer
  `X-Stream-State: done | cancelled` (se ve con `-D cabeceras.txt`).

Reproducir mientras se genera (con ffplay o mpv):

```bash
curl -sN -X POST $HOST/v1/tts/stream -H "Content-Type: application/json" \
  -d '{"text":"Kaixo mundua. Sortu ahala entzuten da.","voice":"Antton","format":"wav"}' \
  | ffplay -autoexit -nodisp -
```

Versión GET (útil para navegadores o `<audio src>`):

```bash
curl -N "$HOST/v1/tts/stream?text=Kaixo%20mundua&voice=Maider&format=wav" -o kaixo.wav
```

Otros parámetros habituales: `"voice":"clone:Mikel"` (perfil clonado), `"temperature":0.5`
(entonación más variada), `"seed":42` (resultado reproducible), `"pacing":"realtime"` (envío a
ritmo de reproducción), `"priority":5`. Lista completa en [API.md](API.md).

Las cifras se leen en euskera automáticamente:

```bash
curl -N -X POST $HOST/v1/tts/stream \
  -H 'Content-Type: application/json' \
  -d '{"text":"Gaur, 2024ko azaroaren 2an, 1.250 lagun bildu dira. Sarrerak 15€ balio du.","format":"wav"}' \
  -o albistea.wav
```

### Clonación zero-shot en la propia petición

5-10 s de habla limpia en euskera; no hace falta transcripción.

```bash
REF=$(base64 -w0 nire_ahotsa.wav)
curl -N -X POST $HOST/v1/tts/stream -H 'Content-Type: application/json' \
  -d '{"text":"Ahots hau audioko ahotsaren kopia da.","ref_audio_b64":"'$REF'","format":"wav"}' \
  -o klona.wav
```

O guardarla como perfil y usarla después con `"voice":"clone:nire_ahotsa"`:

```bash
curl -s -X POST $HOST/v1/voices/profiles -H 'Content-Type: application/json' \
  -d '{"profile_id":"nire_ahotsa","gender":"female","description":"Nire ahotsa","ref_audio_b64":"'$REF'"}'
```

## 3. Locutar texto que llega a trozos (modo incremental)

Terminal A: abre la locución; la respuesta queda abierta esperando texto.

```bash
curl -N -X POST $HOST/v1/tts/stream \
  -H "Content-Type: application/json" \
  -d '{"id":"loc1","mode":"incremental","voice":"Maider","format":"wav"}' \
  -o locucion.wav
```

Terminal B: envía el texto. Cada frase completa se sintetiza en cuanto llega.

```bash
curl -s -X POST $HOST/v1/streams/loc1/text \
  -H "Content-Type: application/json" \
  -d '{"text":"Kaixo, hau da lehenengo esaldia. Eta hau bigarrena. "}'
```

```bash
curl -s -X POST $HOST/v1/streams/loc1/text \
  -H "Content-Type: application/json" \
  -d '{"text":"Última frase.","end":true}'
```

- `"flush":true` sintetiza ya lo pendiente aunque no acabe en punto.
- `"end":true` cierra la entrada: cuando termina de sonar lo pendiente, la respuesta de la terminal A se cierra.
- Equivalentes sueltos: `POST /v1/streams/loc1/flush` y `POST /v1/streams/loc1/end`.
- Si no llega texto en `idle_timeout_s` (60 s por defecto), la entrada se cierra sola.

## 4. Pausar la locución

```bash
curl -s -X POST $HOST/v1/streams/loc1/pause
```
Devuelve el estado del stream con `"state":"paused"`. El servidor deja de enviar audio y la generación
sigue en segundo plano, así que reanudar es inmediato.

> Con `pacing: "burst"` (por defecto) el servidor envía el audio más rápido que el tiempo real y el
> cliente puede tener ya varios segundos en su buffer. Si quieres que pausar y cancelar se oigan al
> instante en clientes simples, abre la locución con `"pacing":"realtime"`.

## 5. Reanudar

```bash
curl -s -X POST $HOST/v1/streams/loc1/resume
```

## 6. Cancelar (cortar la locución)

```bash
curl -s -X DELETE $HOST/v1/streams/loc1
```
```json
{"id":"loc1","cancelled":true,"state":"running"}
```
Aborta las peticiones pendientes al motor y cierra la respuesta de audio, que termina con el trailer
`X-Stream-State: cancelled`. `"cancelled":false` indica que ya había terminado. Alternativa:
`POST /v1/streams/loc1/cancel`.

Cancelar **todas** las locuciones activas del servidor:

```bash
curl -s -X DELETE $HOST/v1/streams
```

Cerrar la conexión de la terminal A (Ctrl+C) también cancela la locución.

## 7. Consultar el estado

```bash
curl -s $HOST/v1/streams/loc1              # detalle: estado, segmento actual, estadísticas
```

```bash
curl -s "$HOST/v1/streams?active=1"        # solo las activas
```

```bash
curl -s $HOST/v1/streams                   # activas + historial reciente
```

Estados: `queued` → `running` ⇄ `paused` → `done` | `cancelled` | `error`.

## 8. Errores

| Código | Ejemplo |
|---|---|
| `400` | `{"error":{"code":"invalid_params","message":"\"speed\" debe estar entre 0.5 y 2"}}` |
| `401` | Falta el token o no es válido. |
| `404` | Stream o voz inexistente: `{"error":{"code":"not_found",...}}` |
| `409` | Locución cancelada antes de emitir audio. |
| `429` | Demasiados streams activos en el servidor. |
| `502`/`503` | El motor no responde o no está listo (ver `/v1/health`). |

---

## 9. Locución por WebSocket

Con un cliente WS interactivo (cada línea que escribes es un mensaje):

```bash
websocat ws://localhost:5055/v1/stream
```

```bash
npx wscat -c ws://localhost:5055/v1/stream
```

Mensajes a enviar (uno por línea):

```json
{"type":"start","id":"ws1","text":"Kaixo, hau WebSocket bidezko esatari-lana da. Bigarren esaldia.","voice":"Maider"}
{"type":"start","id":"ws2","mode":"incremental","voice":"Antton"}
{"type":"text","id":"ws2","text":"Texto que llega a trozos. "}
{"type":"text","id":"ws2","text":"Y se cierra aquí.","end":true}
{"type":"pause","id":"ws1"}
{"type":"resume","id":"ws1"}
{"type":"cancel","id":"ws1"}
{"type":"cancel","all":true}
{"type":"list"}
```

El servidor responde con eventos JSON (`started`, `segment_start`, `first_audio`, `segment_end`, `done`,
`cancelled`…) y el audio en frames binarios `[sid u32][seq u32][PCM]`. Los terminales muestran ese
binario como caracteres extraños: para guardar o reproducir el audio usa
[`examples/node-client.js`](../examples/node-client.js) o la consola web.

**Control con curl de una locución abierta por WebSocket.** Los streams WS también están en la API REST,
así que basta con conocer su `id`:

```bash
curl -s -X POST $HOST/v1/streams/ws1/pause
```

```bash
curl -s -X POST $HOST/v1/streams/ws1/resume
```

```bash
curl -s -X DELETE $HOST/v1/streams/ws1
```

El cliente WS recibe los eventos `state` (`paused`/`running`) y `cancelled` con `"reason":"api"`.

---

## Windows

En **PowerShell** usa `curl.exe` y pasa el JSON desde un fichero para evitar problemas de comillas:

```powershell
'{"id":"loc1","text":"Kaixo mundua.","voice":"Maider","format":"wav"}' | Set-Content -Encoding utf8 body.json
curl.exe -N -X POST http://localhost:5055/v1/tts/stream -H "Content-Type: application/json" --data-binary "@body.json" -o locucion.wav
curl.exe -s -X POST http://localhost:5055/v1/streams/loc1/pause
curl.exe -s -X DELETE http://localhost:5055/v1/streams/loc1
```

En **cmd** se escapan las comillas dobles:

```bat
curl -N -X POST http://localhost:5055/v1/tts/stream -H "Content-Type: application/json" -d "{\"text\":\"Kaixo mundua.\",\"voice\":\"Maider\",\"format\":\"wav\"}" -o locucion.wav
```
