3.0.0 (2026-09-21)
⚠ BREAKING CHANGES
- server: Redis 8.0 or newer is now required. The addon refuses to start against an older server and names the version it found. Instances pinning an older Redis image should move to redis:latest or an 8.0+ tag; the compose file in the README already uses redis:latest.
Features
- cache: add Redis hash primitives for meta components (ac95acb)
- cache: keep each title's meta components in one Redis hash (a7d4e3f)
- cache: map meta components onto hash fields (190529a)
- cold-store: add completeness classifier (03e495e)
- cold-store: add strict mode flag and partial tier TTL (67e192b)
- cold-store: demote incomplete titles to a partial tier (e624412)
- collections: folders inside folders, as nested box sets on Jellyfin and flattened for Fusion and Nuvio (e0a1c81)
- collections: serve a layout's images through the instance's image cache (922a9fa)
- collections: show a collection or classic row to the server users made of its tags (85cd281)
- dashboard: a Jellyfin tab showing what the server recorded for a configuration (96950d2)
- dashboard: recently played by day with one tile per show, a rows selector, and counts that fold id spellings (2ce61f2)
- dashboard: report container memory and OOM kills (026b5c5)
- dashboard: surface event loop lag and slow Jellyfin requests (1573385)
- image-cache: keep collection images in their own class (1bd1db0)
- jellyfin: a thumbs-down on a series drops it (0c5cba0)
- jellyfin: answer a search from the search hits instead of hydrating every result (8d29a1a)
- jellyfin: answer an anime search from the MAL and Kitsu hits (0c55106)
- jellyfin: build the shelves from an episode index instead of every show's meta (9f42f73)
- jellyfin: carry an item's versions inline when it is opened (3aa7942)
- jellyfin: describe a stream's video, audio and subtitles from what the stream addon parsed (780a5fa)
- jellyfin: fill the More Like This row from TMDB recommendations (6b80ff2)
- jellyfin: fill Upcoming with the next episode of every caught-up show (f8c770e)
- jellyfin: give every user a card carrying its own trackers, watchlist and skip source (85cd281)
- jellyfin: keep a client's display preferences and serve custom CSS (8ecd4ea)
- jellyfin: keep the settings a client saves for its user (5326b6a)
- jellyfin: lay the dialog out as a user list with an editor (419595c)
- jellyfin: leave the Latest rows to configurations that want them (cdecb92)
- jellyfin: let a configuration choose whether a title opens with its streams resolved (cccc8c5)
- jellyfin: let a user pick where skip markers come from (554f714)
- jellyfin: let a user play from its own stream addon (cdecb92)
- jellyfin: let each user pick their trackers, skip markers and watchlist services (419595c)
- jellyfin: let the main user pick tags and read every tracker (214aec2)
- jellyfin: offer intro, recap and credits skips from PublicMetaDB and IntroDB (3f9ef54)
- jellyfin: offer the subtitle files a stream carries and the stream addon finds (1b1ed19)
- jellyfin: open a cast member to their page and filmography (6b80ff2)
- jellyfin: pick a watchlist per service and shelf (85cd281)
- jellyfin: play a trailer addon's direct links in the client (c3b30b5)
- jellyfin: read and write favourites through the PublicMetaDB watchlist (09133a7)
- jellyfin: read every connected tracker into the resume shelf under Automatic (214aec2)
- jellyfin: read PublicMetaDB for the resume shelf, the watched ticks and Next Up (baedec6)
- jellyfin: report Atmos and DTS variants as an audio stream's profile (b5c732b)
- jellyfin: serve the collection builder's layout as libraries and box sets (0cc02ef)
- jellyfin: serve the watchlist as a client's favourites (419595c)
- jellyfin: show new seasons and unreleased watchlist films as upcoming (8ecd4ea)
- jellyfin: take a client's user data update, clearing the resume point everywhere (ea0ffc9)
- jellyfin: take a source's duration and bitrate from the stream addon when it knows them (1b1ed19)
- jellyfin: take skip markers from AniSkip and say in the version picker why a title has no streams (5887384)
- jellyfin: write a playing position to the table on an interval (780a5fa)
- language-map: report when 3-letter resolution degrades to eng (5905f07)
- meta: stamp localization completeness on tmdb and tvdb assemblies (9d38057)
- recommendations: accept any interval between rewrites (cdecb92)
- recommendations: write a row again before it lapses (ffc598b)
- redis: set the server's own cache settings on startup (fd5901c)
- search: report a provider's refusal as a card in the row (9ccf66f)
- search: tag a search catalog the way a catalog is tagged (6e84b0d)
- settings: make the log level editable from the dashboard (9f3a709)
- settings: offer 45 tunables the dashboard never showed (9f3a709)
- settings: offer the watched snapshot grace period (1ee4c47)
- simkl: follow each user's own V2 request allowance (99260ea)
- simkl: import custom lists as catalogs (18199c8)
- simkl: sign in through AUTH V2 when a V2 client is set (59eb0b0)
- tmdb: classify whether title and overview are in the user's language (2a4f9b9)
- trailers: let a user take trailers from a trailer addon (3445161)
- tvdb: report which language a translation came from (b3d7040)
- watch-tracking: answer the watch_state pull with tracker and own state (198bf7c)
- watch-tracking: declare the watch_state resource and take its push spelling (41f92d6)
- watch-tracking: take a season or series mark as one event (10ec95b)
Bug Fixes
- ai-search: let the web search switch decide grounding, and fall back when it is refused (c2ad6d2)
- cache: keep a rebuild from dropping another profile's components (e60e013)
- cache: keep a title's status through meta reconstruction (38cf1d6)
- cache: log configured key names only, and a cached empty page at debug (9db2d70)
- catalogs: end a filtered page on the upstream's own short page (26bb17f)
- catalogs: end a filtered page only on an empty upstream page (4b55224)
- catalogs: end a filtered page only on an empty upstream page (050fb26)
- catalogs: resolve the Simkl Up Next catalog's episode ids (606db08)
- clamp reported playback progress to 100 (eea2b03)
- cold-store: evict whole titles instead of their oldest rows (5e949f7)
- cold-store: judge language only, never artwork (7f8b63d)
- collections: subfolder images go through the image cache, and the editor checks subfolder catalogs (50c9b68)
- dashboard: accept a single-label hostname in the image cache policy rules (078ee77)
- dashboard: copy needs the clipboard API, point at export otherwise (0d7170f)
- dashboard: keep the Jellyfin tab's selection across the mobile breakpoint (10aff67)
- dashboard: page and search the user list instead of loading every configuration (718b082)
- dashboard: show evicted keys from Redis stats (745fe37)
- database: clear a deleted user's config cache after the row is gone (dcc9cd2)
- images: bring a poster to 2:3 when it is stored (7a386d2)
- jellyfin: an empty watched history is not a failed read (6adb013)
- jellyfin: an exhausted MDBList quota no longer fails the libraries (5934798)
- jellyfin: answer a season or series mark once every episode holds it (ea0ffc9)
- jellyfin: answer a series page's Next Up with that series' episode (d391203)
- jellyfin: answer an item in full when the client asks for its MediaSources (05e0100)
- jellyfin: answer an opened item under the id it was opened by (6b80ff2)
- jellyfin: answer item art with CORS headers so a page script can read it (8ecd4ea)
- jellyfin: answer the handshake the way a client expects (44bacb1)
- jellyfin: apply a season or series mark to each of its episodes (ad78577)
- jellyfin: apply the watched state to search results and the home Latest rows (659dad1)
- jellyfin: build Next Up once per request shape and stop rescanning aliases (7f66d0c)
- jellyfin: cap a folder page only for an eager request, fill a row-sized one (f2a43d7)
- jellyfin: cap only folder pages, serve cached art in one hop at display size (93bf83a)
- jellyfin: clear a watch that was unmarked on the tracker (53b9e7f)
- jellyfin: close a tracker session a mark leaves behind (718b082)
- jellyfin: continue a folder walk from its last page and cap list pages (bcbd638)
- jellyfin: count a show's plays under every id spelling, and from this server's own records (9c05c1f)
- jellyfin: count a show's progress over its aired episodes, specials left out (233aad1)
- jellyfin: count an episode as watched under every id it goes by (d391203)
- jellyfin: count live sessions across processes and restarts (f7d9c71)
- jellyfin: count paused viewers on the dashboard instead of hiding them (f3835d1)
- jellyfin: date an imported watch with the tracker's own, and skip a sync pass the seen markers cannot be read for (0fffeb6)
- jellyfin: describe each tracker source on its own (c3b30b5)
- jellyfin: drop a resume build begun before an invalidation and read the newest alias (e110e45)
- jellyfin: favourites answer IsFavorite, page the watchlist, and can be left to this server (e8dbcdc)
- jellyfin: favourites follow the watchlist picks, and a heart set here stands only until the shelves catch up (b53719e)
- jellyfin: fill Next Up from what this server saw finished, without a tracker (ab7c7d3)
- jellyfin: flush remembered artwork on shutdown (f3835d1)
- jellyfin: follow the most recent shows played here, not the most recent rows (654696e)
- jellyfin: give a search result the id of its own kind whichever catalog found it (5eda47e)
- jellyfin: give anime and single-kind catalogs a CollectionType on their view (af6654f)
- jellyfin: give directors and writers their portraits (b08a232)
- jellyfin: give every library item a DateCreated (ca941df)
- jellyfin: hand Apple clients a trailer link the YouTube app opens (6b80ff2)
- jellyfin: hold a fetched meta briefly so one read serves a client's chain of requests (6a64889)
- jellyfin: hold failed tracker and catalog reads instead of retrying them per request (b5b913b)
- jellyfin: keep a session open while a client only pings (f3835d1)
- jellyfin: keep a show PublicMetaDB lists as dropped out of Next Up and Upcoming (6f3e000)
- jellyfin: keep a show the tracker lists as dropped out of Next Up and Upcoming (c6295e0)
- jellyfin: keep a user's own picks when it is the same person as the account (7a386d2)
- jellyfin: keep a watched episode airing today out of Upcoming (554f714)
- jellyfin: keep a watched snapshot and its digest for their whole TTL (4f03099)
- jellyfin: keep background work to configurations a client uses, and drop per-request waste (9d458d6)
- jellyfin: keep Upcoming to caught-up shows and Next Up to aired episodes (5326b6a)
- jellyfin: key an item id on the title's kind, not the catalog it was listed from (5b0b56c)
- jellyfin: leave a view untyped unless its catalog is movies or series (a8f6ac1)
- jellyfin: let only a dated, newer watch finish a paused row (3e43ea0)
- jellyfin: list a saved configuration's catalogs as libraries at once (97d11e3)
- jellyfin: list an in-progress episode once whatever ids it is stored under (2524b58)
- jellyfin: log the playstate sync under the user it runs for (e8827ea)
- jellyfin: look up a long history in slices during tracker sync (43ea204)
- jellyfin: map tracker episodes with the configuration's own TMDB key (5595e20)
- jellyfin: mark a season or series in one pass and one tracker call (5887384)
- jellyfin: mark a series watched on a tracker that reports no total (659dad1)
- jellyfin: mark a stale resume row played once the tracker's history lists it (214aec2)
- jellyfin: mark a title played at the trackers' 80% and measure it over the file's own length (7b44ac4)
- jellyfin: match AniSkip markers to the file's own release and follow the version picked to play (234eac8)
- jellyfin: move a show from Upcoming to Next Up on the tracker's own air time, and report a resume the client sends at zero (39983a4)
- jellyfin: name a source by the release the addon says it is (1b1ed19)
- jellyfin: Next Up skips an undated episode of a show whose episodes carry dates (3bd1c0b)
- jellyfin: open a title at once and resolve its versions from the picker (b51ef40)
- jellyfin: open an anime episode under the series its row came from (554f714)
- jellyfin: page the MDBList Next Up list and read the whole watched history (baedec6)
- jellyfin: page the unfiltered library listing without skipping items, and end it (234aa19)
- jellyfin: page to the true end, count filtered offsets, keep learned lengths per account (d958205)
- jellyfin: put media sources back on every episode (92ba6e3)
- jellyfin: read a drop or undrop on MDBList straight away (04ede9a)
- jellyfin: read a shelf's series metas ahead for Odin so its per-series fetches land on the memo (39250a6)
- jellyfin: read and write MDBList and Simkl through their rate limiters (41bae1e)
- jellyfin: read favourites from the addon's watchlist catalogs (4a8977d)
- jellyfin: read the unified MDBList watchlist when both shelves are picked (e880900)
- jellyfin: record plays and watched marks whatever the watch tracking mode (331ed72)
- jellyfin: refresh the tracker snapshot after a mark (82b0c26)
- jellyfin: register the own-plays read and watchlist memo settings (21cfc6f)
- jellyfin: report the version a current server reports (44bacb1)
- jellyfin: round every millisecond to ticks conversion (e5aca2d)
- jellyfin: say why a user data update was refused (672f956)
- jellyfin: seed MDBList's Next Up from the latest watched episode (606db08)
- jellyfin: seed Next Up on Simkl from shows being watched only (50bc6bd)
- jellyfin: send every date as an ISO timestamp (44ade31)
- jellyfin: send SortName on every item (1e7fb76)
- jellyfin: send the show's art on its seasons and episodes as the API does (6ffbc6b)
- jellyfin: send UnplayedItemCount only when the count is known (c68620c)
- jellyfin: serve collection covers from the image cache instead of cropping them (081d6a8)
- jellyfin: shape the posters the host list was missing (57e5dcd)
- jellyfin: share and hold a complete tracker watchlist read for a minute (5ef6175)
- jellyfin: show a title once in Continue Watching whatever spellings the table holds (75e0eac)
- jellyfin: stamp resume items with their last play date (7872aea)
- jellyfin: start a series' episode list at the episode asked for and page it (a9995d3)
- jellyfin: start the playstate sync after the id mappers are loaded (06ef725)
- jellyfin: stop reshaping episode stills into posters (7e86127)
- jellyfin: stop the dashboard blocking the server (a8d684f)
- jellyfin: sync only the configurations a client uses, and date the imports already held (07d16de)
- jellyfin: take a short catalog page as the last one (8ecd4ea)
- jellyfin: take a show's aired episodes from the episode index instead of building the meta in the response (b31c8fb)
- jellyfin: take Next Up from MDBList's own list and fill the page (3e43ea0)
- jellyfin: warn when a configuration has no stream addon (f3835d1)
- jellyfin: write a film's playstate under every id its meta carries (3e43ea0)
- keep object-shaped cache entries for their whole TTL (5887384)
- kitsu: bound every request (5995f0a)
- language-map: treat an unmappable language as unresolved, not English (437ceaa)
- logs: mask PublicMetaDB keys like other credentials (9c3959f)
- name the MDBList scrobble action in its log lines and include the response body on failure (88b8bc1)
- open the discover builder in a language TMDB has no genre names for (118e50c)
- publicmetadb: a list not on the account shows as an empty row (34883fe)
- publicmetadb: tell a watchlist from a custom list on both sides (034e126)
- recommendations: keep a recommendation catalog off the home board when asked (05ab179)
- recommendations: leave recommendation and MovieLens catalogs out of a shared export (ffc598b)
- recommendations: log the refresh sweep under the user it runs for (aae68f6)
- scripts: see tunables read through envInt (9f3a709)
- server: boot on a Redis that will not answer COMMAND (1e9f611)
- server: refuse to start on a Redis the meta cache cannot use (68aa736)
- settings: split the artwork TTL that drove two different caches (9f3a709)
- simkl: disconnecting one configuration keeps the token others share (eb846e3)
- simkl: pace a delete like the write it is (9348c3e)
- simkl: send a write through the same path a read takes (9348c3e)
- split extras on & for loopback catalog calls (56b5535)
- take Trakt out of the watch tracking list and the Jellyfin tracker choice (a3e3258)
- tmdb: cache a missing external ids record as an answer (66c5c3f)
- tmdb: cache person search, details and credits like the other endpoints (6b80ff2)
- treat a missing TMDB external ids record as no ids (5aa3bca)
- update a film's PublicMetaDB resume point instead of failing on the second save (e690cd8)
- warmer: stop writing catalog metas into the shared meta cache (b6638e5)
- watch-tracking: accept episode ids spelled with MyAnimeList (f3835d1)
- watch-tracking: never take a film's anime entry for its franchise show (df4326d)
- watch-tracking: number episodes the way TMDB does on both sides of a tracker (df4326d)
- watch-tracking: send a mark that repeats an old decision to the trackers (82b0c26)
- watch-tracking: treat an MDBList miss as a failure and delete a session on a watch (606db08)
Performance Improvements
- jellyfin: answer with the watched snapshot already held while a fresh one is read (1ee4c47)
- jellyfin: build the episodes a listing returns, not the whole show (f3835d1)
- jellyfin: keep remembered artwork in one hash per scope (699f43e)
- jellyfin: read watched state for the page, not every child (ca3b400)
- jellyfin: reshape only the posters that need it (37b6794)
- jellyfin: send episode media sources only when asked (2f1ae9c)
- jellyfin: stop scanning the keyspace for seen configurations (90e7ad5)
- jellyfin: walk catalog pages a few at a time and remember their lengths (ca54502)
- jellyfin: write a listing's artwork in one command (45d1a2c)
- mdblist: read catalog pages from one larger list request (459e6fb)
- mdblist: read catalog pages from one larger list request (4d19ef8)
- take request-path waste off the addon (6fc867a)