Fixing  Jellyfin iOS Audiobook Streaming

Fixing Jellyfin iOS Audiobook Streaming

Contributing to FOSS without coding

Back at the end of January, I was devouring Cory Doctorow’s latest audiobook: The Lost Cause. I was energized by the ideas and couldn't stop telling my girlfriend about them. She had enough and was ready to give it a listen herself. Normally, I’d just give her access to my libro.fm account, but I was still listening to it and wasn’t sure if it might lose my spot in the book if two people listened at once.

I’ve been getting into self-hosting and recently set up a Jellyfin server (Netflix but with your own videos) via Coolify. Jellyfin supports audiobooks, and since I bought a DRM-free copy of the audiobook, I have the liberty to drop the m4b file onto my server and stream it from anywhere. I did just that, streaming the audiobook to my laptop for a quick test, and told my girlfriend she should be able to listen to the book from the Jellyfin app on her upcoming night train trip.

The self-hosting gods didn’t look favorably upon us that day. The app was giving her an error when playing the audiobook.

This client isn’t compatible with the media and the server isn’t sending a compatible media format

On my Pixel 7, the app was working flawlessly, and it worked fine on Firefox Android too. Attempts to play via her iPhone’s browser were equally futile. I pulled out my old iPhone SE and reproduced the issue. Sigh… Though, it is fair that a FOSS (Free and open-source software) project like Jellyfin hasn’t been as battle-tested on iOS since iOS isn’t exactly the preferred mobile platform of FOSS developers.

From that day, I vowed to never use Jellyfin again and to complain about how buggy it is every time someone mentions it.

Investigating the Issue

Audiobook support was added to Jellyfin more than four years ago. Two years later, someone reported this issue with audiobooks on iOS. Turns out, this issue is specific to m4b audiobooks, which don’t work with Safari at all. Despite over a dozen comments confirming the issue, nobody managed to pinpoint the bug. I replied to the issue “4 years later and no fix… any update on this or is the project dead?”, patted myself on the back, and closed my laptop for the day.

Knowing next to nothing about the Jellyfin codebase, audio codecs, or iOS development, I figured that the issue was probably simple to fix. In all likelihood, there is a file somewhere that Jellyfin uses to decide when to transcode. All I needed to do was add one line if (filetype == ‘m4b’ && platform == ‘iOS’) return transcodeToMP3(filepath). A half-hour of searching for a needle in a stack of Jellyfin repos humbled me enough to realize I wouldn’t be fixing this myself.

Reaching Out

I just needed someone to give me a jump start by pointing me to the right section of the codebase. I knew commenting on the issue was unlikely to help because 1) only people who already commented on the issue were likely to see it, 2) nobody seemed to be actively working on this, and 3) some 4,000 new issues have been reported since this bug so it is not likely to get fresh eyes anytime soon. Having searched the Jellyfin forums earlier, I knew that the troubleshooting section was active, with most posts getting replies the same day. Impatient, I started writing on their Matrix chat instead, but my message quickly grew long with details of how to reproduce the issue. I was worried my message would get lost in the chat or worse, that I’d step away and miss replies.

As I was writing, I made the strategic decision to frame my post as a user troubleshooting the issue rather than a developer requesting help. There were a few reasons for this:

  1. I wasn’t sure if there was a configuration fix for this (like to force transcoding).

  2. If someone was facing this issue later, it would be easier for them to find my post on the forum since it was phrased more for server admins rather than developers.

  3. I often see GitHub posts filled with maintainers thanklessly offering guidance to new contributors, only to never get another reply. Despite my best intentions, I wasn’t actually sure if I wanted to put the energy into getting a whole dev environment setup and trying to fix an iOS bug.

Before posting, I turned on email notifications in the forum settings to quickly follow up with anyone trying to help. A few hours after posting, a user named TheDreadPirate replied, asking if the issue happens with the iOS app, which I hadn’t mentioned because I thought the Safari part might be easier to tackle. I replied within the hour.

Twenty-four hours later, I got no reply and had some free time (and motivation), so I hopped on their Matrix chat room.

I posted on the forums yesterday but figured I'd hop on here too.

Can someone could point me to the code/docs about how jellyfin decides to transcode?

To which I got a not-so-inspiring reply:

what you are asking for is more then just “one line” of code

However, TheDreadPirate also jumped in to drop more helpful info:

same. the log you attached in the forum post include the playback profiles and aac in m4b was in the directplay category

DirectPlayProfile { Container: "m4b", AudioCodec: "aac", VideoCodec: null, Type: Audio }

so the client should be telling the server to just send the file unmodified.

Unfortunately, as it happens, someone called me right as I was getting these messages, so I didn’t get a chance to ask where in the codebase this could be addressed. The next few days, I was wrapped up in moving to a new apartment across town, so I didn’t dig into the issue, and I missed a follow-up question on my forum post.

Resolution

A week and a half later, I saw there was a PR open to fix the issue, which looked a lot like I suspected it would. That PR was quickly closed in favor of this one, which is even simpler. I don’t fully understand how/why this file works, but it broadly makes sense. I returned to my original forum post and let them know the issue was fixed.

Just like that, a years-old issue has been fixed for all Jellyfin iOS audiobook users. Or at least it will be when released, which, according to the docs and history could be a few months since they don’t release on a set cadence. I like to think that the fix was made because of my efforts, but it could be a coincidence that another user ran into the same issue and fixed it. At the end of the day, I’m just happy the issue is resolved.

I write this to share a bit of the process and challenges (like knowing where to post and how to frame it) of getting involved in FOSS. I hope this post inspires people to contribute, even if they’re not software developers. One of the beautiful things about FOSS is that anyone can contribute. Many projects would benefit from having more people helping with communications, onboarding, marketing, or even just reporting a bug like this. Admittedly, the process can be a bit daunting as each project has its own process, tools, and expectations. With a bit of persistence, you can make the software the world relies on a bit better for everyone.