Jellyfin plugin — let users rate movies & TV shows with stars across each Jellyfin Instance
C#
39
158 commits
updated Sep 28, 2026
Letterboxd-style ratings, watchlist, lists & social layer for Jellyfin
⭐ If StarTrack is useful to you, please consider starring this repo! ⭐
StarTrack started as a simple star-rating plugin and has grown into a full Letterboxd-style social layer for Jellyfin: ratings, reviews, watchlists, liked films, a chronological diary with rewatches, Top 4 favourites, collaborative lists, recommendations, server-wide watchlist sharing, two-way Letterboxd and Serializd sync, and optional Trakt, Simkl and Yamtrack workflows. Ratings can also mirror into Jellyfin's native per-user rating field for use with Jellyfin filters and backups. Everything is stored as plain JSON on your own server; external services are contacted only when you explicitly configure and connect them.
Designed to integrate cleanly with modern Jellyfin setups: desktop, mobile, TV/webOS remote navigation, and reverse proxies that serve Jellyfin from a BaseURL sub-path.
A fix release for 1.8.0, almost all of it reported by users of it.
Jellyfin 12.1
Fixes
index.html — the normal state in Docker — when the server was injecting perfectly well by its other route (#20).New
3.5, the default), both (3.5 (7/10)) or out of ten (7/10). It applies to every rating StarTrack prints, community averages and your own: the panel, the floating pill, poster badges, the recent list, My Ratings cards, the diary and the taste comparison. Display only - you still rate with five stars, nothing is converted, and a rating always lands on a real position of the scale (so /10 shows 7, never 7.0)☆ Rate badge remains available on TV so remote users always have a focusable way to rate/jelly| ★ Films | Full poster grid of everything you've rated, with a pinned Top 4 row |
| ☆ Watchlist | Bookmark films to watch later, and remove them from the same screen - toggleable to show everyone's combined watchlist with per-user filtering |
| ♡ Liked | One-tap heart toggle, separate from your star rating |
| 📖 Diary | Chronological journal of every watch with rewatch detection, grouped by month, with visual star bars |
| ✍ Reviews feed | Server-wide vertical feed of every rating that has a review, with poster, reviewer, star bar, date and review text |
| ✨ For you | Personalised recommendations weighted by your top genres (movies + series). Reshuffle button rerolls 30 picks from a 60-candidate pool |
| 📃 Lists | Create collaborative film lists that other users on your server can contribute to. Owner-only delete |
Two-way as of 1.7.0. Import has always worked from a username alone. Sending ratings back is new, and comes in two flavours because Letterboxd has no public write API:
| CSV export | Automatic push | |
|---|---|---|
| Password needed | No | Yes - stored encrypted |
| Effort | Download, then upload at letterboxd.com/import | Runs hourly on its own |
| Two-factor accounts | Works | Cannot work |
| Can Cloudflare block it | No | Sometimes |
The CSV route is offered first and is what most people should use. Automatic push is opt-in, hidden behind a checkbox, and states plainly that it stores your Letterboxd password so it can sign in as you. If your account has two-factor authentication, only the CSV route is possible - StarTrack cannot complete a second factor unattended, and says so rather than failing mysteriously.
ratings.csv, diary.csv, watchlist.csv and likes/films.csv - the offline equivalentLetterboxd puts a Cloudflare JavaScript challenge in front of most of what StarTrack needs: the likes page, the profile page (Top 4), the ratings and diary pages that Sync everything reads, and the sign-in used for automatic push. No server can pass a JavaScript challenge on its own, and no header makes it go away. The diary RSS feed and the watchlist page are not affected, so Sync now keeps working without help.
Without help, StarTrack notices the challenge, stops asking for six hours instead of hammering Letterboxd every few minutes, and says so in the Letterboxd panel.
With help, both work. FlareSolverr is a small self-hosted container that solves the challenge in a real headless browser - the same tool Prowlarr, Jackett and Sonarr use for the same wall, so if you run those you already have one. Put its URL in Dashboard → Plugins → StarTrack → FlareSolverr URL, and:
Verify login tries all of this for you in order - as configured, then without any cookies you pasted, then on a freshly solved challenge - and only retries a Cloudflare block, never a rejected password. If it still fails, that is Cloudflare refusing the server, not something you did.
So if you want to send ratings back, use "Download CSV for letterboxd.com/import". No password, no cookies, nothing to expire, and it is the only route that works for accounts with two-factor authentication. Automatic push remains available and works when Cloudflare lets it, but it is the fragile half by nature, not by oversight.
Pasting a cf_clearance cookie and User-Agent from your browser still works as a
stopgap and still takes priority if set - but a stale paste is worse than
none, because Cloudflare reads a bad clearance as a signal in itself. If sign-in
has stopped working, clear those two boxes before you try anything else.
Letterboxd does not do television and Serializd does not do film, so the two together cover a whole library.
Importing needs only a username. Your Serializd diary is public, so StarTrack reads it straight out - no password, no token, nothing stored. Pushing is the half that needs credentials, exactly as with Letterboxd.
| Import | Push | |
|---|---|---|
| What you provide | Username | Email + password |
| Password stored | No | Yes - encrypted |
| Covers | Series, seasons, episodes | Series, seasons, episodes |
| Runs | Hourly, or Sync now | Hourly, or Push now |
Season ratings stay between StarTrack and Serializd. Trakt, Simkl, Yamtrack and Letterboxd have no concept of rating a season, so seasons are deliberately withheld from them rather than filed as a show or, worse, a film.
StarTrack Liked list and, for VIP accounts, Trakt Favorites5★ only, 4.5★ only, 4★ only … down to 0.5★ onlyindex.html, or added to each page as it is served - the normal one for Docker, where index.html is read-only), and flags a stale cached widget and a FlareSolverr behind a VPNAutoExportDaily task writes CSV or JSON snapshots under <jellyfin-data>/data/InternalRating/exports/@media query overhaul for phones and tablets<jellyfin-data>/data/InternalRating/A small floating pill appears at the bottom-right of every Movie / Series / Episode detail page. The canonical StarTrack badge beside Jellyfin's native ratings opens the same panel. Both are keyboard- and TV-remote accessible, and their size can be adjusted for large displays.
Half-star precision, optional written review, community average with per-user breakdown, one-click heart / watchlist / Top 4 pin. "Show all ratings" expands every user's score.
The pill also shows your recent ratings at a glance. One tap opens the Letterboxd sync panel - enter your username, enable hourly auto-sync, or drop in your export ZIP.
Auto-injected into the Jellyfin nav menu - no theme changes required.
Seven top-level views - Media / Watchlist / Liked / Diary / Reviews / For You / Lists - and five type tabs on the Media view: All / Movies / TV Shows / Episodes / Anime. Anime is detected via genre or tag, so a movie or series can count as anime regardless of its underlying Jellyfin type.
Live search with a 150 ms debounce, seven sort options (date rated, film year, your rating, community rating, runtime - each ↑↓), one-click Letterboxd settings pane, and CSV export in Letterboxd-compatible format.
Bringing ratings in. My Ratings → ⇄ External Sync → ⇧ Import file takes a StarTrack CSV or JSON export, a Letterboxd export, or an IMDb ratings export (imdb.com → Your Ratings → Export). The format is detected from the file — there is nothing to choose. IMDb's 1–10 maps exactly onto StarTrack's 0.5–5 stars, the same ten positions, so nothing is rounded.
Sending ratings out. CSV (Letterboxd-compatible), JSON, and an IMDb-format CSV for Yamtrack's Import → IMDb. That last format cannot carry episodes, or anything your library has no IMDb id for — StarTrack now tells you how many rows that was, rather than handing back a quietly shorter file.
Pinned Top 4 sits above the full grid, full-poster cards with a star tier ribbon, year and runtime overlay. Hover any card to reveal Pin / Add to list buttons.
Per-type Top 4: 4 movies, 4 series, 4 episodes, max 12 total. Each pinned slot shows its rank, star tier, year and runtime, and hovering reveals an × remove button.
Server-wide vertical feed of every rating that has a written review, with poster, reviewer, star bar, date and review text. (Reviewer name and review text blurred in this screenshot.)
Letterboxd-compatible sync pane: username input, hourly auto-sync toggle, one-click Sync Now (pulls RSS ratings + watchlist + likes scrape), ZIP drop zone for full exports, Import Top 4 from your public profile, Diagnose button for matcher diagnostics, and Clean dead ratings to purge zombie entries after a library rebuild.
StarTrack adds itself to Jellyfin's web interface by injecting a script into it. That means it appears anywhere you use Jellyfin in a browser:
| Works | Does not work |
|---|---|
| Jellyfin Web in any desktop browser (10.11, 12.0, 12.1) | Roku app |
| Jellyfin Web on phones/tablets | Native Android / iOS apps |
| LG webOS and Samsung Tizen TV browsers | Android TV / Fire TV apps |
| Jellyfin Media Player (desktop) | Kodi and other external clients |
The native apps do not load Jellyfin's web interface, so there is nothing for StarTrack to attach to and no plugin can change that. Your ratings are still recorded server-side, so anything you rate in a browser is visible to everyone and counts everywhere — you just cannot rate from those apps.
/jelly); StarTrack applies it to injected assets, plugin APIs and native Jellyfin media requestsStarTrack uses ASP.NET Core middleware to inject its widget at runtime. No File Transformation plugin required.
https://raw.githubusercontent.com/ZL154/jellyfin-plugin-startrack/main/manifest.json
<jellyfin-data>/plugins/StarTrack/Jellyfin.Plugin.InternalRating.dll
Jellyfin 12 moved to .NET 10, so one DLL cannot serve both server generations. StarTrack ships two, built from the same source:
| Your Jellyfin | Version to install | Ends in | .NET |
|---|---|---|---|
| 10.11.x | 1.8.1.0 | .0 | 9 |
| 12.x | 1.8.1.1 | .1 | 10 |
Installing from the repository? Ignore all of this — the catalogue only offers your server the build it can run. This matters only for manual installs.
The two differ only in which Jellyfin they are compiled against. Features, settings and your data are identical, and upgrading your server from 10.11 to 12 needs nothing more than letting the catalogue update the plugin.
⚠️ If you install the wrong one, it fails silently. Jellyfin logs nothing, the plugin simply does not appear and
/Plugins/StarTrack/Debugreturns 404. If StarTrack has "vanished" after a manual install, check the last digit first.And installing the right one afterwards is not enough on its own. Jellyfin marks the plugin
NotSupportedthe moment it fails to load the wrong build, and that mark survives the swap — the correct DLL is then skipped as "disabled" at every start. After replacing the DLL, open Dashboard → Plugins, find StarTrack, and Enable it (or set"status": "Active"in itsmeta.json), then restart. The self-check below calls this out: no answer at all from the plugin, on a manual install, is almost always this.
Dashboard → Plugins → StarTrack → "Is StarTrack working?" → Run self-check.
It checks eight things — five on the server, three that only your browser can answer — and tells you which one is failing:
<script> tag is actually in the page's HTMLIf nothing at all appears, that answer is itself the diagnosis: the plugin is installed but not running. On a manual install that nearly always means the downloaded build doesn't match your Jellyfin version — see Which build do I need?.
The panel also produces a paste-ready summary for bug reports.
Then open any Movie, TV Show, or Episode detail page: the ☆ Rate pill and one
StarTrack badge should appear, and clicking either should open the rating panel.
A floating pill appears at the bottom-right of every Movie / Series / Season / Episode detail page. StarTrack also places one clickable badge alongside Jellyfin's native ratings. Click either control - or focus it with a keyboard/TV D-pad and press Enter/OK - to:
Admins can use a compact rating-only badge, move poster badges to any corner and choose Normal, Large or Large (TV only) sizing. Users can override the size for their current device from My Ratings → ⚙ Preferences. On TV, unrated media keeps a dimmed ☆ Rate badge so the remote always has a focus target.
Open it from My Ratings in the Jellyfin sidebar, from the ★ My Ratings link in the top bar (Jellyfin 12.1), or from the ★ My Ratings button in any rating panel. Esc or the browser's Back button closes it. The view selector at the top has seven tabs:
Each view supports search, sort, star filter (where applicable) and a type filter: All / Movies / TV Shows / Seasons / Episodes / Anime. Hovering a card reveals its actions - pin to Top 4, add to a list, and on your own watchlist, remove from it.
Dashboard → Plugins → StarTrack → Is StarTrack working? runs eight checks - plugin loaded, script injected, widget executed, token fresh, FlareSolverr reachable and on the same IP as Jellyfin, and so on - and gives you a summary to paste into a bug report. If the page itself returns 404, the plugin is installed but not running, which almost always means the wrong build for your Jellyfin version.
Serializd covers television; Letterboxd covers film. The two halves have different requirements on purpose.
To import (no password):
That is all importing needs - your diary is a public page, so StarTrack just reads it. Nothing is stored beyond the username.
To also push your ratings out: 4. Tick Also push my TV ratings to Serializd, which reveals the credential box 5. Enter your Serializd email and password, click Verify login to check them, then Save 6. Click Push now, or leave it to the hourly task
Choose which kinds to push with the Series / Season / Episode ratings toggles. Include written reviews is off by default: Serializd only keeps review text on a log entry, so a rating carrying a review is sent as a log rather than a bare rating.
Serializd has no public API tokens, so pushing genuinely requires the account password. It is encrypted at rest with the same key ring as the Letterboxd credential and is never returned by any endpoint. If you would rather not store one, use import only - it loses nothing except the outbound direction.
trakt.tv/activate.Ratings only import for items that exist in your Jellyfin library; titles you rated on a service but don't own can't be matched.
Open Dashboard → Plugins → StarTrack to configure the server-wide controls:
AutoExportDaily to true and AutoExportFormat to csv or json in the plugin configuration. The v1.6.4 visual admin form does not expose these two advanced fields yet. The task runs at 03:00 UTC and writes to <jellyfin-data>/data/InternalRating/exports/.If Jellyfin is hosted below a reverse-proxy path, set Jellyfin's normal BaseURL. StarTrack automatically uses that prefix; no separate StarTrack path setting is required.
All endpoints are under /Plugins/StarTrack/. Every endpoint requires Jellyfin authentication unless otherwise noted.
| Method | Endpoint | Description |
|---|---|---|
GET | /Ratings/{itemId} | Average + every user's rating and review |
POST | /Ratings/{itemId} | Submit / update your rating {"stars":4,"review":"..."} |
DELETE | /Ratings/{itemId} | Remove your rating |
GET | /MyRatings?limit=N | All your rated items, newest first |
GET | /Recent?limit=N | Recent ratings across all users |
GET | /Stats | Server-wide rating count |
GET | /ExportCsv | Download your ratings as Letterboxd-compatible CSV |
POST | /BackfillNativeRatings | Copy all existing StarTrack ratings into Jellyfin's native per-user field (elevated admin only; idempotent) |
| Method | Endpoint | Description |
|---|---|---|
GET | /MyWatchlist | Your watchlist |
GET | /EveryonesWatchlist | Every user's watchlist aggregated, with per-item user lists |
POST | /Watchlist/{itemId} | Add an item to your watchlist |
DELETE | /Watchlist/{itemId} | Remove an item |
GET | /MyLikes | Your liked films |
POST | /Likes/{itemId} | Like an item |
DELETE | /Likes/{itemId} | Unlike an item |
GET | /MyFavorites | Your Top 4 (max 12 across types) |
POST | /MyFavorites | Replace your favourites {"itemIds":[...]} |
GET | /Interactions/{itemId} | Combined watchlisted/liked/favourite status for one item |
GET | /Recommendations?limit=N | Personalised picks weighted by your top genres |
| Method | Endpoint | Description |
|---|---|---|
GET | /MyDiary?limit=N | Your chronological diary with rewatches |
POST | /Diary | Manually add a diary entry ({"itemId","watchedAt","stars","review","rewatch"}) |
DELETE | /Diary/{entryId} | Remove a diary entry |
| Method | Endpoint | Description |
|---|---|---|
GET | /Lists | All lists on the server |
POST | /Lists | Create a list {"name","description","collaborative"} |
GET | /Lists/{listId} | Single list with all items |
DELETE | /Lists/{listId} | Delete (owner only) |
POST | /Lists/{listId}/Items | Add a film {"itemId":"..."} |
DELETE | /Lists/{listId}/Items/{itemId} | Remove a film |
| Method | Endpoint | Description |
|---|---|---|
GET | /Letterboxd/Settings | Your Letterboxd username + auto-sync state |
POST | /Letterboxd/Settings | Save username + auto-sync toggle |
POST | /Letterboxd/SyncNow | Pull ratings (RSS) + watchlist (RSS) + likes (HTML scrape) |
POST | /Letterboxd/Import | Upload ZIP or CSV - auto-extracts ratings.csv, diary.csv, watchlist.csv, likes/films.csv |
POST | /Letterboxd/ScrapeFavorites | Scrape your Letterboxd profile's "favourite films" section |
POST | /Letterboxd/Cleanup | Purge ratings whose library item no longer has a file on disk |
GET | /Letterboxd/Diagnose | Library matcher diagnostic + sample of normalised titles |
GET | /Letterboxd/Account | Write-back config. Reports whether a password/cookies are stored, never returns them |
POST | /Letterboxd/Account | Save direction, credentials and per-kind push toggles |
POST | /Letterboxd/VerifyLogin | Test a Letterboxd sign-in without writing anything |
POST | /Letterboxd/PushNow | Push ratings/watched/likes now, returns a per-kind report |
GET | /Letterboxd/ExportCsv | Download ratings as a letterboxd.com/import CSV (no password required) |
| Method | Endpoint | Description |
|---|---|---|
GET | /Serializd/Account | Direction, username, per-kind toggles. Reports whether a password is stored, never returns it |
POST | /Serializd/Account | Save direction, username, credentials and push toggles |
POST | /Serializd/VerifyLogin | Test a Serializd sign-in without writing anything |
POST | /Serializd/SyncNow | Import the public diary now - needs a username only, no password |
POST | /Serializd/PushNow | Push series/season/episode ratings now, returns a per-kind report |
All under /Plugins/StarTrack/ExternalSync/. {provider} is trakt, simkl, or yamtrack.
| Method | Endpoint | Description |
|---|---|---|
GET | /Status | Connection state + direction for every provider |
POST | /{provider}/StartAuth | Begin device-code / PIN login |
POST | /{provider}/PollAuth | Poll for auth completion + store token |
POST | /Yamtrack/Connect | Connect Yamtrack with a base URL + token |
POST | /{provider}/SetDirection | Set Off / ExportOnly / ImportOnly / TwoWay |
POST | /{provider}/Sync | Run a sync now |
POST | /{provider}/BackfillWatched | One-shot library backfill (marks watched / repairs dates) |
POST | /{provider}/Disconnect | Remove the stored token for a provider |
GET | /Export?format=csv|json|imdb|yamtrack | Download Letterboxd CSV (default), JSON, or IMDb/Yamtrack-compatible CSV |
POST | /Import?format=csv|json | Import ratings from an uploaded CSV/JSON body (5 MB limit) |
| Method | Endpoint | Description |
|---|---|---|
GET | /Members | All visible members with avatar, totals, Top 4, follow flag |
GET | /MembersSearch?q= | Same as /Members, filtered by name |
GET | /Members/{userId}/Profile | Light profile bundle (recents, top-4, watchlist, likes, diary preview) |
GET | /Members/{userId}/Stats | Heavy stats - histogram, genres, directors, actors, decades, hours, calendar heatmap, on-this-day, most-rewatched, year cards |
GET | /Members/{userId}/Reviews | All of a user's reviews |
GET | /Members/{userId}/Followers / /Following | Follow graph |
POST | /Members/{userId}/Follow / /Unfollow | Follow / unfollow a member |
GET | /Activity?scope=following|everyone&limit=N | Chronological feed of recent ratings + diary + reviews |
GET | /Compare?a={userIdA}&b={userIdB} | Pearson similarity, both histograms, biggest disagreements, films-each-loved |
GET | /MyPrivacy | Your privacy flags (hide from members / follower count / following / stats / activity) |
POST | /MyPrivacy | Update your privacy flags |
| Method | Endpoint | Description |
|---|---|---|
GET | /PublicConfig | Admin toggle state + supported languages (no auth) |
GET | /Translations/{lang} | JSON translation bundle for a language (no auth) |
GET | /AdminConfig | Current plugin configuration (admin only) |
POST | /AdminConfig | Save plugin configuration (admin only) |
| Method | Endpoint | Description |
|---|---|---|
GET | /Widget | Embedded widget JavaScript (no auth) |
GET | /StandalonePage?token=...&userId=... | Self-contained My Ratings WebView shell; fetchable before it seeds the supplied Jellyfin credentials |
GET | /WhoAmI | Auth debug for the current session |
GET | /Debug | Diagnostic report (no auth) |
All data is stored as plain JSON in <jellyfin-data>/data/InternalRating/:
| File | Contents |
|---|---|
ratings.json | Per-item rating + review per user |
user_interactions.json | Per-user watchlist, liked films, Top 4 favourites |
diary.json | Chronological diary entries with rewatch flag |
lists.json | All collaborative lists |
letterboxd.json | Per-user Letterboxd sync settings (username, auto-sync, last-synced state) |
follows.json (new in 1.5) | Per-user follow graph |
privacy.json (new in 1.5) | Per-user privacy flags (hide stats / activity / followers / etc) |
external-sync.json (new in 1.6) | Per-user, per-provider external-sync settings: direction, OAuth tokens, last-sync state |
exports/<userId>-<date>.csv|json (optional) | Daily per-user rating backups when automatic export is enabled |
Back them up or migrate them like any other data file - no database required. Native-rating mirroring supplements these files; StarTrack's JSON remains the source of truth.
git clone https://github.com/ZL154/jellyfin-plugin-startrack.git
cd jellyfin-plugin-startrack/InternalRatingSystem
dotnet publish -c Release -o ./publish_out
The compiled DLL is at publish_out/Jellyfin.Plugin.InternalRating.dll.
Version note: The plugin must be compiled against the
Jellyfin.ControllerNuGet package version that exactly matches your server. The csproj currently targets10.11.6. Update<PackageReference>if your server version differs.
Issues and pull requests are welcome.
What is new in the current release stays at the top of Features; everything it describes is also folded into the feature sections above, so those read as a description of StarTrack today. Older release notes live here.
Jellyfin 12. StarTrack now runs on Jellyfin 12 and on 10.11, from one codebase, as two builds - see Which build do I need?. Installing from the repository picks the right one for you.
Letterboxd, repaired
Diary
Ratings
3.5, the default), both (3.5 (7/10)), or out of ten (7/10). Applies everywhere an average is displayed, so a 3.5 sitting next to IMDb's 7 no longer reads as a disagreement. Display only - you still rate with five stars, nothing is converted.Diagnostics
Contributed
Fully translated into all eight languages.
The largest release so far: a second external service, a new thing you can rate, and the Letterboxd integration finally going both ways.
External services
Rating seasons
Diary
TV and clients
Contributed
☆ Rate target on TV./jelly.(1) count.GET /Plugins/StarTrack/StandalonePage returns a self-contained HTML page that hosts the StarTrack overlay without the surrounding Jellyfin web shell. It is designed for WebViews, mobile apps, smart-TV launchers, third-party clients and kiosk displays. The shell accepts ?token=...&userId=..., seeds Jellyfin's credential storage, loads the existing widget and opens My Ratings without changing normal Jellyfin Web behaviour.↻ Rewatch pills and a denser three-column row layout.StarTrack is built and maintained in my spare time. If it's useful to you and you'd like to support ongoing development, any of these means a lot:
Not expected, just appreciated. Contributions - issues, PRs, translation fixes - are equally valuable.
MIT - © 2025 ZL154
C#
62.3%
JavaScript
35.2%
HTML
2.5%
Jellyfin plugin — let users rate movies & TV shows with stars across each Jellyfin Instance
C#
39
158 commits
updated Sep 28, 2026
Letterboxd-style ratings, watchlist, lists & social layer for Jellyfin
⭐ If StarTrack is useful to you, please consider starring this repo! ⭐
StarTrack started as a simple star-rating plugin and has grown into a full Letterboxd-style social layer for Jellyfin: ratings, reviews, watchlists, liked films, a chronological diary with rewatches, Top 4 favourites, collaborative lists, recommendations, server-wide watchlist sharing, two-way Letterboxd and Serializd sync, and optional Trakt, Simkl and Yamtrack workflows. Ratings can also mirror into Jellyfin's native per-user rating field for use with Jellyfin filters and backups. Everything is stored as plain JSON on your own server; external services are contacted only when you explicitly configure and connect them.
Designed to integrate cleanly with modern Jellyfin setups: desktop, mobile, TV/webOS remote navigation, and reverse proxies that serve Jellyfin from a BaseURL sub-path.
A fix release for 1.8.0, almost all of it reported by users of it.
Jellyfin 12.1
Fixes
index.html — the normal state in Docker — when the server was injecting perfectly well by its other route (#20).New
3.5, the default), both (3.5 (7/10)) or out of ten (7/10). It applies to every rating StarTrack prints, community averages and your own: the panel, the floating pill, poster badges, the recent list, My Ratings cards, the diary and the taste comparison. Display only - you still rate with five stars, nothing is converted, and a rating always lands on a real position of the scale (so /10 shows 7, never 7.0)☆ Rate badge remains available on TV so remote users always have a focusable way to rate/jelly| ★ Films | Full poster grid of everything you've rated, with a pinned Top 4 row |
| ☆ Watchlist | Bookmark films to watch later, and remove them from the same screen - toggleable to show everyone's combined watchlist with per-user filtering |
| ♡ Liked | One-tap heart toggle, separate from your star rating |
| 📖 Diary | Chronological journal of every watch with rewatch detection, grouped by month, with visual star bars |
| ✍ Reviews feed | Server-wide vertical feed of every rating that has a review, with poster, reviewer, star bar, date and review text |
| ✨ For you | Personalised recommendations weighted by your top genres (movies + series). Reshuffle button rerolls 30 picks from a 60-candidate pool |
| 📃 Lists | Create collaborative film lists that other users on your server can contribute to. Owner-only delete |
Two-way as of 1.7.0. Import has always worked from a username alone. Sending ratings back is new, and comes in two flavours because Letterboxd has no public write API:
| CSV export | Automatic push | |
|---|---|---|
| Password needed | No | Yes - stored encrypted |
| Effort | Download, then upload at letterboxd.com/import | Runs hourly on its own |
| Two-factor accounts | Works | Cannot work |
| Can Cloudflare block it | No | Sometimes |
The CSV route is offered first and is what most people should use. Automatic push is opt-in, hidden behind a checkbox, and states plainly that it stores your Letterboxd password so it can sign in as you. If your account has two-factor authentication, only the CSV route is possible - StarTrack cannot complete a second factor unattended, and says so rather than failing mysteriously.
ratings.csv, diary.csv, watchlist.csv and likes/films.csv - the offline equivalentLetterboxd puts a Cloudflare JavaScript challenge in front of most of what StarTrack needs: the likes page, the profile page (Top 4), the ratings and diary pages that Sync everything reads, and the sign-in used for automatic push. No server can pass a JavaScript challenge on its own, and no header makes it go away. The diary RSS feed and the watchlist page are not affected, so Sync now keeps working without help.
Without help, StarTrack notices the challenge, stops asking for six hours instead of hammering Letterboxd every few minutes, and says so in the Letterboxd panel.
With help, both work. FlareSolverr is a small self-hosted container that solves the challenge in a real headless browser - the same tool Prowlarr, Jackett and Sonarr use for the same wall, so if you run those you already have one. Put its URL in Dashboard → Plugins → StarTrack → FlareSolverr URL, and:
Verify login tries all of this for you in order - as configured, then without any cookies you pasted, then on a freshly solved challenge - and only retries a Cloudflare block, never a rejected password. If it still fails, that is Cloudflare refusing the server, not something you did.
So if you want to send ratings back, use "Download CSV for letterboxd.com/import". No password, no cookies, nothing to expire, and it is the only route that works for accounts with two-factor authentication. Automatic push remains available and works when Cloudflare lets it, but it is the fragile half by nature, not by oversight.
Pasting a cf_clearance cookie and User-Agent from your browser still works as a
stopgap and still takes priority if set - but a stale paste is worse than
none, because Cloudflare reads a bad clearance as a signal in itself. If sign-in
has stopped working, clear those two boxes before you try anything else.
Letterboxd does not do television and Serializd does not do film, so the two together cover a whole library.
Importing needs only a username. Your Serializd diary is public, so StarTrack reads it straight out - no password, no token, nothing stored. Pushing is the half that needs credentials, exactly as with Letterboxd.
| Import | Push | |
|---|---|---|
| What you provide | Username | Email + password |
| Password stored | No | Yes - encrypted |
| Covers | Series, seasons, episodes | Series, seasons, episodes |
| Runs | Hourly, or Sync now | Hourly, or Push now |
Season ratings stay between StarTrack and Serializd. Trakt, Simkl, Yamtrack and Letterboxd have no concept of rating a season, so seasons are deliberately withheld from them rather than filed as a show or, worse, a film.
StarTrack Liked list and, for VIP accounts, Trakt Favorites5★ only, 4.5★ only, 4★ only … down to 0.5★ onlyindex.html, or added to each page as it is served - the normal one for Docker, where index.html is read-only), and flags a stale cached widget and a FlareSolverr behind a VPNAutoExportDaily task writes CSV or JSON snapshots under <jellyfin-data>/data/InternalRating/exports/@media query overhaul for phones and tablets<jellyfin-data>/data/InternalRating/A small floating pill appears at the bottom-right of every Movie / Series / Episode detail page. The canonical StarTrack badge beside Jellyfin's native ratings opens the same panel. Both are keyboard- and TV-remote accessible, and their size can be adjusted for large displays.
Half-star precision, optional written review, community average with per-user breakdown, one-click heart / watchlist / Top 4 pin. "Show all ratings" expands every user's score.
The pill also shows your recent ratings at a glance. One tap opens the Letterboxd sync panel - enter your username, enable hourly auto-sync, or drop in your export ZIP.
Auto-injected into the Jellyfin nav menu - no theme changes required.
Seven top-level views - Media / Watchlist / Liked / Diary / Reviews / For You / Lists - and five type tabs on the Media view: All / Movies / TV Shows / Episodes / Anime. Anime is detected via genre or tag, so a movie or series can count as anime regardless of its underlying Jellyfin type.
Live search with a 150 ms debounce, seven sort options (date rated, film year, your rating, community rating, runtime - each ↑↓), one-click Letterboxd settings pane, and CSV export in Letterboxd-compatible format.
Bringing ratings in. My Ratings → ⇄ External Sync → ⇧ Import file takes a StarTrack CSV or JSON export, a Letterboxd export, or an IMDb ratings export (imdb.com → Your Ratings → Export). The format is detected from the file — there is nothing to choose. IMDb's 1–10 maps exactly onto StarTrack's 0.5–5 stars, the same ten positions, so nothing is rounded.
Sending ratings out. CSV (Letterboxd-compatible), JSON, and an IMDb-format CSV for Yamtrack's Import → IMDb. That last format cannot carry episodes, or anything your library has no IMDb id for — StarTrack now tells you how many rows that was, rather than handing back a quietly shorter file.
Pinned Top 4 sits above the full grid, full-poster cards with a star tier ribbon, year and runtime overlay. Hover any card to reveal Pin / Add to list buttons.
Per-type Top 4: 4 movies, 4 series, 4 episodes, max 12 total. Each pinned slot shows its rank, star tier, year and runtime, and hovering reveals an × remove button.
Server-wide vertical feed of every rating that has a written review, with poster, reviewer, star bar, date and review text. (Reviewer name and review text blurred in this screenshot.)
Letterboxd-compatible sync pane: username input, hourly auto-sync toggle, one-click Sync Now (pulls RSS ratings + watchlist + likes scrape), ZIP drop zone for full exports, Import Top 4 from your public profile, Diagnose button for matcher diagnostics, and Clean dead ratings to purge zombie entries after a library rebuild.
StarTrack adds itself to Jellyfin's web interface by injecting a script into it. That means it appears anywhere you use Jellyfin in a browser:
| Works | Does not work |
|---|---|
| Jellyfin Web in any desktop browser (10.11, 12.0, 12.1) | Roku app |
| Jellyfin Web on phones/tablets | Native Android / iOS apps |
| LG webOS and Samsung Tizen TV browsers | Android TV / Fire TV apps |
| Jellyfin Media Player (desktop) | Kodi and other external clients |
The native apps do not load Jellyfin's web interface, so there is nothing for StarTrack to attach to and no plugin can change that. Your ratings are still recorded server-side, so anything you rate in a browser is visible to everyone and counts everywhere — you just cannot rate from those apps.
/jelly); StarTrack applies it to injected assets, plugin APIs and native Jellyfin media requestsStarTrack uses ASP.NET Core middleware to inject its widget at runtime. No File Transformation plugin required.
https://raw.githubusercontent.com/ZL154/jellyfin-plugin-startrack/main/manifest.json
<jellyfin-data>/plugins/StarTrack/Jellyfin.Plugin.InternalRating.dll
Jellyfin 12 moved to .NET 10, so one DLL cannot serve both server generations. StarTrack ships two, built from the same source:
| Your Jellyfin | Version to install | Ends in | .NET |
|---|---|---|---|
| 10.11.x | 1.8.1.0 | .0 | 9 |
| 12.x | 1.8.1.1 | .1 | 10 |
Installing from the repository? Ignore all of this — the catalogue only offers your server the build it can run. This matters only for manual installs.
The two differ only in which Jellyfin they are compiled against. Features, settings and your data are identical, and upgrading your server from 10.11 to 12 needs nothing more than letting the catalogue update the plugin.
⚠️ If you install the wrong one, it fails silently. Jellyfin logs nothing, the plugin simply does not appear and
/Plugins/StarTrack/Debugreturns 404. If StarTrack has "vanished" after a manual install, check the last digit first.And installing the right one afterwards is not enough on its own. Jellyfin marks the plugin
NotSupportedthe moment it fails to load the wrong build, and that mark survives the swap — the correct DLL is then skipped as "disabled" at every start. After replacing the DLL, open Dashboard → Plugins, find StarTrack, and Enable it (or set"status": "Active"in itsmeta.json), then restart. The self-check below calls this out: no answer at all from the plugin, on a manual install, is almost always this.
Dashboard → Plugins → StarTrack → "Is StarTrack working?" → Run self-check.
It checks eight things — five on the server, three that only your browser can answer — and tells you which one is failing:
<script> tag is actually in the page's HTMLIf nothing at all appears, that answer is itself the diagnosis: the plugin is installed but not running. On a manual install that nearly always means the downloaded build doesn't match your Jellyfin version — see Which build do I need?.
The panel also produces a paste-ready summary for bug reports.
Then open any Movie, TV Show, or Episode detail page: the ☆ Rate pill and one
StarTrack badge should appear, and clicking either should open the rating panel.
A floating pill appears at the bottom-right of every Movie / Series / Season / Episode detail page. StarTrack also places one clickable badge alongside Jellyfin's native ratings. Click either control - or focus it with a keyboard/TV D-pad and press Enter/OK - to:
Admins can use a compact rating-only badge, move poster badges to any corner and choose Normal, Large or Large (TV only) sizing. Users can override the size for their current device from My Ratings → ⚙ Preferences. On TV, unrated media keeps a dimmed ☆ Rate badge so the remote always has a focus target.
Open it from My Ratings in the Jellyfin sidebar, from the ★ My Ratings link in the top bar (Jellyfin 12.1), or from the ★ My Ratings button in any rating panel. Esc or the browser's Back button closes it. The view selector at the top has seven tabs:
Each view supports search, sort, star filter (where applicable) and a type filter: All / Movies / TV Shows / Seasons / Episodes / Anime. Hovering a card reveals its actions - pin to Top 4, add to a list, and on your own watchlist, remove from it.
Dashboard → Plugins → StarTrack → Is StarTrack working? runs eight checks - plugin loaded, script injected, widget executed, token fresh, FlareSolverr reachable and on the same IP as Jellyfin, and so on - and gives you a summary to paste into a bug report. If the page itself returns 404, the plugin is installed but not running, which almost always means the wrong build for your Jellyfin version.
Serializd covers television; Letterboxd covers film. The two halves have different requirements on purpose.
To import (no password):
That is all importing needs - your diary is a public page, so StarTrack just reads it. Nothing is stored beyond the username.
To also push your ratings out: 4. Tick Also push my TV ratings to Serializd, which reveals the credential box 5. Enter your Serializd email and password, click Verify login to check them, then Save 6. Click Push now, or leave it to the hourly task
Choose which kinds to push with the Series / Season / Episode ratings toggles. Include written reviews is off by default: Serializd only keeps review text on a log entry, so a rating carrying a review is sent as a log rather than a bare rating.
Serializd has no public API tokens, so pushing genuinely requires the account password. It is encrypted at rest with the same key ring as the Letterboxd credential and is never returned by any endpoint. If you would rather not store one, use import only - it loses nothing except the outbound direction.
trakt.tv/activate.Ratings only import for items that exist in your Jellyfin library; titles you rated on a service but don't own can't be matched.
Open Dashboard → Plugins → StarTrack to configure the server-wide controls:
AutoExportDaily to true and AutoExportFormat to csv or json in the plugin configuration. The v1.6.4 visual admin form does not expose these two advanced fields yet. The task runs at 03:00 UTC and writes to <jellyfin-data>/data/InternalRating/exports/.If Jellyfin is hosted below a reverse-proxy path, set Jellyfin's normal BaseURL. StarTrack automatically uses that prefix; no separate StarTrack path setting is required.
All endpoints are under /Plugins/StarTrack/. Every endpoint requires Jellyfin authentication unless otherwise noted.
| Method | Endpoint | Description |
|---|---|---|
GET | /Ratings/{itemId} | Average + every user's rating and review |
POST | /Ratings/{itemId} | Submit / update your rating {"stars":4,"review":"..."} |
DELETE | /Ratings/{itemId} | Remove your rating |
GET | /MyRatings?limit=N | All your rated items, newest first |
GET | /Recent?limit=N | Recent ratings across all users |
GET | /Stats | Server-wide rating count |
GET | /ExportCsv | Download your ratings as Letterboxd-compatible CSV |
POST | /BackfillNativeRatings | Copy all existing StarTrack ratings into Jellyfin's native per-user field (elevated admin only; idempotent) |
| Method | Endpoint | Description |
|---|---|---|
GET | /MyWatchlist | Your watchlist |
GET | /EveryonesWatchlist | Every user's watchlist aggregated, with per-item user lists |
POST | /Watchlist/{itemId} | Add an item to your watchlist |
DELETE | /Watchlist/{itemId} | Remove an item |
GET | /MyLikes | Your liked films |
POST | /Likes/{itemId} | Like an item |
DELETE | /Likes/{itemId} | Unlike an item |
GET | /MyFavorites | Your Top 4 (max 12 across types) |
POST | /MyFavorites | Replace your favourites {"itemIds":[...]} |
GET | /Interactions/{itemId} | Combined watchlisted/liked/favourite status for one item |
GET | /Recommendations?limit=N | Personalised picks weighted by your top genres |
| Method | Endpoint | Description |
|---|---|---|
GET | /MyDiary?limit=N | Your chronological diary with rewatches |
POST | /Diary | Manually add a diary entry ({"itemId","watchedAt","stars","review","rewatch"}) |
DELETE | /Diary/{entryId} | Remove a diary entry |
| Method | Endpoint | Description |
|---|---|---|
GET | /Lists | All lists on the server |
POST | /Lists | Create a list {"name","description","collaborative"} |
GET | /Lists/{listId} | Single list with all items |
DELETE | /Lists/{listId} | Delete (owner only) |
POST | /Lists/{listId}/Items | Add a film {"itemId":"..."} |
DELETE | /Lists/{listId}/Items/{itemId} | Remove a film |
| Method | Endpoint | Description |
|---|---|---|
GET | /Letterboxd/Settings | Your Letterboxd username + auto-sync state |
POST | /Letterboxd/Settings | Save username + auto-sync toggle |
POST | /Letterboxd/SyncNow | Pull ratings (RSS) + watchlist (RSS) + likes (HTML scrape) |
POST | /Letterboxd/Import | Upload ZIP or CSV - auto-extracts ratings.csv, diary.csv, watchlist.csv, likes/films.csv |
POST | /Letterboxd/ScrapeFavorites | Scrape your Letterboxd profile's "favourite films" section |
POST | /Letterboxd/Cleanup | Purge ratings whose library item no longer has a file on disk |
GET | /Letterboxd/Diagnose | Library matcher diagnostic + sample of normalised titles |
GET | /Letterboxd/Account | Write-back config. Reports whether a password/cookies are stored, never returns them |
POST | /Letterboxd/Account | Save direction, credentials and per-kind push toggles |
POST | /Letterboxd/VerifyLogin | Test a Letterboxd sign-in without writing anything |
POST | /Letterboxd/PushNow | Push ratings/watched/likes now, returns a per-kind report |
GET | /Letterboxd/ExportCsv | Download ratings as a letterboxd.com/import CSV (no password required) |
| Method | Endpoint | Description |
|---|---|---|
GET | /Serializd/Account | Direction, username, per-kind toggles. Reports whether a password is stored, never returns it |
POST | /Serializd/Account | Save direction, username, credentials and push toggles |
POST | /Serializd/VerifyLogin | Test a Serializd sign-in without writing anything |
POST | /Serializd/SyncNow | Import the public diary now - needs a username only, no password |
POST | /Serializd/PushNow | Push series/season/episode ratings now, returns a per-kind report |
All under /Plugins/StarTrack/ExternalSync/. {provider} is trakt, simkl, or yamtrack.
| Method | Endpoint | Description |
|---|---|---|
GET | /Status | Connection state + direction for every provider |
POST | /{provider}/StartAuth | Begin device-code / PIN login |
POST | /{provider}/PollAuth | Poll for auth completion + store token |
POST | /Yamtrack/Connect | Connect Yamtrack with a base URL + token |
POST | /{provider}/SetDirection | Set Off / ExportOnly / ImportOnly / TwoWay |
POST | /{provider}/Sync | Run a sync now |
POST | /{provider}/BackfillWatched | One-shot library backfill (marks watched / repairs dates) |
POST | /{provider}/Disconnect | Remove the stored token for a provider |
GET | /Export?format=csv|json|imdb|yamtrack | Download Letterboxd CSV (default), JSON, or IMDb/Yamtrack-compatible CSV |
POST | /Import?format=csv|json | Import ratings from an uploaded CSV/JSON body (5 MB limit) |
| Method | Endpoint | Description |
|---|---|---|
GET | /Members | All visible members with avatar, totals, Top 4, follow flag |
GET | /MembersSearch?q= | Same as /Members, filtered by name |
GET | /Members/{userId}/Profile | Light profile bundle (recents, top-4, watchlist, likes, diary preview) |
GET | /Members/{userId}/Stats | Heavy stats - histogram, genres, directors, actors, decades, hours, calendar heatmap, on-this-day, most-rewatched, year cards |
GET | /Members/{userId}/Reviews | All of a user's reviews |
GET | /Members/{userId}/Followers / /Following | Follow graph |
POST | /Members/{userId}/Follow / /Unfollow | Follow / unfollow a member |
GET | /Activity?scope=following|everyone&limit=N | Chronological feed of recent ratings + diary + reviews |
GET | /Compare?a={userIdA}&b={userIdB} | Pearson similarity, both histograms, biggest disagreements, films-each-loved |
GET | /MyPrivacy | Your privacy flags (hide from members / follower count / following / stats / activity) |
POST | /MyPrivacy | Update your privacy flags |
| Method | Endpoint | Description |
|---|---|---|
GET | /PublicConfig | Admin toggle state + supported languages (no auth) |
GET | /Translations/{lang} | JSON translation bundle for a language (no auth) |
GET | /AdminConfig | Current plugin configuration (admin only) |
POST | /AdminConfig | Save plugin configuration (admin only) |
| Method | Endpoint | Description |
|---|---|---|
GET | /Widget | Embedded widget JavaScript (no auth) |
GET | /StandalonePage?token=...&userId=... | Self-contained My Ratings WebView shell; fetchable before it seeds the supplied Jellyfin credentials |
GET | /WhoAmI | Auth debug for the current session |
GET | /Debug | Diagnostic report (no auth) |
All data is stored as plain JSON in <jellyfin-data>/data/InternalRating/:
| File | Contents |
|---|---|
ratings.json | Per-item rating + review per user |
user_interactions.json | Per-user watchlist, liked films, Top 4 favourites |
diary.json | Chronological diary entries with rewatch flag |
lists.json | All collaborative lists |
letterboxd.json | Per-user Letterboxd sync settings (username, auto-sync, last-synced state) |
follows.json (new in 1.5) | Per-user follow graph |
privacy.json (new in 1.5) | Per-user privacy flags (hide stats / activity / followers / etc) |
external-sync.json (new in 1.6) | Per-user, per-provider external-sync settings: direction, OAuth tokens, last-sync state |
exports/<userId>-<date>.csv|json (optional) | Daily per-user rating backups when automatic export is enabled |
Back them up or migrate them like any other data file - no database required. Native-rating mirroring supplements these files; StarTrack's JSON remains the source of truth.
git clone https://github.com/ZL154/jellyfin-plugin-startrack.git
cd jellyfin-plugin-startrack/InternalRatingSystem
dotnet publish -c Release -o ./publish_out
The compiled DLL is at publish_out/Jellyfin.Plugin.InternalRating.dll.
Version note: The plugin must be compiled against the
Jellyfin.ControllerNuGet package version that exactly matches your server. The csproj currently targets10.11.6. Update<PackageReference>if your server version differs.
Issues and pull requests are welcome.
What is new in the current release stays at the top of Features; everything it describes is also folded into the feature sections above, so those read as a description of StarTrack today. Older release notes live here.
Jellyfin 12. StarTrack now runs on Jellyfin 12 and on 10.11, from one codebase, as two builds - see Which build do I need?. Installing from the repository picks the right one for you.
Letterboxd, repaired
Diary
Ratings
3.5, the default), both (3.5 (7/10)), or out of ten (7/10). Applies everywhere an average is displayed, so a 3.5 sitting next to IMDb's 7 no longer reads as a disagreement. Display only - you still rate with five stars, nothing is converted.Diagnostics
Contributed
Fully translated into all eight languages.
The largest release so far: a second external service, a new thing you can rate, and the Letterboxd integration finally going both ways.
External services
Rating seasons
Diary
TV and clients
Contributed
☆ Rate target on TV./jelly.(1) count.GET /Plugins/StarTrack/StandalonePage returns a self-contained HTML page that hosts the StarTrack overlay without the surrounding Jellyfin web shell. It is designed for WebViews, mobile apps, smart-TV launchers, third-party clients and kiosk displays. The shell accepts ?token=...&userId=..., seeds Jellyfin's credential storage, loads the existing widget and opens My Ratings without changing normal Jellyfin Web behaviour.↻ Rewatch pills and a denser three-column row layout.StarTrack is built and maintained in my spare time. If it's useful to you and you'd like to support ongoing development, any of these means a lot:
Not expected, just appreciated. Contributions - issues, PRs, translation fixes - are equally valuable.
MIT - © 2025 ZL154
C#
62.3%
JavaScript
35.2%
HTML
2.5%