Metadata-Version: 2.5
Name: nkl-kokoro-tts-cli
Version: 0.1.8
Summary: Minimal, pipeline-first CLI for Kokoro TTS — stdin/stdout, wav/mp3, log levels
Project-URL: Homepage, https://github.com/nkl/kokoro-tts-cli
Project-URL: Repository, https://github.com/nkl/kokoro-tts-cli
Project-URL: Issues, https://github.com/nkl/kokoro-tts-cli/issues
Author: nkl
License: MIT
License-File: LICENSE
Keywords: cli,kokoro,pipeline,text-to-speech,tts
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
Requires-Python: >=3.10
Requires-Dist: huggingface-hub
Requires-Dist: kokoro>=0.9.4
Requires-Dist: loguru
Requires-Dist: misaki[en]>=0.9.4
Requires-Dist: numpy
Requires-Dist: soundfile
Requires-Dist: torch
Requires-Dist: transformers
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Description-Content-Type: text/markdown

# nkl-kokoro-tts-cli

Minimal, **pipeline-first** CLI for [Kokoro TTS](https://github.com/hexgrad/kokoro) (`hexgrad/Kokoro-82M`). Unix-philosophy: text in → `wav`/`mp3` out, logs to `stderr`, `stdout` stays binary-clean.

> Contrast with [Ultimate TTS Studio](https://github.com/SUP3RMASS1VE/Ultimate-TTS-Studio-SUP3R-Edition): Windows/Gradio studio with 10 engines. This is the opposite — one engine, one job, works in pipes.

## Features

*   **Pipeline-first** — `stdin`/`stdout` everywhere. `echo "hi" | kokoro-tts -o - | mpv --no-video -` works, no temp file. Logs (`[INFO]`/`[WARN]`/`[ERROR]`) go to `stderr` only.
*   **Always local model** — default `~/.cache/huggingface/hub` (or `$HF_HUB_CACHE`/`$KOKORO_MODEL_DIR`). First run auto-downloads `hexgrad/Kokoro-82M` (~340M) via `huggingface_hub`, then fully offline. `kokoro-tts download --model-dir ./kokoro-model` for a plain dir.
*   **Unix verbosity** — default `WARN` (only warnings/errors). `-v` → `INFO`, `-vv` → `DEBUG`, `-q` → `ERROR` only, `--log-level` overrides. Quiet by default for `| mpv`.
*   **Perf** — in-process pipeline + voice tensor cache (`cold 5.3s → warm 0.42s` 12.5×), lazy `torch`/`kokoro` import so `--help` is instant, `CUDA` ~10× for long texts.
*   **Audio** — `wav` default (no deps) via `soundfile` 24kHz, `mp3` via `ffmpeg` (`-f mp3`), `mpv` pipe friendly.
*   **Voices & langs** — `am_echo` default, 50+ known (`af_`, `bf_`, `ef_`, `jf_`, `zf_`…), auto `lang` from voice prefix (`a` US, `b` GB, `e` es, `f` fr, `h` hi, `i` it, `p` pt-br, `j` ja, `z` zh), custom `.pt` voice.
*   **Linux standards** — `uv` + `hatchling` `src/` layout, `pyproject.toml`, `requires-python >=3.10`, `kokoro-tts` entry point, `88` mocked tests, `SIGPIPE` safe.

## Installation

**System deps (recommended):**
```bash
# Debian/Ubuntu
sudo apt install espeak-ng ffmpeg mpv
# Arch
sudo pacman -S espeak-ng ffmpeg mpv
```
`espeak-ng` improves G2P fallback, `ffmpeg` needed for `mp3`, `mpv` for pipe playback.

**User install:**
```bash
# uv tool (isolated, recommended)
uv tool install nkl-kokoro-tts-cli
kokoro-tts --help

# pip
pip install nkl-kokoro-tts-cli

# first run auto-downloads model to ~/.cache/huggingface/hub (~340M)
kokoro-tts "Hello world" -o hello.wav
```

**Pre-download (optional, for offline):**
```bash
kokoro-tts download                          # → ~/.cache/huggingface/hub
kokoro-tts download --model-dir ./kokoro-model  # → plain ./kokoro-model
KOKORO_MODEL_DIR=./kokoro-model kokoro-tts "Hi" -o out.wav
```

## Development

```bash
git clone https://github.com/nkl/kokoro-tts-cli
cd kokoro-tts-cli

# env + deps (includes en_core_web_sm 3.8.0 via direct ref)
uv sync --extra dev
uv run kokoro-tts --help

# tests (70+ mocked, no GPU/HF needed)
uv run pytest -q          # 88 passed
uv run pytest -v
uv run pytest -k test_model_local

# lint / build / publish
uv run ruff check src tests
uv build
uv run twine check dist/*
uv tool install --force dist/nkl_kokoro_tts_cli-*.whl
```

Project layout: `src/nkl_kokoro_tts_cli/{cli,tts,logger}.py` (`cli` = pipe/arg parsing, `tts` = Kokoro wrapper + pipeline/voice cache, `logger` = leveled `stderr`), `tests/{test_cli,test_tts,test_logger,test_model_local}`.

## Usage

```bash
kokoro-tts --help
kokoro-tts --list-voices
kokoro-tts --version
```

### Basic

```bash
kokoro-tts "Hello world, this is Kokoro." -o hello.wav
kokoro-tts "Hello" --voice am_echo --speed 1.0 -o hello.wav
kokoro-tts "Hello" --voice af_bella --lang a -o out.wav  # lang auto from voice
```

### Pipes (first-class)

```bash
echo "Hello from the pipeline" | kokoro-tts -o out.wav
cat book.txt | kokoro-tts --format mp3 -o book.mp3
kokoro-tts --input-file chapter.txt -o chapter.wav --speed 0.95
from_clipboard | kokoro-tts -o - | mpv --no-video -  # from_clipboard = xsel -ob
```

`kokoro-tts` writes `wav` to `stdout` when `-o -` or when `stdout` is piped and no `-o` given.

### `mpv` pipeline

```bash
# wav (no ffmpeg)
echo "Hello, this is a test" | kokoro-tts --voice am_echo -o - | mpv --no-video -

# mp3 (needs ffmpeg)
echo "Hello" | kokoro-tts -o - --format mp3 | mpv -

# with speed / volume, silent logs
cat transcript.txt | kokoro-tts --voice af_bella --speed 0.95 -o - --log-level ERROR | mpv --no-video --volume=80 --really-quiet -
```
Tip: `mpv --no-video -` reads `stdin`. Our logs stay on `stderr`.

### No `-o` defaults

* `stdout` is a pipe → `wav` to `stdout` (`| mpv -` works without `-o -`)
* `stdout` is a TTY → `./output.wav` + `[WARN]`

### Formats

```bash
kokoro-tts "Hello" -o out.wav               # wav (default)
kokoro-tts "Hello" -o out.mp3 --format mp3  # mp3 (ffmpeg)
echo "hi" | kokoro-tts -o - --format mp3 | mpv -
```

### Voices & languages

```bash
kokoro-tts --list-voices                    # 50+ af_/am_/bf_/bm_/ef_/jf_/zf_...
kokoro-tts "Bonjour le monde" --voice ff_siwis -o fr.wav
kokoro-tts "Hola mundo" --voice ef_dora --lang e -o es.wav
kokoro-tts "Hello" --voice /path/to/custom.pt -o out.wav
kokoro-tts "Hi" --voice af_bella,af_nicole -o averaged.wav  # averaged
```

`--lang` auto-inferred from voice prefix; default `am_echo` → `a` (US).

### Verbosity (Unix standard)

```bash
kokoro-tts "Hello" -o out.wav               # default WARN (only WARN/ERROR)
kokoro-tts "Hello" -o out.wav -v            # INFO + WARN + ERROR
kokoro-tts "Hello" -o out.wav -vv           # DEBUG (adds chunk/ps debug)
kokoro-tts "Hello" -o out.wav -q            # ERROR only
kokoro-tts "Hello" -o out.wav --log-level DEBUG
```

### Long texts & splitting

Kokoro chunks at `510` phonemes. Override:
```bash
kokoro-tts --input-file long.txt --split-pattern '\n+' -o out.wav
```

### Model & device

```bash
# model (always local)
kokoro-tts download --model-dir ./kokoro-model  # plain dir
KOKORO_MODEL_DIR=./kokoro-model kokoro-tts "Hi" -o out.wav
# default HF cache: ~/.cache/huggingface/hub/models--hexgrad--Kokoro-82M

# device (auto picks CUDA if available)
kokoro-tts "Hello" -o out.wav --device auto -v  # [INFO] pipeline ready device='cuda:0' (hf-cache)
kokoro-tts "Hello" -o out.wav --device cpu
kokoro-tts "Hello" -o out.wav --device cuda
```

Long `2600 chars`: `CPU 40s` vs `CUDA 4s`.

## Troubleshooting

* `ffmpeg not found` for `mp3` → `sudo apt install ffmpeg`
* `EspeakFallback not Enabled` → `sudo apt install espeak-ng`
* `Can't find model 'en_core_web_sm'` → fixed in `0.1.5+` via direct `en_core_web_sm 3.8.0` dep; `uv tool update nkl-kokoro-tts-cli`
* `unauthenticated requests to HF Hub` → suppressed in `0.1.5`; set `export HF_TOKEN=hf_xxx` to raise rate limits
* `CUDA requested but not available` → `--device cpu`

## Publish

```bash
uv build
uv run twine check dist/*
uv publish
```

Or with `just`:
```bash
just build   # uv build (runs tests first)
just publish # uv build + twine check + uv publish
```

`pyproject.toml` uses `hatchling`, `allow-direct-references` for `en_core_web_sm`, entry `kokoro-tts = nkl_kokoro_tts_cli.cli:main`.

## License

MIT
