Jellyfin Server 12.1.0
Develop a Jellyfin server plugin that allows the Jellyfin server owner/administrator to manually manage parental content ratings for media and control which Jellyfin users are allowed to see or access content based on those ratings.
The plugin must support Canadian and United States parental/content ratings.
The system must work independently of whether Jellyfin or the media metadata provider already supplied a correct rating.
The administrator must always have the ability to manually override a media item’s rating through this plugin.
The plugin must allow the administrator to select and manage individual Jellyfin media libraries.
The initial libraries that must be supported are:
Do not hard-code functionality exclusively to these three names.
The plugin should discover Jellyfin media libraries dynamically so additional libraries can be supported later.
Each library must be selectable independently from the plugin administration page.
After selecting a library, display the media contained inside that library.
The interface must make it easy to manage ratings without displaying every individual episode.
Display each movie as one selectable item.
Example:
[ ] John Wick
[ ] Deadpool
[ ] Oppenheimer
The administrator can check one or more movies and assign a rating.
Display each documentary as one selectable item.
If a documentary is represented as a single Jellyfin media item, treat it similarly to a movie.
Example:
[ ] Planet Earth
[ ] The Last Dance
[ ] World War II Documentary
TV content must use a hierarchy.
Example:
Tulsa King
Do NOT display individual episodes for parental-rating assignment.
Ratings are assigned at:
Never require the administrator to rate individual episodes.
If the administrator assigns a rating to an entire TV series, every season and episode under that series must inherit that rating unless a season has an explicit override.
Example:
Tulsa King = 18A
Season 1 automatically = 18A
Season 2 automatically = 18A
Season 3 automatically = 18A
If the administrator later changes:
Season 1 = 14A
Then:
Season 1 and all episodes inside Season 1 inherit 14A.
Season 2 and Season 3 continue inheriting the parent series rating of 18A.
Season-level overrides take priority over the series-level rating.
Each selectable media item must contain a checkbox.
The administrator must be able to:
Provide a rating dropdown.
Provide a button:
Save Ratings
The rating must NOT be changed until the administrator clicks Save Ratings.
Support ratings commonly used in both Canada and the United States.
The plugin must normalize metadata variations.
For example:
PG
PG
PG-13
Rated R
R
18-A
18A
18-A
These variations must not be treated as completely different ratings when they represent the same classification.
Trailing spaces, HTML encoding, capitalization differences, and common formatting variations must be cleaned before the rating is evaluated.
At minimum support:
The plugin architecture should allow additional US classifications to be added later.
Canadian classifications can differ depending on province and media type.
The plugin should at minimum recognize common classifications such as:
Also support common formatting variants such as:
The rating architecture must be configurable rather than hard-coded into complicated application logic.
The developer should be able to add additional Canadian ratings later without redesigning the entire plugin.
Internally the plugin should use a normalized rating representation.
Example concept:
G
PG
PG-13
14A
R
18A
NC-17
TV-Y
TV-Y7
TV-G
TV-PG
TV-14
TV-MA
Unrated
Raw metadata values should be translated into the normalized value before access rules are evaluated.
Example:
“18-A “ → 18A
“Rated R” → R
“PG “ → PG
“PG ” → PG
Normalization must happen before comparisons are made.
The plugin must display all Jellyfin user accounts that the current administrator has permission to manage.
Each user should have a checkbox.
Example:
[ ] Parent
[ ] Child 1
[ ] Child 2
[ ] Guest
Multiple users may be selected at once.
The administrator must also be able to configure one user independently.
Each user may be assigned a maximum permitted content level.
Example concept:
Child Account:
Maximum Allowed Rating: PG-13
Content rated at or below the permitted level may be visible.
Content above the permitted level must be hidden and inaccessible.
The exact mapping between Canadian and United States ratings must be handled by a clearly defined rating policy instead of comparing rating names alphabetically.
When a user is restricted from content, the plugin must enforce the restriction server-side.
Do NOT rely only on hiding the item in the web interface.
Restricted users must not be able to access blocked media through:
The access decision must ultimately be enforced by the Jellyfin server.
UI hiding alone is not acceptable security.
By default, media above a user’s permitted rating should not appear to that user.
The media should behave as though the user does not have access to it.
It must not expose:
This prevents younger users from seeing inappropriate titles or artwork even when playback itself would otherwise be blocked.
Administrator configures:
Child User Maximum Rating = PG-13
Library contains:
Movie A = PG
Movie B = PG-13
Movie C = R
Movie D = 18A
Expected result:
Movie A = Accessible
Movie B = Accessible
Movie C = Restricted
Movie D = Restricted
The restricted movies should not normally appear to that user.
Tulsa King:
Series Rating = 18A
Season 1 = inherits 18A
Season 2 = inherits 18A
Season 3 = inherits 18A
A user restricted below 18A must not see:
Tulsa King
or any of its seasons or episodes.
If the administrator changes:
Season 1 = PG-13
while the parent series remains:
Tulsa King = 18A
The plugin must decide visibility carefully.
The user may only access Season 1 if the application’s Jellyfin integration can safely expose that season without unintentionally exposing metadata or access to the restricted seasons.
Access enforcement must always follow the most specific applicable rating:
Episode explicit rating, if ever supported in the future
→ Season override
→ Series rating
→ Existing Jellyfin media rating
→ Unrated policy
For the current plugin, administrators do not manually manage episode ratings.
When determining the effective rating, use the following priority:
Manual plugin assignments must take priority over automatically imported metadata.
Do not destroy the media’s original metadata unnecessarily.
If Jellyfin already knows that a movie is rated R, the plugin may use that information.
If the administrator manually changes the plugin rating to PG-13, the plugin override becomes the effective parental-control rating.
If the override is later removed, the original Jellyfin metadata rating can become active again.
Provide an administrator setting controlling how unrated media is treated.
Options should include:
Allow Unrated Content
Block Unrated Content
Treat Unrated Content As A Specific Rating
For child accounts, administrators may choose to block unrated media completely.
Unrated content must never silently bypass configured parental restrictions unless the administrator explicitly allows it.
The administrator must be able to select several media items simultaneously.
Example:
[✓] Movie 1
[✓] Movie 2
[✓] Movie 3
[✓] Documentary 1
Rating:
PG-13
Click:
Save Ratings
All selected media receives the chosen plugin rating.
Provide convenient bulk-selection controls.
Examples:
Select All
Deselect All
Select All Movies
Select All Series
Select All Seasons for This Series
These controls must only affect the currently relevant library/view unless clearly stated otherwise.
Large Jellyfin libraries require search.
Provide a search field capable of filtering media by:
Useful filters should include:
Example:
Show only: 18A
This allows the administrator to review restricted content quickly.
Every displayed media item should show its current effective rating.
Example:
Tulsa King — 18A
Season 1 — 18A (Inherited)
Season 2 — PG-13 (Override)
The interface should distinguish:
The administrator should always be able to understand why an item currently has a particular rating.
Provide a way to remove a manually assigned plugin rating.
Example button/action:
Remove Override
Removing an override should restore the next available rating source.
Example:
Plugin Override = R
Jellyfin Metadata = PG-13
Remove Override
Effective Rating becomes:
PG-13
Do not permanently erase the existing Jellyfin metadata just because an override was removed.
Only authorized Jellyfin administrators should be allowed to configure the plugin.
Normal users must never have access to:
All administrative endpoints must verify Jellyfin administrative authorization server-side.
Do not rely only on hiding administrator buttons.
Every access-control decision must use the authenticated Jellyfin user identity.
Never trust:
Authorization must be validated server-side.
Store plugin-managed configuration separately from the actual video files.
Do not rename, modify, move, or rewrite media files.
The plugin data should include at minimum:
Media item identifier
Library identifier
Assigned rating
Assignment type
User restriction configuration
Last modified date
Plugin configuration version
Where possible, use stable Jellyfin item identifiers instead of relying solely on filesystem paths.
The plugin must handle Jellyfin library rescans safely.
If files are rescanned but Jellyfin retains the same logical media item, parental settings should remain.
If an item is deleted, stale plugin records should eventually be cleaned safely.
Never accidentally apply an old rating to an unrelated new media item because a filename or filesystem location was reused.
The plugin must NEVER:
The plugin manages Jellyfin authorization and plugin metadata only.
Parental restrictions must fail safely.
If the plugin cannot determine whether restricted content should be allowed for a restricted user, prefer denying access rather than accidentally allowing inappropriate content.
Administrator accounts should not accidentally become locked out because of malformed rating metadata.
Errors should be logged for investigation.
Log important administrative actions.
Examples:
Rating changed
Rating override removed
User parental policy changed
Bulk rating operation performed
Plugin configuration changed
Access denied because of parental restriction
Do not flood Jellyfin logs with unnecessary entries for every normal library query.
Debug logging may provide additional details when explicitly enabled.
Never log authentication tokens or sensitive credentials.
Suggested page structure:
Overview of:
Choose Library:
Movies
TVShows
Documentaries
Then manage media ratings.
Display Jellyfin users and their maximum permitted ratings.
Configure:
General plugin options and diagnostics.
Changing a dropdown must not immediately write changes.
The administrator should:
Select media
Choose rating
Click Save Ratings
After saving:
Display a clear success message.
Example:
Ratings saved successfully.
If saving fails:
Display a useful error.
Example:
Unable to save rating changes. Check the Jellyfin server log for details.
Do not silently fail.
For large bulk operations, request confirmation.
Example:
You are about to assign 18A to 147 media items.
Continue?
This reduces accidental mass-rating mistakes.
Do not require unnecessary confirmation for changing one ordinary media item.
Do not load thousands of Jellyfin items into the browser at once.
Use one or more of:
The plugin must remain usable with large libraries.
Avoid repeatedly scanning the physical filesystem when Jellyfin’s library database/API already contains the required media hierarchy.
Jellyfin should remain the primary source for:
Do not independently crawl /mnt/storage or other media directories unless there is a technical Jellyfin limitation that makes this unavoidable.
Use Jellyfin’s library representation wherever possible.
The administration UI may visually represent media similar to its folder organization.
However, authorization must be based primarily on Jellyfin media entities rather than blindly trusting filesystem folders.
Example:
Tulsa King
→ Series
Season 1
→ Jellyfin Season
Episodes
→ Jellyfin Episodes
This keeps the plugin compatible with Jellyfin’s library model.
Only show valid rating targets.
Movie:
Selectable
Documentary:
Selectable
Series:
Selectable
Season:
Selectable
Episode:
Not selectable in the current version
Random non-media files:
Not selectable
Images/subtitles/NFO files:
Not selectable
Keep the rating comparison system separated from the UI.
Do not scatter logic such as:
if rating == “PG-13”
throughout the codebase.
Create a centralized rating policy/service responsible for:
This will make future maintenance much easier.
Do not assume that Canadian and US classifications have identical names or direct one-to-one equivalents.
Create an explicit policy table/configuration describing their relative access levels.
The plugin should evaluate a normalized parental-control level while still displaying the original recognizable rating to the administrator.
Do not guess equivalency dynamically from rating text.
The most important technical rule:
HIDING CONTENT IS NOT THE SAME AS BLOCKING CONTENT.
The plugin must prevent unauthorized playback/access at the server authorization layer wherever Jellyfin’s plugin architecture provides supported integration points.
A restricted user must not be able to bypass the plugin merely by discovering an item ID or playback URL.
Target Jellyfin 12.1.0.
Do not intentionally support deprecated Jellyfin APIs merely because older plugin examples use them.
Before implementing an integration point:
Confirm it exists in the Jellyfin 12.1.0 server/plugin architecture being compiled against.
Do not invent APIs, classes, hooks, interfaces, or events.
If Jellyfin 12.1.0 does not provide a safe supported hook required for a feature, document the limitation rather than implementing a fragile fake workaround.
The plugin must not modify Jellyfin core source code.
The server should remain upgradeable.
All functionality should live inside the plugin unless Jellyfin exposes no plugin-compatible mechanism for a required feature.
Any discovered Jellyfin limitation must be documented before proposing modification of Jellyfin itself.
Use clean separation between:
Avoid giant classes containing unrelated functionality.
Use dependency injection where appropriate within Jellyfin’s supported plugin architecture.
Do not silently catch exceptions.
Handle expected errors cleanly and log useful diagnostic information.
The plugin must tolerate:
One broken media item must not break the entire parental-control interface.
All values received from the administration UI must be validated server-side.
Validate:
Never accept an arbitrary rating string and save it without validation.
Version 1 should focus on:
Do not expand Version 1 into unrelated parental-control features.
Do NOT add unless requested later:
Keep Version 1 focused.
The architecture should make it possible to add later:
Do not implement these now.
Simply avoid designing Version 1 in a way that makes them impossible later.
The final administrator workflow should be simple:
No manual editing of configuration files should be required for normal use.
Libraries:
Movies
TVShows
Documentaries
Ratings:
Deadpool = R
Oppenheimer = R
Finding Nemo = G
Tulsa King = 18A
Tulsa King Season 1 = 18A
Tulsa King Season 2 = 18A
Tulsa King Season 3 = 18A
Planet Earth = PG
Users:
Owner
Maximum Rating: Unrestricted
Teenager
Maximum Rating: PG-13 / configured Canadian equivalent level
Young Child
Maximum Rating: PG
Expected behavior:
Owner sees everything.
Teenager does not see or access Deadpool, Oppenheimer, Tulsa King, or other content above their configured limit.
Young Child sees only content permitted under the PG policy.
The restriction applies regardless of which Jellyfin client the user uses.
Do not consider the feature complete simply because restricted titles disappear from the Jellyfin web interface.
The plugin is complete only when rating assignment, rating inheritance, user policy evaluation, media filtering, and server-side access enforcement all operate together.
Security and authorization must be implemented first as server behavior.
The administration interface is the management layer on top of that behavior.
Create automated tests where practical for the rating-policy and normalization system.
At minimum test:
“PG” → PG
“PG “ → PG
“PG ” after decoded input → PG
“Rated R” → R
“R” → R
“18-A” → 18A
“18A” → 18A
“14-A” → 14A
Unknown rating → Unrated/Unknown policy
Also test:
Movie override
Series inheritance
Season override
User below rating limit
User equal to rating limit
User above rating requirement
Unrestricted administrator
Unrated-content blocking
Deleted media reference
Deleted user reference
Bulk assignment
Override removal
Before writing access-control code that depends on Jellyfin internals:
Inspect the actual Jellyfin 12.1.0 assemblies/source/API available to the project.
Do not assume an older Jellyfin plugin tutorial remains valid.
Compile frequently.
Do not create placeholder integrations that appear functional while providing no real authorization protection.
If an intended hook does not exist, report exactly:
Do not silently weaken the parental restriction requirement.
Version 1 is complete when an administrator can: