epuboverlay is a powerful tool designed to generate narrated EPUB 3 files with Media Overlays using F5-TTS, Kokoro, or Pocket-TTS, and to extract synchronized audio tracks (M4B/M4A/MP3) and standard subtitle files (ASS, SRT, VTT, etc.) from narrated e-books. It also includes capabilities to parse and extract chapters/previews from other document formats like PDF, DOCX, Markdown, and TXT.
Instead of restricting you to one format, epuboverlay bridges the gap between interactive e-book reading and standalone audio listening:
- 📖 Generation (EPUB Enhancement): It segments XHTML text into sentences/clauses, wraps them in visual
<span id="...">tags, synthesizes matching spoken narration in your chosen voice, generates standard W3C SMIL sync maps, and packages everything back into a fully validated EPUB 3 container. This allows modern e-readers (such as Apple Books, Kobo, or Thorium Reader) to highlight sentences in real-time sync with the audio. - 🎵 Extraction (Standalone Playback): It unpacks existing narrated EPUBs, extracts their audio tracks, and generates matching subtitle sync files (with options like chapter merging, custom cover art, and subtitle centering/burning) for playback on standard media/music/video players (e.g. Poweramp).
- System Architecture
- Key Features
- Prerequisites
- Installation
- Usage
- Configuration Reference
- Under the Hood: Pipeline Phases
- Caching & Resume State
- REST API Documentation
- Testing & Verification
- License
The following diagram illustrates the dual workflows supported by epuboverlay:
flowchart TD
subgraph Gen ["1. Media Overlay Generation"]
A1[Input EPUB] --> B1[Pipeline Orchestrator]
B1 --> C1[Extract XHTML Chapters & Manifest]
C1 --> D1[Text Segmentation & HTML Spanning]
D1 --> E1["Parallel Audio Synthesis (F5-TTS / Kokoro / Pocket-TTS / Dummy)"]
E1 --> F1[Accumulate Timing Offsets from Audio Frames]
F1 --> G1[Concatenate WAVs & Convert to M4A/AAC via FFmpeg]
G1 --> H1[Generate SMIL Sync Maps]
H1 --> I1[Update OPF Metadata & Duration Properties]
I1 --> J1[Repackage EPUB Container]
J1 --> K1[Narrated EPUB 3 Output]
end
subgraph Ext ["2. Media Overlay Extraction"]
A2[Narrated EPUB 3 Input] --> B2[Unpack EPUB & Parse SMIL Overlays]
B2 --> C2[Align XHTML Text Spans with SMIL Timings]
C2 --> D2{Merge Chapters?}
D2 -- Yes --> E2[Merge Audio & LRC Timings via FFmpeg]
D2 -- No --> F2[Export Per-Chapter Audio & LRC Pairs]
E2 --> G2[Extracted Audio + LRC Package]
F2 --> G2
end
- 🚀 Multi-Engine Speech Synthesis: Supports generation via F5-TTS, Hexgrad Kokoro-82M, Pocket-TTS, or a lightweight Dummy synthesizer.
- 🎭 AI Voice Cloning & Blending:
- Zero-shot voice cloning using F5-TTS or Pocket-TTS (Kyutai Labs). Provide a short reference audio clip (.wav) to clone your own voice.
- Voice formula blending using Kokoro (e.g.
af_heart*0.6+af_sky*0.4) with slider controls, presets, and real-time voice previews.
- 🎙️ Voice Playground & Voice Library: Integrated voice testing area with text normalization and real-time playback, paired with a multi-tab voice library to upload, record, and preview reference audio clips.
- 📝 Advanced Text Normalization: Built-in pipeline with customizable settings for numeral expansion, contraction resolution, heteronym resolution, punctuation harmonization, and custom lexicons.
- 📂 Multi-Format Preprocessors: Built-in document preprocessor factory to extract chapter structure, previews, and character counts from EPUB, PDF, DOCX, Markdown, and TXT files.
- ⚡ Parallel Synthesis & Multiprocessing: Utilizes a multiprocessing backend to process text segments in parallel, saturating modern GPUs and speeding up narration generation by up to 2x–3x compared to sequential inference.
- 💾 Persistent & Scoped Cache: Hashes input EPUB content and synthesizer configurations, caching assets in structured subdirectories. Features automated legacy cache migration to gracefully move older flat structure caches.
- 🔄 Job Resumption with Config Editing: Failed or cancelled generation jobs can be resumed from the Web UI or via the REST API, with the option to update execution parameters (like device, speed, concurrency, NFE, or model compilation) without starting over.
- 📤 Extensible Media Overlay Extraction: Extract audio tracks as M4B (with metadata, chapter markers, and custom cover art) or M4A (without chapters), and subtitles in multiple formats (ASS, SRT, VTT, TTML, SBV, LRC, TXT) with options to center or burn subtitles directly into generated MP4 video tracks.
- 📖 Table of Contents (TOC) Rebuilding: Dynamic reconstruction of NAV (XHTML) and NCX (XML) Table of Contents when flat EPUBs undergo heuristic splitting during media overlay generation.
- 🖥️ Interactive Web Dashboard: A premium dark-mode SPA featuring drag-and-drop uploads for both generation and extraction, live system hardware metrics (CPU, RAM, Disk, GPU/VRAM), chapter-by-chapter progress visualizers, active job control (cancellation, resumption), streaming audio previews, and direct download links.
- 📊 CLI Progress Tracking: Detailed CLI output with subcommands, progress bar, chapter/chunk indices, elapsed time, characters processed, and live ETA calculations.
- 🔄 CLI & Web Syncing: The Web Dashboard detects and monitors background CLI execution jobs in real-time, allowing you to run jobs on headless servers and monitor them via the web interface.
- Python: 3.8 or higher.
- FFmpeg: Required to compress raw WAV audio output into M4A/AAC or MP3.
# Debian/Ubuntu sudo apt update && sudo apt install ffmpeg # macOS (using Homebrew) brew install ffmpeg # Windows # Download binaries from ffmpeg.org and add them to your System PATH.
- Dummy Mode: Requires zero machine learning libraries and generates silent placeholders instantaneously. Great for debugging and validating EPUB packaging.
- F5-TTS Mode: Requires
f5-tts, PyTorch, and a compatible backend (CUDA for Nvidia GPUs, MPS for Apple Silicon, or CPU fallback). - Kokoro Mode: Requires
kokoro>=0.7.16(Hexgrad Kokoro-82M), PyTorch, and a compatible backend (CPU/GPU). - Pocket-TTS Mode: Requires
pocket-tts,scipy, andnumpyfor CPU-efficient local zero-shot voice cloning.
- DOCX Mode: Requires
python-docxto extract chapters and text from Microsoft Word documents. - PDF Mode: Requires
pypdfto extract page-by-page text from PDF documents.
-
Clone the repository:
git clone <repo-url> cd epuboverlay
-
Set up a virtual environment:
python3 -m venv .venv source .venv/bin/activate -
Install the package and dependencies:
# Install the package and all its synthesizer backend dependencies (f5-tts, kokoro, pocket-tts) pip install -e . # (Optional) Install document preprocessor dependencies (python-docx, pypdf) pip install -e .[all]
The Web Dashboard is the easiest way to manage your book generation jobs, view live statistics, preview generated audio, extract M4A/MP3s + LRCs, and download the finished product.
Start the dashboard using the console script:
epuboverlay-web --port 8765 --host 127.0.0.1Once started, navigate to http://localhost:8765 in your browser.
- Generate Overlays: Drag and drop your EPUB file, select your synthesizer backend, configure parameters (such as voice blends, speed, concurrency, and model compilation), and press Start Generation.
- Extract Audio & Lyrics: Use the Extract MP3 + LRC tab to upload an existing narrated EPUB, choose whether to merge chapters, and extract a ZIP package containing the audio files and corresponding
.lrcsync maps. - Job Resumption: Interrupted, cancelled, or failed jobs can be resumed. Clicking Resume opens a configuration dialog where you can tweak inference settings before continuing the job.
- Live System Metrics: Watch real-time CPU, RAM, Disk, and GPU performance stats to monitor execution.
- Audio Previews: As soon as a chapter is fully synthesized, it will list on the dashboard for instant playing.
- Download: Download completed synced EPUBs directly from the dashboard.
The CLI uses subcommands to handle generation and extraction.
# General help
epuboverlay --help
# Subcommand-specific help
epuboverlay generate --help
epuboverlay extract --helpRun the generator via the console script or python module syntax:
# Option A: Using the console script
epuboverlay generate --epub my_book.epub -o my_book_narrated.epub --synthesizer dummy
# Option B: Using Python module execution
python -m epuboverlay generate --epub my_book.epub -o my_book_narrated.epub --synthesizer dummypython -m epuboverlay generate \
--epub path/to/input.epub \
-o path/to/output_synced.epub \
--synthesizer f5-tts \
--ref-audio path/to/voice_clip.wav \
--ref-text "This is the text matching my short voice clip." \
--device cuda \
--concurrency 2python -m epuboverlay generate \
--epub path/to/input.epub \
-o path/to/output_synced.epub \
--synthesizer kokoro \
--voice af_heart \
--concurrency 2python -m epuboverlay generate \
--epub path/to/input.epub \
-o path/to/output_synced.epub \
--synthesizer pocket-tts \
--ref-audio path/to/voice_clip.wav \
--concurrency 2While running, the CLI outputs a detailed status bar:
Orchestrating EPUB Media Overlay generation...
Input EPUB: my_book.epub
Output EPUB: my_book_narrated.epub
Synthesizer: f5-tts
Concurrency: 2
[Chapter 1/14] [Chunk 4/30] |████░░░░░░░░░░░░░░░░| 20.3% Elapsed: 1m14.2s ETA: ~4m51.8s (chapter_01)
Extract audio tracks (M4B, M4A) and subtitle files (ASS, SRT, VTT, etc.) from an EPUB that already has Media Overlays:
# Extract separate M4B chapter audio and ASS subtitles (default)
epuboverlay extract --epub my_book_narrated.epub -o output_dir/
# Extract and merge all chapters into a single M4B audiobook file with chapter markers, metadata, and custom cover art
epuboverlay extract --epub my_book_narrated.epub -o output_dir/ --merge --cover-art path/to/cover.jpg
# Export to SRT and VTT formats, center subtitles, and convert to MP4 video with embedded subtitles
epuboverlay extract --epub my_book_narrated.epub -o output_dir/ --formats "srt,vtt" --center --mp4-video --embed-subtitles
# Extract only subtitles (LRC, SRT) without extracting/encoding audio
epuboverlay extract --epub my_book_narrated.epub -o output_dir/ --formats "lrc,srt" --no-audioThe following table lists the settings available in the generation CLI/Web API:
| Option | Shortcut | Description | Default | Availability |
|---|---|---|---|---|
--epub |
[Required] Absolute or relative path to the input EPUB 3 file. | CLI & Web | ||
--output-epub |
-o |
[Required] Path where the output synced EPUB will be saved. | CLI & Web | |
--synthesizer |
-s |
Synthesis model to use: f5-tts, kokoro, pocket-tts, or dummy. |
f5-tts |
CLI & Web |
--ref-audio |
-a |
Path to your reference voice sample (Required for f5-tts and pocket-tts). |
CLI & Web | |
--ref-text |
-t |
Verbatim text transcript of your reference audio clip (Required for f5-tts). |
CLI & Web | |
--voice |
Name of the built-in Kokoro voice to use (e.g. af_heart). |
"" |
CLI & Web | |
--voice-formula |
Custom voice mix formula for Kokoro (e.g. af_heart*0.6+af_sky*0.4). |
"" |
CLI & Web | |
--lang-code |
Kokoro language code (e.g. a for American English, b for British English). |
a |
CLI & Web | |
--compile |
Optimize inference speed by compiling the model via torch.compile (f5-tts only; the first chunk compilation takes 1-2 minutes). |
False |
CLI & Web | |
--device |
Hardware device: cuda, cpu, or mps. |
None (auto-detects) |
CLI & Web | |
--speed |
Rate of generated speech (e.g., 1.2 for 20% faster). |
1.0 |
CLI & Web | |
--max-chars |
Maximum character length of text sent to the synthesizer at once. | 150 |
CLI & Web | |
--frame-rate |
Audio sampling rate in Hz. | 24000.0 |
CLI & Web | |
--concurrency |
-c |
Number of concurrent processes/workers for synthesis. | 2 |
CLI & Web |
--cache-dir |
Custom folder path for intermediate chapter audio/SMIL caching. | ~/.epuboverlay/cache/... |
CLI Only | |
--nfe-step |
Number of inference steps for F5-TTS model (min: 10, max: 64). | 32 |
CLI & Web | |
--pocket-voice |
PocketTTS preset voice name (optional). | "" |
CLI & Web | |
selected_chapters |
JSON list of chapter idref strings to selectively process (optional). |
"" |
Web API Only |
| Option | Shortcut | Description | Default | Availability |
|---|---|---|---|---|
--epub |
[Required] Path to the input EPUB 3 file with media overlays. | CLI & Web | ||
-o, --output |
[Required] Output directory for extracted audio and subtitle files. | CLI Only | ||
--merge |
Merge all chapters into a single audio + subtitle set. | False |
CLI & Web | |
--formats |
Comma-separated list of subtitle formats to export (choices: ass, srt, vtt, ttml, sbv, lrc, txt, or none). |
ass |
CLI & Web | |
--center |
Center subtitles horizontally and vertically. | False |
CLI & Web | |
--mp4-video |
Convert the extracted audio to an MP4 video using an ultra-low-bitrate static black video. | False |
CLI & Web | |
--no-audio |
Do not extract/generate audio files, only extract subtitles. | False |
CLI & Web | |
--embed-subtitles |
Embed/burn subtitles into the MP4 video (only if --mp4-video is specified). |
False |
CLI & Web | |
--cover-art |
Path to an optional cover art image to embed in the audiobook (.m4b). | CLI & Web | ||
--audio-format |
Audio container format — m4b (with chapters) or m4a (without chapters). |
m4b |
CLI & Web |
The pipeline decompresses the input EPUB ZIP archive into a temporary folder structure. It reads META-INF/container.xml to locate the OPF package description file (content.opf). It parses the OPF manifest to map spine items in reading order, ignoring non-HTML contents (like cover pages, CSS files, and image assets).
To map audio to text elements, the raw XHTML document is parsed using python's xml.etree.ElementTree.
- Block Element Extraction & Preprocessing: The parser identifies structural blocks (
<p>,<li>,<h1>to<h6>,<blockquote>, etc.). If heuristic splitting is used for flat EPUBs, it runs paragraph line-wrap merging and scans for heuristic anchors to segment the document, rebuilding the NAV (XHTML) and NCX (XML) Table of Contents. - Abbreviation Protection: Sentence parsing (split_into_sentences) protects common abbreviation suffixes (e.g.,
Mr.,Dr.,vs., initials likeA.) to prevent premature splits. - Clause Splitting: If a sentence exceeds the
--max-charslimit (150 chars), it is split on punctuation marks (,,;,:,—) using chunk_text to keep synthesis chunks natural and brief. - Spanning: The original text content inside each HTML block is replaced with
<span id="epuboverlay-s-N">tags wrapped around each chunk. - Entity Preservation: Named HTML entities (such as
,“,”) are temporarily mapped to XML-safe numeric character references (e.g. ,“) before XML parsing to avoid XML serialization failures.
- Text chunks are fed into the selected synthesizer.
- The F5TTSSynthesizer calls
F5TTS.inferusing the reference audio sample and transcription, returning float32 audio arrays and the precise generated sample length. - The KokoroSynthesizer handles generation using the lightweight Hexgrad Kokoro-82M engine.
- The PocketSynthesizer uses Kyutai Labs' lightweight CPU zero-shot voice cloning model.
- If
--concurrencyis greater than 1, synthesis of separate text spans within the chapter is executed in parallel using aThreadPoolExecutor(or via separate processes depending on execution constraints).
-
Timestamp Accumulation: Because synthesizers output exactly the audio generated for the provided text, the system calculates the duration of each chunk:
$$\text{Duration (seconds)} = \frac{\text{Generated Audio Frames}}{\text{Frame Rate (Hz)}}$$ The start and end times for each span ID are compiled sequentially by accumulating these durations. - Audio Concatenation: Individual WAV chunks are merged in memory or compiled via temporary files to form a continuous chapter-length audio stream.
-
M4A/AAC Compression & Audiobook Export: The concatenated WAV is processed and compressed to standard M4A (AAC-LC) using
ffmpegto reduce package file sizes. It also supports packaging into an M4B audiobook container complete with metadata, chapter designations, and custom cover art.
- SMIL Sync Maps: For each chapter, a W3C SMIL document (
smil_c1.smil) is generated mapping each visual span ID to its exact beginning/end time offset inside the chapter's M4A/MP3 file. - OPF Updates:
- Manifest entries for SMIL overlays and M4A/MP3 tracks are added.
- The
media-overlayattribute is added to each chapter's<itemref>element to link it to the SMIL map. - Global book duration and individual chapter durations are registered inside the
<metadata>tags using standard<meta property="media:duration">fields. - The
media:namespace prefix is declared in the root<package>element.
- Archive Repackaging: All files are re-zipped into a standard EPUB container. Crucially, the
mimetypefile is written uncompressed as the first entry in the ZIP directory to satisfy EPUB validation specifications.
To make the tool robust against interruptions:
- An MD5 hash of the original EPUB binary is calculated.
- A configuration hash is calculated from the synthesizer class, speed, frame rate, max characters, reference audio metadata (size, path, modification time), and reference transcript text.
- These hashes compose a unique cache directory:
~/.epuboverlay/cache/<epub_hash>_<config_hash>/. - Inside this directory, the extracted EPUB workspace is preserved. Completed chapters have their audio written to
{chapter_rel_dir}/audio/audio_{idref}.m4aand sync maps to{chapter_rel_dir}/smil_{idref}.smil(matching their relative chapter location within the EPUB container). - On startup, the pipeline automatically detects and migrates legacy cache files (previously stored under the main OPF directory) to the new subdirectory-scoped structure.
- When running, the pipeline checks for both files and queries the duration of the SMIL mapping. If present, it skips synthesis for that chapter, adds its duration to the global sum, updates the OPF manifest references, and moves on immediately.
The FastAPI backend exposes the following REST endpoints:
- Route:
GET /api/jobs - Response: Array of job objects. Includes active CLI processes detected on the host system.
- Route:
POST /api/jobs - Content-Type:
multipart/form-data - Parameters:
epub: File (EPUB binary, required)synthesizer: String (f5-tts,kokoro,pocket-tts, ordummy, defaultf5-tts)ref_audio: File (WAV/MP3/M4A reference sample, required forf5-ttsandpocket-tts)ref_text: String (reference text transcript, required forf5-tts)ref_id: String (saved reference voice ID, optional)voice: String (name of Kokoro voice, e.g.af_heart)voice_formula: String (Kokoro voice mix formula, e.g.af_heart*0.6+af_sky*0.4)lang_code: String (Kokoro language code, defaulta)pocket_voice: String (PocketTTS preset voice name, optional)device: String (cuda,cpu,mps, or empty)speed: Float (default1.0)max_chars: Integer (default150)frame_rate: Float (default24000.0)concurrency: Integer (default2)nfe_step: Integer (default32)compile: Boolean (defaultfalse)selected_chapters: String (JSON array of chapter idref strings, optional)
- Response: Details of the created job. Includes estimated audiobook duration and total character counts.
- Errors: Returns
409 Conflictif another job is already running.
- Route:
GET /api/jobs/{job_id} - Response: Status, details, and progress dictionary for the requested job.
- Route:
POST /api/jobs/{job_id}/cancel - Behavior: Signals a running process to cancel. If a CLI job, sends a
SIGTERMkill signal to the process ID.
- Route:
GET /api/jobs/{job_id}/download - Response: Synced EPUB binary attachment.
- Errors:
400 Bad Requestif job is not completed.
- Route:
GET /api/jobs/{job_id}/audio/{chapter_idref} - Response: Audio stream (supports M4A/MP3) for inline browser player.
- Route:
GET /api/jobs/{job_id}/events - Response: Server-Sent Events stream delivering real-time status payloads:
{ "id": "job_id", "status": "running", "progress": { "phase": "synthesizing", "chapter_index": 2, "chapter_total": 12, "chapter_name": "chapter_02", "chunk_index": 14, "chunk_total": 45, "elapsed_seconds": 45.2, "message": "Synthesizing chunk...", "overall_percent": 21.5, "synthesis_elapsed_seconds": 12.4, "total_chunks_to_synthesize": 120, "chunks_processed_so_far": 24, "total_characters": 85000, "estimated_total_hours": 1.57, "audiobook_duration_seconds": 0.0, "estimated_remaining_seconds": 182.4 } }
- Route:
POST /api/jobs/{job_id}/resume - Content-Type:
application/x-www-form-urlencodedormultipart/form-data - Parameters (all optional):
synthesizer: String (f5-tts,kokoro,pocket-tts, ordummy)device: String (cuda,cpu,mps, or empty)speed: Floatmax_chars: Integerframe_rate: Floatconcurrency: Integernfe_step: Integercompile: Booleanvoice: Stringvoice_formula: Stringlang_code: String
- Response: Details of the updated and restarted job.
- Errors: Returns
400 Bad Requestif the job is not in a failed/cancelled state, or409 Conflictif another job is already running.
- Route:
DELETE /api/jobs/{job_id} - Response:
{"status": "deleted"}if successful. - Errors:
400 Bad Requestif the job is not found or is currently running.
- Route:
POST /api/jobs/{job_id}/convert-audio - Content-Type:
multipart/form-data - Parameters:
merge: Boolean (defaulttrue)formats: String (comma-separated subtitle formats, default"ass")center: Boolean (defaulttrue)mp4_video: Boolean (defaultfalse)embed_subtitles: Boolean (defaultfalse)include_audio: Boolean (defaulttrue)audio_format: String (m4borm4a, defaultm4b)cover_art: File (optional cover art image upload)
- Response: A ZIP archive containing extracted or merged audio+subtitle tracks.
- Route:
POST /api/chapters - Content-Type:
multipart/form-data - Parameters:
epub: File (uploaded EPUB, DOCX, PDF, Markdown, or TXT file, required)
- Response: Array of parsed document sections including
idref,title,char_count, andpreview.
- Route:
POST /api/extract - Content-Type:
multipart/form-data - Parameters:
epub: File (EPUB binary with media overlays, required)merge: Boolean (defaultfalse)formats: String (comma-separated subtitle formats, default"ass")center: Boolean (defaultfalse)mp4_video: Boolean (defaultfalse)embed_subtitles: Boolean (defaultfalse)include_audio: Boolean (defaulttrue)audio_format: String (m4borm4a, defaultm4b)cover_art: File (optional cover art image upload)
- Response: A ZIP archive containing extracted per-chapter or merged audio+subtitle files.
- Route:
GET /api/stats - Response: Live utilization metrics for CPU, RAM, Disk space, and GPU specifications (VRAM usage, percentage, temperature parsed via
nvidia-smi).
Comprehensive test suites are included to verify individual components. Tests cover sentence boundary detection, abbreviation protection parsing, timing offset accumulation, HTML entity processing, cache persistence, full end-to-end simulated EPUB generation, and media overlay extraction.
To run the automated unit tests, run the following command in the project root:
python -m unittest discover -vAll test cases are defined under the tests/test_pipeline.py and tests/test_extract.py modules.
This project is licensed under the Apache License, Version 2.0. See the LICENSE file for the full license text.
