Add new APIs: Home for recently played and recently added, Artist, and Album.#1
Add new APIs: Home for recently played and recently added, Artist, and Album.#1hamoudydev wants to merge 8 commits into
Conversation
This adds support for browsing music libraries beyond just playlists: New data classes: - LibrarySection: Represents a Plex library section with type, title, etc. - Artist: Music artist with albums() and tracks() methods - Album: Music album with tracks() and artist() methods - Tag: Generic tag for genres, countries, styles, moods New PlexServer methods: - librarySections(): Get all library sections - musicSection(): Find the first music library section - artists(sectionId): Get all artists from a music library - albums(sectionId): Get all albums from a music library - tracks(sectionId): Get all tracks from a music library - artistAlbums(ratingKey): Get albums for a specific artist - albumTracks(ratingKey): Get tracks for a specific album Updated Http.kt to register Artist and Album in polymorphic serializer. Bumped version to 0.2.0 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Added installation instructions - Documented all features and API methods - Added usage examples for library browsing - Added data class reference table - Added changelog 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Both Artist and Album use @SerialName("Directory") which caused a
conflict when registered in the same polymorphic module. Removed
them from the polymorphic serializer since they're deserialized
directly via MediaContainer<Artist> or MediaContainer<Album>.
Updated README changelog to document the fix.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add recentlyPlayedTracks() - get tracks sorted by last played - Add recentlyAddedTracks() - get tracks sorted by added date - Add recentlyAddedAlbums() - get albums sorted by added date - Add onDeck() - get continue listening tracks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
joeyberkovitz
left a comment
There was a problem hiding this comment.
the classes look kind of ok (minus the comments) but I have a major concern regarding pagination
it was already a concern with playlists, but an argument could be made there that the number of playlists are relatively small and the number of songs in a playlist are also usually small
that same argument cannot be made when enumerating the entire library. as such, any function should have proper pagination on it
| @Serializable | ||
| @SerialName("MediaContainer") | ||
| data class LibrarySectionsResponse( | ||
| val size: Long, | ||
| @XmlElement(true) @SerialName("Directory") val sections: List<LibrarySection> | ||
| ) |
There was a problem hiding this comment.
So LibrarySectionsResponse is just MediaContainer<LibrarySection>?
There was a problem hiding this comment.
LibrarySectionsResponse is needed because the /library/sections endpoint returns <Directory> elements directly under MediaContainer, whereas MediaContainer expects elements in a generic list. The XML structure differs from other endpoints, requiring this custom wrapper.
There was a problem hiding this comment.
That's just how MediaContainer always works though?
See the newly added Playlist unit test. You get a <MediaContainer> with <Playlist> elements directly under the MediaContainer
Ref: ac59cf6
| val key: String, | ||
| val title: String, | ||
| val type: String, | ||
| val uuid: String? = null, |
There was a problem hiding this comment.
same comment on everything, I don't get why there's a ? = null if the default value for a nullable field is just null anyway
There was a problem hiding this comment.
The = null defaults are required for kotlinx.serialization to handle optional fields. Without explicit defaults, missing fields in API responses cause deserialization errors. This is a kotlinx.serialization requirement, not just Kotlin syntax.
There was a problem hiding this comment.
I added a unit test to illustrate that it works without this. See ac59cf6
I intentionally left fields unpopulated in the Playlist XML, such as summary and titleSort. The test still passes
| // Note: Artist and Album both use @SerialName("Directory") | ||
| // so they can't be in the same polymorphic module. | ||
| // They're deserialized directly via MediaContainer<Artist> | ||
| // or MediaContainer<Album> instead. |
There was a problem hiding this comment.
I'm not too certain about this. kotlinx serialization has pretty advanced polymorphic support. I think it can work
There was a problem hiding this comment.
Ran into issues with the XML serialization discriminator when Artist and Album were in the polymorphic module. Happy to revisit if you have a working pattern - the current approach works but I agree it's not ideal.
There was a problem hiding this comment.
TBH - the comment is just what's throwing me off. It's just irrelevant. The only reason why I had the polymorphism here is to support Playlist::items() which returns Array<MediaItem> where in theory the members of the playlist aren't strictly Tracks but rather could be other items if the API allowed it.
Nothing in this PR uses generics in responses, so this isn't needed
- Add pagination support (start, size params) to artists(), albums(), tracks() - Move Tag class to its own file (Tag.kt) - Add convenience methods to LibrarySection (artists, albums, tracks, recentlyAdded, recentlyPlayed) - Set server reference on LibrarySection objects Note on nullable defaults: The `? = null` pattern is required for kotlinx.serialization to handle optional fields that may be missing from API responses. Without default values, missing fields cause deserialization errors. Note on LibrarySectionsResponse: This wrapper is needed because the /library/sections endpoint returns Directory elements directly under MediaContainer, which requires a different serialization structure than MediaContainer<T>. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…atibility These methods are also available on Artist.albums() and Album.tracks(), but keeping them on PlexServer maintains backward compatibility with existing consumers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Thanks for the feedback! Here's what was updated:
The Let me know if you'd like any other changes! |
- LibrarySection now inherits from MediaItem instead of duplicating _server field and setServer() method - Added MediaType enum for type constants (ARTIST=8, ALBUM=9, TRACK=10) - Added paginationArgs() helper for pagination parameters - Added sectionQueryArgs() helper combining type and pagination - Refactored all library query methods to use helpers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Cleanup: removed Transient and XmlDefault imports that were no longer used after moving Tag to its own file. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
No description provided.