Vellichor

Vellichor

Docker app from woodsjail.'s Repository

Overview

Self-hosted web app that turns your written stories and ebooks into narrated audiobooks using the Kokoro-82M TTS model. Multi-voice character casting, background ambience, chaptered M4B + per-chapter MP3 output, and auto-export to an Audiobookshelf library. Optional NVIDIA GPU acceleration (requires the Nvidia Driver plugin); falls back to CPU. Optional AI "Smart cast" needs a separate Ollama container β€” set Ollama URL below; without it the rule-based Quick detect is used.

Vellichor 🎧

A self-hosted web app that turns your written stories (and ebooks) into narrated audiobooks. Fast by default with the Kokoro-82M TTS model, plus an optional expressive engine (Chatterbox) that adds emotional intensity and voice cloning β€” clone a voice from a short clip, or record your own right in the browser. GPU-accelerated and light enough to run on a modest card (originally built on a GTX 1080); falls back to CPU. Open source (MIT).

Requirements

  • Docker and Docker Compose.
  • (Optional) NVIDIA GPU for acceleration β€” requires the NVIDIA Container Toolkit on the host (on Unraid, the Nvidia Driver plugin). With no GPU it runs on CPU instead β€” see the no-GPU note in Getting started.
  • Disk: ~7 GB for the Docker image, plus models downloaded on first use β€” Kokoro (small), the optional Ollama Smart-cast LLM (~2 GB), and the optional Chatterbox expressive model (~1–2 GB). Budget ~12 GB total to use everything.

Getting started

# 1. Clone
git clone https://github.com/woodscode/vellichor-web.git
cd vellichor-web

# 2. Create your .env: set the admin password and a cookie-signing key
cp .env.example .env
sed -i "s|^SECRET_KEY=.*|SECRET_KEY=$(openssl rand -hex 32)|" .env
$EDITOR .env                       # set VELLICHOR_PASSWORD

# 3. Edit docker-compose.yml for your box:
#    - Audiobookshelf export mount (…:/library) β€” repoint to your library, or
#      remove the volume if you don't use Audiobookshelf
#    - host port (default 7777:7777)
#    - NO GPU? remove the `runtime: nvidia` and `NVIDIA_*` lines from BOTH
#      services (it then runs on CPU β€” slower, but works)

# 4. Build & start
docker compose up -d --build

# 5. (Optional) enable AI Smart cast β€” pull the local LLM once
docker exec vellichor-ollama ollama pull llama3.2:3b

Then open http://:7777 and sign in as admin with your VELLICHOR_PASSWORD. Store that password in your password manager.

To give someone else an account, click πŸ‘€ admin in the header β†’ People β†’ add a username and an initial password. See Accounts below.

