The audience tag admitted any title whose official rating sat at or below the ceiling, with no quality condition at all. The community-rating floor was only consulted when a title had no usable rating. On a real library that let 21 poorly reviewed but mildly rated titles through - Norbit, Cats, Super Mario Bros., Rocky V, Cool as Ice among them - none of which belong in a library whose entire purpose is to be smaller than the one it was carved out of. A ceiling answers whether a title is suitable; it says nothing about whether it is worth offering. MinCommunityRating now applies to rated titles, alongside the existing MinCommunityRatingWhenUnrated for titles with no rating to judge. The two are separate settings because an unrated title carries more uncertainty and may warrant a higher bar. A rated title with no community rating at all now fails, which is deliberate: with neither a score to check nor a reputation to weigh, there is nothing to decide on. Verified against the live library: a full pass over 2778 titles settles at 561 tagged, and the 21 are not re-added.
Jellyfin Audience
A Jellyfin plugin that repairs ratings the server cannot parse, and maintains an audience tag so a user account can be narrowed to a manageable subset of a large library.
It runs in-process as a scheduled task. It needs no API key, no credential file and no external service.
Why it exists
Ratings Jellyfin cannot parse are a hole in parental controls
Jellyfin resolves an OfficialRating string to a numeric score. When it cannot,
the item takes the same path as an unrated one, and unrated items are visible
to everyone by default unless an account explicitly blocks unrated content.
That is not a theoretical problem. A real library scanned for this contained
Singapore board ratings (M18, NC16, R21) on adult titles. Jellyfin could
not parse any of them, so no age ceiling would have hidden them. It cuts the
other way too: TP (Spain) and L (Brazil) both mean "all ages", and titles
carrying them were being hidden from children as though unrated.
This plugin remaps those strings to their documented US equivalents and locks the result.
Allowed Tags fails closed
Narrowing an account with Allowed Tags is the right mechanism, but it is a whitelist: anything without the tag is invisible. A library that keeps growing would leave that account frozen at whatever was tagged on the day it was set up. So the tagging pass has to run on a schedule, not once.
What it does not do
It does not guess a rating from genre, synopsis, or anything else about the content. A wrong guess fails open, and failing open is the one outcome parental controls exist to prevent. Ratings it cannot map are logged as warnings and left untouched, for a person to decide.
Configuration
Dashboard, then Plugins, then Audience.
| Setting | Default | Meaning |
|---|---|---|
Enabled |
on | Master switch for the scheduled task |
RepairRatings |
on | Remap unparseable board ratings |
ApplyTags |
on | Maintain the audience tag |
TagName |
grandma |
Tag applied to matching titles |
MaxRatingScore |
14 |
Rating ceiling for tagging. Inclusive |
MinCommunityRatingWhenUnrated |
6.0 |
Community-rating floor for titles with no usable rating |
RespectManualUntag |
on | Honour the ledger, so manual untagging sticks |
The US rating ladder is 0 G and TV-G, 7 TV-Y7, 10 PG and TV-PG, 13
PG-13, 14 TV-14, 17 R and TV-MA. Note that Jellyfin 10.11 rescored TV-PG
from 13 to 10, so a ceiling carried over from an older install will not behave
the way it used to.
MinCommunityRatingWhenUnrated is a weak gate on obscure titles, where a
perfect score can rest on a handful of votes. It is a starting filter, not a
verdict.
Curating the result
The rule is a first pass, not an opinion. To remove something, untag it in the Jellyfin UI. The ledger records that the item was tagged once, so the next run sees a deliberate removal and leaves it alone.
Turning off RespectManualUntag makes every run re-apply the rule, which undoes
curation. It exists for the case where you want to reset and start again.
Building
Needs the .NET 9 SDK. On a machine where it is a user-local install rather than a system package, it will not be on the PATH of a non-interactive shell:
export DOTNET_ROOT="$HOME/.dotnet"
export PATH="$DOTNET_ROOT:$PATH"
dotnet build Jellyfin.Plugin.Audience/Jellyfin.Plugin.Audience.csproj -c Release
The output is Jellyfin.Plugin.Audience/bin/Release/net9.0/Jellyfin.Plugin.Audience.dll.
Installing
Copy the DLL and meta.json into a folder named Audience_1.0.0.0 inside
Jellyfin's plugins directory, then restart the server. The plugin appears
under Dashboard, Plugins, and its task appears under Scheduled Tasks as
"Repair ratings and apply audience tags".
targetAbi is 10.11.0.0. That is deliberate and is not a mistake on a 12.x
server: a plugin built against Jellyfin.Controller 10.11.11 loads and runs on
Jellyfin 12.0. Do not raise it without confirming the newer package exists and
the result still loads.
Releasing
To publish through a Jellyfin plugin catalog, zip the DLL and meta.json, host
the zip, and add a repository manifest entry whose sourceUrl points at it. The
sourceUrl must end in .zip, and the checksum field must be the MD5 of the
actual published archive. No manifest is committed here, because a manifest
carrying a placeholder checksum or a URL with nothing behind it produces a
broken install for anyone who adds it.
Licence
GPL-3.0.