Metadata-Version: 2.4
Name: zeli-tts
Version: 0.3.0
Summary: ZeliSpeech Python SDK — self-hosted, low-latency streaming text-to-speech.
Author: Zeligate
License: MIT
Project-URL: Homepage, https://github.com/zelibot/zeli-tts-v2
Project-URL: Documentation, https://voice.zeligate.com
Project-URL: Source, https://github.com/zelibot/zeli-tts-v2
Keywords: tts,text-to-speech,streaming,speech,voice,zeli,zelispeech
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"

# ZeliSpeech — Python SDK

A small, dependency-light client for [ZeliSpeech](https://voice.zeligate.com) —
self-hosted, low-latency streaming text-to-speech. Point it at your running
server and get audio back in a couple of lines. The SDK talks the same
authenticated `/v1` API documented in the
[API reference](https://voice.zeligate.com/api-reference/text-to-speech/), so
the request/response shapes follow mainstream TTS-API conventions.

## Install

```bash
pip install zeli-tts
```

Runtime dep: `requests`. Live playback (`play`/`stream`) shells out to `ffplay`
(ffmpeg), `mpv`, or `afplay`.

## Quickstart

```python
from zeli_tts import ZeliSpeech, play, save, stream

client = ZeliSpeech(
    base_url="https://voice.your-domain.com",
    api_key="sk-zeli-...",                    # optional if the box runs open
)

# 1) Stream — first audio after the first sentence, not the whole passage
audio = client.text_to_speech.stream(
    voice_id="zeli-voice-1",
    text="Hey there, this is Zeli.",
    output_format="mp3_44100_128",
)
for chunk in audio:
    ...                # audio bytes as they generate

# 2) Stream + play live (needs ffplay or mpv)
stream(client.text_to_speech.stream(voice_id="zeli-voice-1", text="Playing as I generate."))

# 3) One-shot — a finished clip
audio = client.text_to_speech.convert(voice_id="zeli-voice-1", text="Hello world.")
save(audio, "hello.mp3")
```

## Client

```python
ZeliSpeech(base_url, *, api_key=None, timeout=60.0)
```

- `base_url` — `http://host:8000` or `https://voice.your-domain.com`; do **not**
  include a trailing `/v1`.
- `api_key` — sent as `Authorization: Bearer …` on every request. Required only
  when the box sets `ZELI_API_KEY`; a loopback/embedded box can run open.
- `client.capabilities()` / `client.health()` / `client.is_ready()` — engine
  info and readiness (handy while a box is warming up).

## Synthesis — `client.text_to_speech`

```python
stream(voice_id, text, *, model_id="zeli-turbo", voice_settings=None,
       output_format="mp3_44100_128", timeout=None) -> AudioStream

convert(voice_id, text, *, ...same...) -> bytes
```

- `stream(...)` returns an **`AudioStream`** — iterate it for audio `bytes` as
  they generate; `.read()` drains it to one blob. It exposes `.output_format`,
  `.sample_rate`, `.voice` (the resolved voice id), and `.request_id`.
- `convert(...)` returns the finished clip in the requested `output_format`.
- `output_format` accepts the full menu — `mp3_*`, `wav_*`, raw `pcm_*`,
  `ulaw_8000`, `alaw_8000`, `opus_48000_*` — see the
  [output formats reference](https://voice.zeligate.com/api-reference/output-formats/).
- `voice_settings` (`stability`/`style`/`speed`/…) maps onto Turbo delivery —
  see [voice settings](https://voice.zeligate.com/capabilities/voice-settings/).

## Voices — `client.voices`

```python
client.voices.list()                 # -> [Voice(id, label, description, custom), ...]
client.voices.get("zeli-voice-1")    # -> Voice (404s on unknown ids)
client.voices.add(name="My voice", file="me.wav", description="")  # -> Voice
client.voices.delete("custom-abc123")
```

`add` clones a voice **zero-shot** from a short reference clip (~10–20 s of
clean single-speaker audio; `file` may be a path, a file object, or `bytes`).
Cloning and removal apply to the Turbo engine's custom voices.

## Helpers

```python
from zeli_tts import play, save, stream, pcm_to_wav

play(audio)                    # play a finished clip (any output format)
stream(audio_stream)           # play chunks live; returns the collected bytes
save(audio, "out.mp3")         # write bytes to a file (pcm_* wrapped when .wav)
pcm_to_wav(pcm, sample_rate=24000)
```

## Errors

All derive from `ZeliSpeechError`:

| Exception | When |
|---|---|
| `ConfigurationError` | bad `base_url`, or a missing dep/player |
| `ConnectionError` | server unreachable (refused / DNS / timeout) |
| `APIError` | non-2xx from an HTTP endpoint (`.status_code`, `.status`, `.body`) |
| `GenerationError` | the stream broke mid-synthesis |

Auth failures surface as `APIError` with `.status` of `missing_api_key` or
`invalid_api_key`.

## Migrating from 0.1.x

0.2.0 moves the SDK onto the authenticated `/v1` API and adopts the ZeliSpeech
naming. `ZeliTTS` / `ZeliTTSError` remain as aliases, but the synthesis calls
changed: `stream(text, voice=...)` → `stream(voice_id, text)`, audio now
arrives in `output_format` (mp3 by default) instead of raw PCM, and the
engine-knob kwargs (`exaggeration`, `cfg_weight`, …) are replaced by
`voice_settings`.