Features

  • Built-in story editor (type/paste, # lines become chapters) + upload .txt, .md, .epub, .kepub, .pdf, .docx, .html, .rtf, .fb2. EPUBs are split into chapters in reading order using the book's table of contents; PDFs split on their bookmarks/outline when present.
  • πŸ“š Book library & batch conversion β€” a managed shelf of source files at data/users/<username>/books/. Add files or a whole folder from the Upload tab, or just drop ebooks straight into that share and they appear automatically. Then Convert all, or tick a few and Convert selected β€” each book shows its detected chapter count, and batch jobs queue one at a time using the voice/settings you've chosen.
  • Narration directives β€” inline cues the studio interprets itself: [pause 3s] / [pause] / [beat] insert real silence, and [slow], [fast], [normal] change the pace of the following text. (These are reserved words β€” they're never read aloud or mistaken for a [Name] speaker tag.)
  • Voice picker with 35 voices, grouped/filterable, each with a β–Ά sample you can play/stop (every play button toggles). Story-friendly voices are starred (β˜…). af_heart is the default.
  • Saved presets & My Stories β€” save a bundle of settings (voice, engine, loudness, ambience…) as a named preset and reapply it per series; keep works-in-progress in a My Stories list. Re-saving a preset updates it in place rather than duplicating.
  • Live preview β€” hear your chosen voice read the current text before committing to a full conversion.
  • Choose your TTS engine (dropdown, per conversion):
    • Kokoro (default) β€” fast, lightweight, many preset voices.
    • Chatterbox (expressive) β€” richer, more lifelike delivery with an Expressiveness dial and voice cloning. Heavier (more VRAM, slower).
  • πŸŽ™οΈ Record / clone a voice (Chatterbox) β€” record your own voice in the browser (read the on-screen script for ~15–20s) or upload a short clip, and save it to a reusable My Voices library. Narrate in your voice. (The mic needs a secure page β€” see Expressive voices below.)
  • Reading-speed slider, optional cover art, author label.
  • Output loudness control (Off / Standard / Loud / Extra loud) β€” applies EBU R128 loudness normalization so the finished book plays at a consistent, full volume, even on quiet speakers (e.g. a Toniebox). Defaults to Loud.
  • Live progress (stage, segment count, ETA) + per-job log.
  • 🎭 Multi-voice cast β€” give each character their own voice. Three ways:
    • πŸͺ„ Smart cast (AI) β€” a local Ollama model (Llama 3.2 3B) reads the story, attributes each line to a speaker, and auto-inserts [Name] tags for you to review. Best for messy/untagged dialogue. Falls back to Quick detect if the model isn't ready.
    • πŸ”Ž Quick detect β€” fast rule-based: quotes + dialogue tags, with gender inference (honorifics + pronouns) to pick matching-gender voices.
    • [Name] markup β€” tag speakers yourself for exact control, e.g. [Pip] "I can do it!". The cast panel lets you assign/preview a voice per character before converting. Works for uploaded files too: multi-voice auto-assigns distinct gender-matched voices to detected characters with zero setup, and πŸ”Ž/πŸͺ„ read the file's text so you can review/override the cast first.
  • 🎡 Background ambience β€” mix a bed under the narration: built-in license-free beds (Soft Rain, Gentle Night, Warm Hum, Dreamy Pad), or upload your own / drop files in data/ambience/. Volume slider + auto-ducking (music dips under speech).
  • Output: chaptered M4B + per-chapter MP3 (zip). Both downloadable.
  • Auto-export to an Audiobookshelf library (path + owner UID/GID configurable; see docker-compose.yml).
  • 🎨 Themes β€” Bookshop Light (aged paper), Bookshop Dark (a lamplit study), and Violet (the original look), switchable from the header and remembered per browser.
  • Accounts β€” everyone signs in with their own username and gets their own stories, saved voices, book library, presets, pronunciations and conversion history. Job history survives restarts.

Tests

The account layer has standalone smoke tests (no pytest needed) β€” see tests/README.md:

pip install fastapi httpx python-multipart itsdangerous   # once, outside the container
for t in tests/test_*.py; do python3 "$t" || break; done

Managing it

cd vellichor-web                # your project directory
docker compose up -d            # start / apply changes
docker compose logs -f          # watch logs
docker compose down             # stop
docker compose up -d --build    # rebuild after editing app/ code

AI Smart cast (Ollama)

Smart cast is optional β€” without it, multi-voice casting uses the rule-based Quick detect instead. To enable it, the ollama service (in docker-compose) runs a local LLM. After the first docker compose up -d, pull the model once:

docker exec vellichor-ollama ollama pull llama3.2:3b

Both models share the GPU; OLLAMA_KEEP_ALIVE=2m unloads the LLM from VRAM after use so Kokoro has room (on an 8 GB card they can't both stay resident). To try a more accurate (heavier) model, pull it and set SMARTCAST_MODEL in .env (e.g. qwen2.5:7b), then up -d.

Expressive voices (Chatterbox)

Pick Chatterbox (expressive) from the TTS engine dropdown for more lifelike, emotional narration. Two extra controls appear:

  • Expressiveness β€” how animated the delivery is (0.5 is a good default).
  • Voice source β€” clone a voice instead of using a preset:
    • Preset β€” clones the Kokoro voice picked on the left (zero setup).
    • Record a voice β€” record yourself in the browser (read the on-screen script for ~15–20s), review, then Use for this book or Save it to My Voices.
    • Upload a clip β€” a clean 5–30s single-speaker clip works best.

Saved voices form a My Voices library reusable across books, stored under data/users/<username>/voices/ β€” your recordings are private to your account. Cloning runs locally on your GPU β€” clips never leave the box.

Notes:

  • The microphone only works on a secure page (https:// or localhost). Over http://<ip>:7777 the browser blocks the mic β€” put Vellichor behind an HTTPS reverse proxy (or use localhost). Uploading a clip works either way.
  • Chatterbox is heavier than Kokoro (more VRAM, slower). It loads/unloads around Kokoro and the Ollama model to share an 8 GB card; its model (~1–2 GB) downloads on first use into data/hf-cache.
  • Multi-voice cast is Kokoro-only for now.
  • Chatterbox is MIT-licensed; outputs carry an inaudible Resemble "Perth" watermark by design (harmless for personal use).

Configuration (.env)

  • VELLICHOR_PASSWORD β€” the initial password for the admin account, applied on first start. Once set, change it in the app (πŸ‘€ β†’ Your password); the env var is then ignored, so a stale .env can't undo it. Leaving it blank disables sign-in entirely (see Security).
  • VELLICHOR_ADMIN_USER β€” optional; the admin's username (default admin). Only used when the account store is first created.
  • SECRET_KEY β€” session-cookie signing key (don't change or logins reset).
  • TRUSTED_PROXIES β€” whose X-Forwarded-For to believe when identifying a caller for rate limiting: private (default β€” loopback + RFC1918, right for a reverse proxy on your LAN/Docker network), none (no proxy; Vellichor is reached directly), or a comma-separated list of IPs/CIDRs.
  • LOGIN_MAX_FAILS / LOGIN_LOCK_SECONDS β€” failed sign-ins before lockout (default 5) and the first lockout's length (default 10s, doubling per failure up to 15 minutes).
  • NOTIFY_URL β€” optional. Set to an ntfy/gotify URL to get a push when a conversion finishes, e.g. http://<server-ip>:8087/vellichor.

Data

Shared, at the top of ./data/:

  • users.json β€” the account list (usernames, roles, scrypt password hashes).
  • jobs/ + jobs.json β€” conversion workdirs and history (each job records its owner; you only ever see your own).
  • samples/, refs/, ambience/, hf-cache/ β€” cached voice samples, cloning references, ambience beds and the Hugging Face model cache. Models download on first use.
  • uploads/ β€” transient scratch space.

Private, one folder per account under ./data/users/<username>/:

  • books/ β€” that person's book library. Drop ebooks in over SMB and they appear in their UI.
  • voices/ β€” their saved My Voices cloning clips.
  • stories.json, presets.json, pronunciations.json β€” their saved stories, setting presets and pronunciation rules.

Deleting an account from the People panel deletes its folder and its conversions; audiobooks already exported to Audiobookshelf stay put.

Notes

  • GPU is used automatically (⚑ GPU chip in the header). Falls back to CPU if the NVIDIA runtime is unavailable.
  • Conversions run one at a time (single worker) so the GPU isn't oversubscribed.

Accounts

  • Everyone gets a username and their own private library. Private: stories, saved voices, book library, presets, pronunciations, conversions and their downloads. Shared: the voice models and preset voices, ambience beds, and the Audiobookshelf library you export into.
  • Admins manage people: πŸ‘€ in the header β†’ People β†’ add someone, reset a password, promote/demote, or delete. There is no self-registration β€” nobody can create their own account.
  • Resetting or changing a password signs that person out of every device immediately. So does deleting their account.
  • Two people exporting the same title by author into the shared Audiobookshelf library would collide; the second export is refused (with a note on the job) rather than overwriting the first.
  • Upgrading from the single-password version: the first start creates the admin account from your existing VELLICHOR_PASSWORD and moves all existing data β€” stories, books, voices, presets, pronunciations, job history β€” into it. Nothing is lost and the move happens once (marked by data/.multiuser-migrated).

Security & deployment

  • Set a password. VELLICHOR_PASSWORD seeds the admin account. If no account has a password, authentication is disabled entirely and every request runs as the admin β€” only do that on a trusted private network.
  • Passwords are stored as scrypt hashes in data/users.json (mode 600), never in plaintext.
  • The login form is rate limited. After LOGIN_MAX_FAILS failures a caller is locked out for a doubling delay (10s, 20s, 40s … capped at 15 min), so a password list gets a handful of guesses and then a few per hour. Signing in successfully clears the record, and a quiet 15 minutes resets the count. Failures are logged (docker compose logs) with the username and IP, so you can see when you're being probed. A username can only be locked for 60s β€” long enough to slow distributed guessing, too short for anyone to lock a housemate out on purpose.
  • Set TRUSTED_PROXIES correctly β€” it's what makes the above work. Behind a reverse proxy, the connection always comes from the proxy, so without it every visitor shares one bucket and one attacker locks out the house. It's also why X-Forwarded-For is only believed from a trusted peer: taking it from anyone would let an attacker send a fresh header per request and never be limited.
  • Don't expose it directly to the internet. Accounts keep housemates out of each other's libraries; they are not a hardened security boundary against the open web. If you need remote access, put it behind a reverse proxy (Nginx Proxy Manager, Traefik, Caddy) with HTTPS and ideally an extra auth layer (e.g. Authelia).
  • Keep .env private (chmod 600). It holds your password and SECRET_KEY and is gitignored β€” never commit it.
  • SECRET_KEY signs the session cookie. Generate one with openssl rand -hex 32. Changing it invalidates existing logins.
  • Uploaded documents (epub/pdf/docx/html/rtf/fb2/…) are parsed server-side, and every account can use the GPU β€” only hand out accounts to people you trust.

License

MIT β€” free to use, modify, and redistribute. TTS by Kokoro-82M (Apache-2.0).

Install Vellichor on Unraid in a few clicks.

Find Vellichor in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for Vellichor Review the template variables and paths Click Install

Related apps

Explore more like this

Explore all

Details

Repository
ghcr.io/woodscode/vellichor-web:latest
Last Updated2026-08-23
First Seen2026-06-30

Runtime arguments

Web UI
http://[IP]:[PORT:7777]/
Network
bridge
Shell
bash
Privileged
false
Extra Params
--runtime=nvidia --memory=12g

Template configuration

WebUI PortPorttcp

Port for the web UI.

Target
7777
Default
7777
Value
7777
App data (/data)Pathrw

Accounts, each user's private library (users/name/), job history, and the model cache (Kokoro). Persisted.

Target
/data
Default
/mnt/user/appdata/vellichor/data
Value
/mnt/user/appdata/vellichor/data
Audiobook library (/library)Pathrw

Optional. Where finished audiobooks are exported, e.g. your Audiobookshelf library share. Leave blank if unused.

Target
/library
Admin passwordVariable

Initial password for the admin account, applied on first start only (change it afterwards in the app: πŸ‘€ β†’ Your password). LEAVE BLANK ONLY ON A TRUSTED LAN β€” blank disables sign-in entirely.

Target
VELLICHOR_PASSWORD
Admin usernameVariable

Username for the admin account. Only used when the account list is first created. Other people are added from inside the app (πŸ‘€ β†’ People).

Target
VELLICHOR_ADMIN_USER
Default
admin
Value
admin
Secret keyVariable

Session-cookie signing key. Generate with: openssl rand -hex 32. Blank = a random key each restart (everyone gets logged out on restart).

Target
SECRET_KEY
Trusted proxiesVariable

Whose X-Forwarded-For header to believe when identifying a caller for login rate limiting. &quot;private&quot; (default) trusts loopback + LAN/Docker ranges, which is right when Vellichor is behind a reverse proxy. Use &quot;none&quot; if nothing proxies it, or list IPs/CIDRs. Wrong value = every visitor shares one lockout bucket, or the limit can be bypassed with a spoofed header.

Target
TRUSTED_PROXIES
Default
private
Value
private
Login max failuresVariable

Failed sign-ins before a caller is locked out.

Target
LOGIN_MAX_FAILS
Default
5
Value
5
Login lockout secondsVariable

Length of the first lockout in seconds; it doubles with each further failure, up to 15 minutes.

Target
LOGIN_LOCK_SECONDS
Default
10
Value
10
Ollama URL (AI Smart cast)Variable

Optional. URL of a separate Ollama container for AI Smart cast, e.g. http://192.168.1.50:11434. Blank disables Smart cast (rule-based Quick detect still works).

Target
OLLAMA_URL
Smart cast modelVariable

Ollama model used for Smart cast (must be pulled in your Ollama container).

Target
SMARTCAST_MODEL
Default
llama3.2:3b
Value
llama3.2:3b
Completion notify URLVariable

Optional ntfy/gotify URL to get a push when a conversion finishes.

Target
NOTIFY_URL
Export owner UIDVariable

UID to own exported audiobook files so your library app can read them (Unraid 'nobody' = 99).

Target
ABS_UID
Default
99
Value
99
Export owner GIDVariable

GID to own exported files (Unraid 'users' = 100).

Target
ABS_GID
Default
100
Value
100
NVIDIA_VISIBLE_DEVICESVariable

GPU(s) to expose. Requires the Nvidia Driver plugin. NO GPU? Clear 'Extra Parameters' (remove --runtime=nvidia) and ignore this.

Default
all
Value
all
NVIDIA_DRIVER_CAPABILITIESVariable

Leave as-is for GPU acceleration.

Default
compute,utility
Value
compute,utility