2026.07.02 #220
tsirysndr
announced in
Announcements
2026.07.02
#220
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
[2026.07.02]
Added
jellyfin: Playlists API — full CRUD from the OpenAPI spec so third-party clients (Findroid, Streamyfin, Moonfin, Amcfy) can create, edit, and delete playlists through the standard surface without falling back to the Subsonic bridge;POST /PlaylistsreturnsPlaylistCreationResult,GET/POST /Playlists/{id},GET/POST/DELETE /Playlists/{id}/Items,POST /Playlists/{id}/Items/{itemId}/Move/{newIndex},GET /Playlists/{id}/Users, andDELETE /Items/{id}(which only accepts playlists — tracks/albums/artists return 403); backed by the existingrockbox-playlists::PlaylistStoreso the Subsonic and Jellyfin surfaces share state; a new virtual "Playlists"CollectionFoldershows up in/Users/{uid}/Viewsalongside Music so clients render a top-level tile;PlaylistItemIdvalues are synthesized deterministically from(playlist_id, position)so entry-id round-trips through remove/move without a mapping tablejellyfin: Favorites API — newjf_favorites (kind, native_id, favorited_at)migration covers tracks, albums, artists, and playlists; writes for tracks/albums mirror torockbox-library's existingfavouritestable so smart-playlistis_likedrules and the Subsonic bridge stay in sync, and reads take the union so likes added elsewhere still surface here;POST/DELETE /UserFavoriteItems/{id}(10.9+) and legacyPOST/DELETE /Users/{uid}/FavoriteItems/{id}both return the freshly-updatedUserItemDataDto;?IsFavorite=trueand?Filters=IsFavoriteon/Itemsroute through alist_favoritesorchestrator that honoursIncludeItemTypesacross all four kindsjellyfin: UserData API — newjf_user_data (kind, native_id, played, play_count, playback_position_ticks, last_played_at, likes, rating, updated_at)composite-PK cache backs the spec's per-item user-data fields; for tracks the store merges withrockbox-playlists::track_statson read (whicheverplay_countis higher wins, engine'slast_playedfills a null) so audio-engine counters surface on the Jellyfin side without a sync step;GET /UserItems/{id}/UserData(10.9+) and legacyGET /Users/{uid}/Items/{id}/UserDataroll upIsFavorite+ playback + rating + likes; the matchingPOSTaccepts a partialUpdateUserItemDataDtowhere unset fields preserve stored state per spec, andIsFavoriteis forwarded to the favorites store so both surfaces stay coherent; all four*_to_dtohelpers now emit real user-data fields instead of hardcoded zerosjellyfin: InstantMix API — seed-based mix generator coversGET /Items/{id}/InstantMix(generic dispatcher) plus the legacy/Songs,/Albums,/Artists,/Artists/InstantMix?id=,/Playlists, and/MusicGenres/{name}per-kind aliases; algorithm anchors the seed (or its own tracks), fills with same-artist_idmatches, then same-genre_id, then a random tail from the whole library; track seeds keep position 0 anchored through the shuffle so "play me first" behaviour is preserved; results dedup by native id and truncate toLimit(default 50)jellyfin: Lyrics API — reads a.lrc(synced) or.txt(plain) sidecar next to the audio file;GET /Audio/{id}/Lyricsreturns aLyricDto(withLyricLine.Startin 100-ns ticks and aLyricMetadatablock populated fromar/al/ti/by/offset/length/re/ve/auheader tags),POSTaccepts raw LRC / plain text OR a JSONLyricDtobody (distinguished byContent-Type— JSON re-serializes as LRC so external players can read the file),DELETEremoves sidecars idempotently,GET /Audio/{id}/RemoteSearch/LyricsandGET /Providers/Lyricsreturn empty arrays since no remote providers ship; LRC parser handles header tags, multi-timestamp expansion, and offset application; unit tests cover synced/multi-timestamp/plain parsingjellyfin: Similar API backed by Last.fm and MusicBrainz plugins —GET /Items/{id}/Similarplus legacy/Artists/{id}/Similarand/Albums/{id}/Similarroute through a plugin orchestrator that seedsartist.getsimilar/track.getsimilarfrom Last.fm and canonicalizes MBIDs through MusicBrainz before matching results back to the local library; artist seed → local artist by exact/case-insensitive name; album seed → same-artist expansion (Last.fm has no album endpoint) → their local albums; track seed → local(title, artist)lookup with the returned artist MBID cross-referenced through MB; plugins are gated onlastfm_api_key/musicbrainz_user_agent— no key means no fetches, andSimilarResultshort-circuits to empty rather than falling back to random suggestions; both plugin activations log at startupjellyfin: RemoteImage API backed by Cover Art Archive —GET /Items/{id}/RemoteImagesreturns candidate cover art (honourstype,startIndex,limit),GET /Items/{id}/RemoteImages/Providerslists active providers, andPOST /Items/{id}/RemoteImages/Download?imageUrl=&type=fetches the URL, saves under~/.config/rockbox.org/covers/using the samemd5(album_id)filename scheme the audio scanner uses, and updatesalbum.album_artin one shot; MusicBrainz search resolves(artist, album)to an MBID via a Lucene-escapedrelease-groupquery; CAA client handles both historical (small/large) and size-suffixed (250/500/1200) thumbnail shapes; track seeds fall back to their parent album so the "change cover art" action from a now-playing view works; gated on the samemusicbrainz_user_agentas the Similar pluginjellyfin: Genre Browsing API — newKIND_GENRE+remember_genreso genres get stable GUIDs;genre_to_dtopopulatesSongCount/AlbumCountfor chip UIs;GET /Genresand/MusicGenres(sorted list, honourssearchTerm/nameStartsWith/ range / pagination);GET /Genres/{name},/MusicGenres/{name}, and legacy/Users/{uid}/Genres/{name}with case-insensitive fallback;items_implgained aparentId=<genre_guid>branch (dispatches onIncludeItemTypesto return tracks / albums / artists under the genre) and anIncludeItemTypes=MusicGenreshorthand for the flat listjellyfin: Filters API —GET /Items/FiltersreturnsQueryFiltersLegacy(flat genre name list),GET /Items/Filters2returnsQueryFilters(NameGuidPairlist so chips round-trip through?genreIds=<guid>without a name→guid lookup), plus the pre-10.9GET /Users/{uid}/Items/Filtersalias; years come from distinct non-zeroalbum.year; tags and official ratings stay as empty arrays since rockbox doesn't track themjellyfin: Item Counts endpoint —GET /Items/CountsreturnsItemCountswith realSongCount/AlbumCount/ArtistCountfromrepo::*::count_filtered; unsupported kinds (movies, series, episodes, trailers, boxsets, books) stay at 0 per spec;ItemCountsums the three we surfacejellyfin: Last.fm artist enrichment — newjf_artist_enrichment (artist_id PK, mbid, bio, tags, image_url, fetched_at)cache backsOverviewandGenresonMusicArtistDTOs;LastFm::artist_infocallsartist.getInfo, cleans the trailing "Read more on Last.fm" link, extracts tags and the largest image URL;enrichment::get_artistis a cache-only read used by everyartist_to_dto(SQLite lookup — list requests stay fast),enrichment::refresh_artistenforces a 7-day TTL and upserts on demand from the detail handlers; serves stale rows on network errors so a blip doesn't blank the biojellyfin: Last.fm album enrichment — mirrors the artist flow with a newjf_album_enrichment (album_id PK, mbid, description, tags, image_url, fetched_at)cache;LastFm::album_infocallsalbum.getInfowith MBID priority over(artist, album), parses the wiki summary/content;album_to_dtoreads the cache on every call so list paths stay fast; the album detail branches initem_by_id/user_item_by_idcallrefresh_albumfirst so the returned DTO carries a hot descriptionjellyfin: home-rail routes replace the earlierempty_itemsstubs so Findroid / Streamyfin / official web actually populate their home screens —/Items/Resume+/Users/{uid}/Items/Resumereturn tracks with non-zeroPlaybackPositionTicksordered byupdated_at DESC,/UserItems/Resumeand/UserItems/Latestcover the legacy plain-array shape Findroid still uses,/Items/Suggestions+/Users/{uid}/Items/Suggestionsreturn random tracks by default (honoursIncludeItemTypes=MusicAlbum/MusicArtistandmediaType=Audio) via SQLRANDOM()so the rail refreshes each session; the generic/Itemshandler now recognizessortBy=PlayCountandsortBy=DatePlayed(plusDateLastPlayed) withsortOrder=Descending|Ascending, joiningtrackLEFT JOINtrack_statsso the "Most Played" and "Recently Played" home rails work through the standard/Itemsendpoint without dedicated handlerssettings: new optionallastfm_api_key: Option<String>andmusicbrainz_user_agent: Option<String>fields onNewGlobalSettings— the Jellyfin Similar / RemoteImage / enrichment plugins short-circuit to empty when their respective field is absent, so the plugins only activate when the corresponding credentials are present insettings.tomlFull Changelog: 2026.06.29...2026.07.02
This discussion was created from the release 2026.07.02.
All reactions