[Devember2021] [Complete] Audiobook Server

Edit: Demo server url: http://172.105.18.59/

Problem:

I have tons of audiobooks but not a great way to listen to them across multiple devices. Looking at existing projects the most ubiquitous option seems to be booksonic, but as it’s based off of a music server there’s lots of things that don’t really make sense in the land of audiobooks. So I want to build something ground up specifically for audiobooks.

Goals:

MVP:

  • Show a library of books and their metadata
  • Stream books to an http client (using file chunking)
  • Support chapters in books (mp4 chapters)
  • Keep track of the current position in the book
  • Keep a history of positions in the book (I hate it when I’m using audible and I’ve skipped chapters in a book by accident and that causes me to completely lose my position in the book)

Stretch Goals:

  • View by Genre/Author/Series
  • Sort by last played
  • Multiple Users
  • Find and import/set metadata for books by file name/search
  • Bookmarks
  • Chapters using split files
  • Progressive web app support that can cache audiobooks for offline listening

To keep with the spirit of Devember I plan on spending 30ish hours on it but I can’t really do an hour a day so I’m starting early. I don’t foresee the mvp being much of a problem to meet but I have plenty of stretch goals to fill up time and sincerely doubt I’ll finish all of them unless I have an inspired moment or five. Will get a git repo out in my first update and put updates in here semi-regularly (read: sporadically).

7 Likes

Put together a very rough POC of streaming audiobook files to a web client.

Here is the promised git repo:

Update 2:

  • Added a couple of routes to extract the metadata and cover art from the audio file.
  • Setup npm workspaces to make things a little easier to manage.

Update 3:

  • Switched over to sequelize for database connections. Using sqlite as the db backend at the moment, planning to add mysql support as an option.
  • Added some very basic navigation that lists out the books contained in the books directory and allows playing them.
1 Like

Update 4:

  • Added the ability to track the current position in the audiobook
  • Started to implement some ui components

Update 5:

  • Added audiobook metadata to the player page

Well, I disappeared for a bit (weeks), but that was the whole point of starting early. Code-wise pretty small update to get back into the swing of things. But I wanted to give a summary of where I’m at in honor of there being an official devember2021 thread.

Server:
Currently the server is able to get a list of audiobook files from the assets folder. It can then read those files for metadata and album art and serve those details on dedicated routes. It can chunk an audiobook file and serve it as a standard http get route. Finally the server can store, update, and serve positions in an audiobook.

Client:
The client can show the title and album art of book files in the server’s asset directory. It can play an audiobook from the server. It can update the position in the audiobook in the server. It can restore the position from the server in the audiobook.

This puts the project pretty close to the MVP with just chapter support and position history needing to be implemented. The chapter data already comes from the server in the metadata call so it just needs to be implemented in the client. The history of positions is also being sent to the client with the current position route from the server and just needs to be implemented in the client.

Here are some screenshots of the current ui (current ui is pretty rough):

Update 6:

  • Add position history to the client.
  • Add chapter history to the client.

Well, just hit the MVP by adding these two features. At this point I would classify the project as usable but still has a less than good user experience. The code is pretty ugly right now, so my next step is going to be cleaning up the code. Things like using constants for values, consistent naming, organizing css. Once I’ve cleaned up the code some I’ll go ahead and add my first version tag. Then I’ll start going through my stretch goals and picking things to implement that make this project more usable for myself.

Update 7:

Devember is almost over, so of course this means that I need to get at least one update that was actually done in December…

  • The code base has been cleaned up to a point that I won’t hate myself updating it further.
  • Now using dark mode!
  • Docker builds

I haven’t been the most active on this project, but I have met my goals of putting in at least 30 hours into it and reaching the MVP state. So while I wish I had kept more on top of it and gotten at least a couple of the stretch goals done, I do feel that I can consider the Devember part of the project complete.

I still have plenty of work to do to get this project where I truly want it and to start actively using it instead of audible and I don’t plan on stopping development on it. The ongoing pace will probably be pretty similar as far as updates to the project go; trying to get some work done on it every 2-4 weeks.

Tomorrow i’m going to get a demo server running for the sake of Devember judging and will update the thread with the details.

Update 8:

  • Had forgotten to update the code to not hard-code localhost as the url for the api and now it should work on external ip addresses
  • Setup github actions to create docker images and push them to the github registry
  • Got a linode up and running as a demo server

The demo server is running at http://172.105.18.59/ and has a public domain audiobook to try out the app with.

Update 9:

Turns out, I haven’t abandoned the project! Life threw a couple curve balls but I’m starting to get back to working on this.

Right now I’m focusing on offline support as this is the biggest factor in using the app full time myself. I’ve started in on implementing the pwa service worker, mostly copy pasting some code. I’m not overly familiar with the caching workflow and expect to have some hiccups around the app being a single page app.

The biggest issue is going to be writing logic around the service requests. My plan is to use the browsers indexedDb to store the various data i need and then implement syncing logic to that. The idea being that the sync can fail if i go offline and once you go back online it’ll send all of the positions from the browsers database to the server/download any new positions from the server. Luckily keeping track of multiple positions in a book actually makes this easier because I don’t really have to decide which position is the most accurate. Instead I can just add all of the positions to both the client and server, with the current position just being the most recently timestamped position and if that position is incorrect the user can just select the correct one.

As for storing the book itself I’ve added some rough logic to store a book in the indexedDb as well, however i haven’t implemented reading from it instead of the web. Having to rely on the in browser db is still an unfortunate reality due to there not really being a standard cross browser way to access the filesystem. The latest proposal is the File System Access API and I may attempt to implement it as an alternative method of storing books on a client device, but it is only supported on chromium based browsers at the moment and I’m not sure what the limitations are on it.

Another thing i’ve looked into is storing the position data in the book files themselves when using .m4b files, this would have theoretically made it easier to sync the file to other players. Interestingly if you look up what a .m4b file is most of the immediately available information suggests that it allows storing bookmarks in the file. Turns out that isn’t exactly true. A .m4b file is just another name for a .mp4 file in the same way a .m4a file is. The confusion seems to stem from itunes initally using the .m4b extension to decide whether it should give users the option to make bookmarks, however the bookmarks themselves were not stored in the .m4b file. So the best bet is probably just to allow downloading the position data as a json, and possibly supporting some formats made by common players as well. There’s also the possibility of adding the position data as xmp tags in the audiobook file but I don’t think any players do that currently or if there’s even any good xmp tags for that kind of thing.

Finally, I made a small update to the cover art retrieval code. The way I was getting the cover art with ffmpeg was deprecated a while back so I updated it to work with newer versions of ffmpeg that no longer have the method I was using.