Show HN: @smoores/epub, a JavaScript library for working with EPUB publications (npmjs.com)
Howdy! I've just written a blog post about this, and I figured I would share it here: https://smoores.dev/post/announcing_smoores_epub/. As I've been working on Storyteller[1], I've been developing a library for working with EPUB files, since that's a large amount of the work that Storyteller does. After a friend asked for advice on creating EPUB books in Node.js, I decided to publish Storyteller's EPUB library as a standalone NPM package. I really love the EPUB spec, and I think the Node.js developer community deserves an actively maintained library for working with it!
45 comments
[ 3.0 ms ] story [ 126 ms ] threadI'm sure the march of LLMs will continue eating into this pie, and that's a good thing (most of it is a distraction from the real work), but I love polishing a library on my laptop in a cafe. It's like working on a painting or something.
By contrast, this was mostly just moving a file around and then writing documentation and cleaning up the public API. I rather enjoy thinking about and modeling library APIs in general, so I actually had a lot of fun with it!
[1]: https://www.npmjs.com/package/@nytimes/react-prosemirror
Interesting choice to publish from the storyteller "monorepo." Is that because it evolved in situ, and you've no impetus to incur the overhead of extraction?
Right, this was actually just a file within the Storyteller web package to start. It was fairly well defined, and so pretty easy to pull out into another package in the monorepo, but Storyteller is the primary consumer at the moment, and I want to be able to develop them in sync. Plus, it provides a great test bed for development of the library!
edit: I forgot to mention that the eventual goal is to (hopefully!) publish this package as @storyteller/epub, along with any other packages that end up split out of Storyteller. That will probably include at least a @storyteller/synchronize and a @storyteller/cli.
Unfortunately, someone seems to have snagged the @storyteller org on NPM several years ago and left it to languish without really using it, so I'm waiting to see whether GitHub will consider this squatting and transfer the org to me.
I've also tried reaching out to the developer that owns the org, but they don't seem to have been active on GitHub or NPM for the past 5 years or so, and my only real strategy for reaching out to them was to open an issue on one of their other GitHub projects!
An EPUB is just a ZIP archive of XML and XHTML files (plus other assets, like images). Partly, I suspect, because of the dearth of actively maintained open source projects in the space, and partly because of the nature of tech in the book publishing industry, EPUB generation software used by authors and publishers often messes up this spec, which means that EPUB readers sometimes need to have fairly complex fallback logic for trying to figure out how to render a book. Also, because EPUBs are ZIP archives, some readers may either unzip the entire book into memory or "explode" it into an unzipped directory on disk, both of which may result in some slowness, especially if the book has lots of large resources. The newest Brandon Sanderson novel, for example, is ~300MB _zipped_.
Additionally, and perhaps more importantly, EPUBs (and I believe MOBIs as well) represent content as XHTML and CSS, which means that readers very often need to use a browser or webview to actually render the book. Precisely how they deliver this content into the webview can have a huge impact on performance; most browser don't love to be told to format entire novels worth of content into text columns, for example.
Terry Pratchett books are notorious for that. Some tools EPUB authoring tools artificially introduce breaks, but you can't rely on them.
So it's possible :)
I'm now actually working on a Calibre-Web change to strip the audio and media overlay from the books it serves via OPDS.
Then I'll need to tackle cross-device progress sync. This turned out to be surprisingly tricky.
So I'm trying to design a progress sync protocol. My current idea is to just use several words from the text itself to unambiguously pinpoint the position within a section (chapter).
More info here: https://smoores.gitlab.io/storyteller/docs/administering#inv...
I don’t get around working on it right now, but maybe there’s something useful there for you.
Do you think it might be a good idea to set up a site to share the aligned overlays from Storyteller? This way, people won't have to waste CPU/GPU time re-aligning the same files over and over again.
It should be OK from a copyright perspective, as it won't be distributing any copyrighted material, only the media overlay information.
A challenge I foresee is that the media overlays are only reusable if you have the exact same input EPUB file, and have processed it with Storyteller to mark up the sentence boundaries. EPUBs have unique identifiers, though, so maybe this would be fine! We’d need to add a new processing flow to Storyteller, but it should be doable.
Feel free to hit me up in the Storyteller chat if you want to discuss more! Thanks for sharing this idea!
It's highly likely to be globally unique, and it can also help with the missing forewords/afterwords/bonus content sections.
In addition, you can also add fuzzy matching for the title.
The entire Great Books of Western Civilization are on both, and I know I'd make more progress on reading it if I could hand off between reading and listening more easily!
Maybe “syncing ebooks with audiobooks” would be clearer? Also entirely possible this is just a me problem, not a general one.
Really cool project!
(I would also use the term “synchronising” rather than “syncing”, because “syncing” has kinda developed a meaning of its own, even though it came from the same place. And use the word “with”, and try to use the words “text”. “Synchronising ebook text with audiobooks” is way better than “syncing ebooks and audiobooks”.)
- Switch between reading and listening whenever you want
- Follow the text while listening to audiobooks
- Enjoy audiobooks while reading along with the text
Or even simpler:
- Read or listen - switch anytime
- See the words as you listen
- Listen while you read along
EDIT: storyteller is super interesting. Dug around a bit in the code and see that you do seem to be using readium for some things, so I must just be missing some nuance.