Kenkui
Kenkui turns EPUB books into M4B audiobooks. Speech is synthesized locally on your CPU with Pocket-TTS, and a book can be read by a single narrator or by a full cast, with a different voice for each character.
import kenkui as kk
if __name__ == "__main__":
kk.load_voice("eponine")
kk.magic_run("book.epub", narrator="eponine") # writes book.m4b
- One narrator or a full cast. Characters are discovered locally with spaCy or by a language model, dialogue is attributed to its speaker, and voices are cast deterministically, staying consistent across a whole series.
- Fix it without re-rendering. Read the script a book will be spoken from, correct a speaker, a pause, or a pronunciation, and preview only that line.
- Local and predictable. Speech synthesis never touches the network. Voices are downloaded only when you ask for them, and a failed or cancelled render never leaves a half-written file behind.
- A typed, immutable Python API. Every step returns a new pipeline you can branch, inspect, and reuse.
The Kenkui suite
| Project | What it is | License |
|---|---|---|
| kenkui (this repository) | The Python library: the EPUB-to-M4B pipeline. | Apache-2.0 |
| kenkui-server | A self-hostable API server that runs jobs on this library. | AGPL-3.0-only |
| kenkui-studio | A web and desktop app for creating audiobooks through the server. | AGPL-3.0-only |
| kenkui-voices | The 95 precompiled voices in the built-in catalog. | per-voice |
Install
Kenkui needs Python 3.11–3.13, and FFmpeg 5 or newer on your PATH.
pip install kenkui
brew install ffmpeg # macOS
sudo apt-get install -y ffmpeg # Debian / Ubuntu
The install is large, because Pocket-TTS depends on PyTorch. Voices are downloaded separately, on first use: about 225 MB per language, plus about 6.5 MB per voice. To discover characters offline, also install the spaCy extra:
pip install "kenkui[spacy]"
python -m spacy download en_core_web_lg
Usage
One narrator
import kenkui as kk
if __name__ == "__main__":
kk.load_voice("eponine") # one-time download; later runs are offline
book = kk.book("book.epub")
for chapter in book.inspect().chapters:
print(chapter.id, chapter.title)
result = (
book.pronounce(numbers="standard")
.pauses(chapter_ms=1500, paragraph_ms=300)
.assign_voice("eponine")
.tts()
.metadata(title="My Book", author="An Author")
.write("book.m4b", on_event=print)
)
print(result.output, result.stats.duration_ms)
Rendering starts worker processes, and each one re-imports your script, so
keep rendering under if __name__ == "__main__":.
A full cast
Quote attribution uses any LiteLLM model. Set the
provider's API key in your environment, for example OPENROUTER_API_KEY.
import kenkui as kk
MODEL = "openrouter/deepseek/deepseek-v4-flash"
if __name__ == "__main__":
for voice in ("eponine", "paul", "anna", "charles", "jane", "george"):
kk.load_voice(voice)
(
kk.book("book.epub")
.series("my-series", book=1) # characters keep their voices in later books
.infer_characters("spacy") # or a LiteLLM model
.attribute_quotes(MODEL)
.assign_voices(narrator="eponine", unknown="paul", cast={"alice": "anna"})
.tts()
.write("book.m4b")
)
Correct one line and hear the fix
book = kk.book("book.epub").annotations().assign_voice("eponine")
where = {"chapter": "*", "paragraph": 1}
for row in book.script().at(where): # what will be said, and why
print(row.path, row.character, row.provenance, row.text[:60])
book = book.attribute("irulan", where=where).silence(900, where=where)
book.select(where).preview("probe.wav") # renders only those lines
book.write_annotations() # saved beside the EPUB for the next render
Examples
examples/ contains runnable scripts, from inspecting a book to
batch-rendering a library. Most take the path to an EPUB as their only argument:
| Script | Shows |
|---|---|
01_inspect.py |
Metadata, chapter IDs, validation, and the voice catalog, with no downloads |
02_quickstart.py |
load_voice() and magic_run() |
03_explicit_pipeline.py |
Chapter selection, pronunciation, pauses, cover art, progress events, cancellation, error codes |
04_multi_voice.py |
Character inference, attribution, casting, and inspecting the cast before rendering |
05_review_characters.py |
Correcting the discovered roster before attribution |
06_series_and_style.py |
Series voice continuity, lexicons, and a reusable house_style with .pipe() |
07_dial_in.py |
The script → correct → preview → save loop |
08_library_batch.py |
Rendering a whole library from a table of books |
09_voices.py |
Loading, listing, and registering your own voice |
Documentation
Full documentation is at https://d1zzl3d0p.github.io/kenkui/:
- Getting started
- Guide: every feature, in depth
- API reference
- Models and voices: the catalog, and voice rights
- Troubleshooting: stable error codes
Contributing
See CONTRIBUTING.md. Changes are recorded in CHANGELOG.md.
License
Kenkui is licensed under the Apache License 2.0. Voices and model weights are separate works with their own terms, and every built-in voice ships marked as not cleared for commercial use. See Models and voices.
Metadata
Release files for kenkui 10.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kenkui-10.0.0.tar.gz | 478.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kenkui-10.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 761.0 kB
Release files / kenkui-10.0.0.tar.gz
| Download URL | kenkui-10.0.0.tar.gz |
|---|---|
| Size | 478.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
124b061016336ad49e9a87d9865a46fa2ba38728d8e5177e0d5cf34d44542038
|
|
BLAKE2b-256 checksum How to use checksums |
4ea0b5a703ffffd335b46d20c0f577530361d76bb56089534e6b13102c3fe30b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.
Transparency logRelease files / kenkui-10.0.0-py3-none-any.whl
| Download URL | kenkui-10.0.0-py3-none-any.whl |
|---|---|
| Size | 282.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5de019064e6f2c683a9f8ca2df40877a00e49a86d6f07405a57e65e34ae130ce
|
|
BLAKE2b-256 checksum How to use checksums |
cdeb875649dceb08771801e655e03aff3cbe9c9de7f3c6d0fb23427081c03201
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.
Transparency log