Plain text was the bootstrap. The point of Aoede is to read the books I actually have, and those are EPUB and PDF, so it imports both now. As far as the rest of the app is concerned, nothing else changed: a book is still chapters, paragraphs, and sentences, no matter what file it arrived in.
That sameness is the whole design. EPUB and PDF are wildly different formats, and PDF in particular is messy: text comes out per page, words break across line ends, headers and footers and bare page numbers litter the content, and paragraphs arrive as hard-wrapped fragments. The importer cleans all of that up, rejoins what belongs together, and uses the document outline for chapters when one exists. A scanned PDF with no text layer is detected and politely refused rather than read aloud as garbage. (Running OCR on those with the Vision framework is on the list for later.)
The payoff is that everything downstream of import, the speech, the highlight, the resume position, never has to know where a book came from. Adding a format is a new importer and nothing else. Reading positions and cached timing are keyed to a hash of the text, so they stay valid as long as the content does.