EPUB to audiobook
Turns an EPUB into a single chaptered m4b with cover art and proper metadata. Built for me, and built as its own thing rather than a feature of the library app it obviously belongs in.
Why this is not a MyBooks feature
I'd rather listen than read, and MyBooks is a library application that already knows where all my books are. Generating audiobooks inside it is the obvious next feature, and I still intend to add audiobook support there. Generating the audio is the part I kept out.
A hosted, multi-tenant product that manufactures a new derivative work from a customer's copyrighted file, on the operator's own infrastructure, at whatever volume its customers ask for, sits somewhere quite different from a tool a person runs on their own machine against their own files. Audiobook rights are also routinely licensed separately from ebook rights, so "they own the ebook" doesn't settle the question the way it first appears to.
I'm not a lawyer and this isn't a legal conclusion. It's a judgment about risk posture, settled before I wrote a line of it, when the cost of being wrong was a design decision rather than a product I'd have to unpick.
So the capability lives here, locally, run by the person who owns the
file. MyBooks will learn to catalog and play an .m4b, which
is a storage and playback problem and carries none of this.
Same shape of call as pausing MyBooks over the Drive scope changes. In both cases the code wasn't the hard part and building it was never in doubt. The question was whether it should exist in that position at all.
What it does
Input is an EPUB, or a folder of .docx files with one chapter
each. From an EPUB it reads the title, author, cover, and chapter list out
of the spine and the NCX, and it skips the table of contents page.
From a folder it takes the chapter title from the first heading, or
failing that from the filename with leading numbers stripped. Metadata
comes from a book.json and a cover.jpg if
they're there.
Output is one .m4b with chapter markers, embedded cover, and
the tags the players actually read. Narration comes from a local neural
TTS model and ffmpeg does the muxing. It runs from a PySide6 window or
from the command line, with a --dry-run that just lists the
chapters it found so you can check the parse before committing an
afternoon of CPU to it.
Decisions worth explaining
It resumes
Synthesis runs at roughly 0.3 to 0.6 times realtime on a CPU, so a ten-hour book is most of a day. Every chapter's audio gets cached as it's produced. A crash, a failed mux, or a closed laptop costs you the chapter you were on instead of the eight hours behind it.
The launcher pins the interpreter
The TTS library doesn't run on Python 3.13 or newer, and when it fails the
error isn't obviously about versions. So the Windows launcher pins 3.12
rather than trusting whatever python resolves to on PATH.
Three lines of batch file, and it saves a confusing hour later.
The license terms are written down
The code is MIT. The README also carries a table of every dependency and its license, flagging the two that come with real conditions if the project is ever bundled into a shipped binary. Qt under the LGPL has to stay dynamically linked and replaceable, and espeak-ng is GPL, though it's only ever invoked as a separate process.
None of that is required for a personal tool. I wrote it down because working it out later is how these things get missed.
The rough edge
The model reads what it's given, so numbers, dates, and abbreviations come out as written. A normalization pre-pass would fix most of it and is the next thing going in.
The sample chapter in the repository is from my own novel. It's the one piece of text I can hand out as test input without asking anyone.