Jellyfin’s default home screen gives you one row per library. That’s fine with a couple of hundred movies. It stops being fine once the library grows and you end up scrolling a wall of posters with no structure, which is exactly the problem Netflix-style browsing solved a decade ago. I wanted that experience without switching platforms or maintaining a fork of the client.

The constraint that shaped the design

No client modifications. Whatever I built had to work through primitives Jellyfin already exposes, because forking a client means I’m now maintaining a client. Jellyfin already has a concept of Collections, curated groups of titles that show up as their own row. If I could populate those programmatically, I get the Netflix-style rows for free, on every client, with zero frontend work.

Rule engine, not manual curation

Manually maintaining collections doesn’t scale any better than folders do. So collections are defined declaratively in rules.json (genre, TMDb keyword, year range, rating), and a CollectionRuleEngine matches the library against them:

CollectionRuleEngine        ← core matching logic (shared)
    ↑                  ↑
FullRebuildTask        LibraryEventConsumer
(scheduled)           (on new import)

The engine itself is shared between two triggers: a scheduled full rebuild that keeps collections fresh as ratings and metadata change, and an event consumer that reacts the moment a new file is imported. New content gets tagged and slotted into the right rows without waiting for the next scheduled pass.

Where the metadata comes from

Genre and year are already in Jellyfin’s library data, but “Nail Biters” or ”80s Classics” need something richer than genre tags. TmdbService fetches keyword data per movie from TMDb and caches it in memory, so the rule engine has enough signal to build collections that would otherwise need a human doing manual tagging. There’s an admin UI to view and override tags per movie for the cases where TMDb’s keywords get it wrong.

The folder migration that made it work

The plugin doesn’t require restructuring your files, but it does need library structure right. If you’ve got twenty separate folders for twenty movie genres, Jellyfin gives you twenty “Latest in X” rows before the plugin even runs. Consolidating everything into one library, pointed at all the same directories, no files moved, collapses that down to one “Latest in Movies” row, with the rule engine’s collections sitting on top as the actual browsing structure.

Deployment is a straight DLL drop:

dotnet build -c Release
cp Jellyfin.Plugin.Curator/bin/Release/net9.0/Jellyfin.Plugin.Curator.dll \
   /mnt/Media/Jellyfin/config/plugins/Curator/

Restart Jellyfin, and the collections show up as if they’d always been there. That’s the bar for this kind of thing. It should feel native, not bolted on.