JellyFilm: Introducing our Jellyfin client in the works

JellyFilm: Introducing our Jellyfin client in the works
Photo by Jason Leung / Unsplash

Jellyfin is one of those projects I genuinely love: open, community-driven, and good enough to power an entire media setup at home. What hasn’t been as satisfying is the mobile experience.

My colleague and I have tried pretty much every Jellyfin client we could find for iOS and Android. Many of them are great in parts — but we kept running into the same feeling: our libraries deserve better.

So we decided to build our own.

This post is the start of a series documenting a new Jellyfin client built with Kotlin Multiplatform (KMP). The goal isn’t “yet another client”. The goal is a client we’d genuinely recommend to friends — and one that can surpass what’s currently available on both platforms.

Our guiding idea: your library is not a streaming catalog

A Jellyfin library isn’t “whatever the platform serves you today”. It’s a carefully curated collection of media — chosen, organized, and often lovingly maintained.

That means the app should show the beauty of your library, not hide it behind a generic streaming-style interface.

We want to lean into the things that make self-hosted media special:

  • Custom artwork (posters, backgrounds, logos)
  • Custom collections
  • Ratings and metadata that actually show up
  • Actor / crew information and all the details you’d expect from a proper library view

In short: less “Netflix clone”, more “this is your archive, presented with care”.

Offline is not a nice-to-have

If there’s one area where we think most existing clients fall short, it’s the offline watching experience.

Watching your own content offline sounds simple — until you think about all the real scenarios:

  • a train ride with spotty reception
  • a flight
  • a hotel Wi-Fi that’s technically connected but unusable
  • roaming, tunnels, “my network died at the worst moment”
  • wanting to open the app and immediately play what you already downloaded

In too many apps, “offline” feels like an afterthought: content is downloaded, but playback is unreliable, the UI still depends on network calls, or the app behaves differently depending on whether you’re offline by choice or offline by accident.

For us, offline support is a core feature. We want “downloaded” to mean: yep, this works, every time, even when the internet doesn’t.

Why Kotlin Multiplatform?

We both like native apps. We also like not implementing the same logic twice.

Kotlin Multiplatform hits the sweet spot for this project: shared domain logic (API integration, models, caching rules, offline logic, download management), while still allowing each platform to have a UI that feels at home on iOS and Android.

And since we’re going to be obsessing over details, having one shared core helps us move faster without splitting the codebase into two separate worlds.

What we’ll share on this blog

This won’t just be “look, we shipped a thing” posts. We want to document what we learn in a way that’s useful for others:

  • Architecture decisions (what we share vs what stays platform-specific)
  • Jellyfin API integration: what’s easy, what’s surprisingly tricky
  • Designing an offline-first experience that behaves predictably
  • Downloading + playback: formats, edge cases, and reliability
  • UI choices: showing library richness without becoming cluttered
  • Tooling and CI for KMP in real projects

Also: a few things we’re building just because it makes us happy

Some features are practical. Some features are purely “we want this because it sparks joy”.

We have a couple of those on our list — and we’ll reveal them once they’re ready.

Follow along

This project is in active development. If you’ve ever felt that your Jellyfin library deserves a client that treats it like a library — and if you care about offline done properly — you’ll probably enjoy what’s coming next.

Next up: we’ll share our initial architecture and what “offline-first” means in concrete terms for this app.