Media

Jellyfin: Fast Enough to Feel Native

Fast local session updates without making the fast path a dependency

home automationhomeseermedia serverjellyfinself-hostednetworksoftwareaudio-videowebsocketsoftware-automation

Jellyfin: One Pipeline, Two Doors

Not in the HomeSeer store quite yet. It’s been running here every day, and the plan is to send it to the store as a beta.

Jellyfin solves much the same problem as Plex, but with a very different philosophy.

It is fully self-hosted.

There is no central Jellyfin account required.

No subscription.

No company-operated media service sitting between you and the library already stored in your house.

The server, the users, the authentication, and the media all belong to the installation you control.

For a house that would rather not have its media library mediated by somebody else’s service, that is a pretty compelling argument all by itself.

The HomeSeer plugin does the expected things.

It reports active sessions.

It exposes what is playing and where.

It can control playback.

But the part I found most interesting was not what it reports.

It was how quickly HomeSeer can know that something changed.

Polling works, but it always arrives late

The conventional way to monitor a media server is polling.

Ask Jellyfin for the current sessions.

Wait.

Ask again.

Repeat forever.

There is nothing fundamentally wrong with that.

For plenty of automation, a few seconds of delay is irrelevant.

If someone starts a movie and the lights dim four seconds later, nobody is likely to file a formal complaint.

But polling always has one unavoidable property:

The event happens between polls.

So HomeSeer learns about it somewhere between immediately and one full polling interval later.

Poll more frequently and the system feels more responsive.

Poll more frequently and you also spend more time asking:

“Anything change?”

when the answer is almost always:

“No.”

That tradeoff has always bothered me a little.

Jellyfin gives us another option.

The push socket

Jellyfin exposes a WebSocket at /socket.

Instead of HomeSeer repeatedly asking whether the session list has changed, the plugin can leave a connection open and let Jellyfin push updates to it.

That is much closer to how I want an automation system to behave.

Events should arrive because something happened.

Not because a timer eventually got around to asking.

The plugin subscribes using:

SessionsStart "0,1500"

which requests session updates with a nominal 1.5-second interval.

Except Jellyfin had its own opinion about that number.

When I measured the behavior here on August 11, 2026, the server consistently delivered updates every:

2.0 seconds

regardless of the 1.5-second interval I requested.

So on the Jellyfin version running here at that time, the server appeared to clamp the request to its own minimum.

That is an observation from this installation, not a promise I expect every future Jellyfin version to make.

And that distinction is worth keeping.

Still, two seconds is comfortably fast enough for this job.

One parser, two doors

The especially nice discovery was what came through the WebSocket.

On the server I tested, each pushed session entry had the same useful structure as the corresponding object returned by the ordinary sessions API.

That meant I did not need a separate interpretation layer for WebSocket data.

The socket receives an update.

The polling request receives an update.

Both converge on the same session model.

Then the same reconciliation logic.

Then the same HomeSeer publishing path.

One pipeline.

Two doors.

That is exactly the kind of duplication I am happy not to create.

Because the moment two separate code paths independently interpret the same information, they begin competing to see which one can become subtly different first.

Here, the transport may change.

The meaning does not.

The rule that makes the fast path safe

The WebSocket is faster.

It is also completely optional.

That distinction is important.

If the socket fails, the plugin does not fail.

If Jellyfin closes the connection, the plugin does not fail.

If the network hiccups, the protocol changes unexpectedly, the platform does something strange, or the connection simply disappears in the night, the plugin does not conclude that media sessions have ceased to exist.

It falls back to polling.

The freshness check notices that push updates have stopped.

Normal polling resumes at the full rate.

The socket reconnects later using a growing backoff.

Everything continues working.

The only thing that changes is how quickly HomeSeer hears about an update.

That was a deliberate design rule.

The WebSocket is an optimization.

So it has to behave like one.

Disable it completely and the plugin should still do the same job.

Just a little more slowly.

That sounds obvious, but it is surprisingly easy to build a “fast path” that quietly becomes the only path.

Then one day the optimization breaks and takes the feature with it.

At that point it was never really an optimization.

It was a dependency wearing a disguise.

I like the stronger definition:

If removing an optimization changes anything except performance, it probably was not optional enough.

The fallback is not theoretical

WebSockets are useful, but they are also long-lived network connections.

Long-lived network connections eventually experience life.

Servers restart.

Reverse proxies get involved.

Connections disappear without a polite closing handshake.

Authentication behaves differently between versions.

Networks hiccup.

Clients suspend.

And Jellyfin itself has had bugs and edge cases around WebSocket sessions over the years.

None of that makes WebSockets a bad choice.

It makes them a very good choice for the fast path and a poor choice for the only path.

That is why polling never went away.

The plugin does not have to decide whether push or polling is philosophically superior.

It uses push while push is healthy.

It polls when it is not.

The user gets the state either way.

Deliberately identical to its siblings

Jellyfin is one of three media-server plugins in the suite:

Plex

Jellyfin

Emby

They have different APIs.

Different authentication models.

Different discovery mechanisms.

Different histories.

And their own collections of small surprises.

From the HomeSeer side, I want those differences to be almost invisible.

The devices should look the same.

The fields should mean the same thing.

The event triggers should behave the same way.

The pages should be laid out the same way.

If something is called:

Player State

in Plex, it should not become:

Playback Status

in Jellyfin and:

Session Condition

in Emby simply because I happened to write them on different Tuesdays.

Those inconsistencies seem tiny while you are coding.

They become irritating immediately when somebody uses more than one plugin.

More importantly, identical shapes create something much more useful:

Automations that can survive changing media servers.

If someone moves from Plex to Jellyfin, the concepts HomeSeer sees should not suddenly become foreign.

A player is still a player.

Playing is still playing.

Paused is still paused.

The room did not change merely because the software serving the movie did.

That is why these three plugins intentionally mirror one another field for field wherever the underlying platforms allow it.

Making three things match is more work than making one thing work

There is an odd truth in software suites.

Writing the second implementation is usually not the hardest part.

Making the second implementation behave exactly like the first one often is.

Then the third arrives and gets a vote too.

It would be very easy to let each plugin grow naturally around the API underneath it.

That produces three perfectly functional plugins.

It also produces three different user experiences.

So some of the work here is deliberately resisting the shape of the underlying APIs and translating each one into the common shape of the suite.

That takes extra effort.

But it is also the work that makes the plugins feel like they belong together.

Without it, you have several programs from the same developer.

With it, you start to have a product.

And that distinction matters to me.

What I like about this one

Jellyfin itself fits rather nicely with the philosophy behind a lot of these plugins.

The media is already in the house.

The server is already in the house.

The users are local.

The service can operate without depending on a central vendor account or cloud platform.

So the HomeSeer integration follows the same basic idea.

Talk directly to the server.

Use the event path when it is available.

Fall back gracefully when it is not.

Keep the HomeSeer-facing model consistent with its siblings.

And do not turn a performance enhancement into something that can take the entire feature down.

The WebSocket makes it faster.

Polling makes it dependable.

The common parser keeps the two honest.

One pipeline, two doors.

That is probably the part of this one I like most.