Skip to content

fix(android-auto): paginate large libraries and add Browse-by-Letter#1579

Open
pdebuitlear wants to merge 11 commits intoUnicornsOnLSD:redesignfrom
pdebuitlear:redesign
Open

fix(android-auto): paginate large libraries and add Browse-by-Letter#1579
pdebuitlear wants to merge 11 commits intoUnicornsOnLSD:redesignfrom
pdebuitlear:redesign

Conversation

@pdebuitlear
Copy link
Copy Markdown

Summary

Fixes #1576

Returning entire album/artist collections in a single Android Auto getChildren response causes a FAILED BINDER TRANSACTION crash due to the ~1MB Binder IPC buffer limit. This PR fixes the crash and adds a Browse-by-Letter feature for navigating large libraries.

  • Pagination: root collection responses are capped at 200 items per page; a non-playable "More… (N remaining)" node is appended when further pages exist
  • Browse by Letter: a "Browse by Letter" node appears at the top of page 1 for Albums and Artists, opening an A–Z + # index; each letter page is independently paginated with its own "More…" node if needed
  • Jellyfin API: added NameStartsWith query parameter to getItems, getArtists, and getAlbumArtists for server-side letter filtering (the # bucket falls back to client-side filtering)
  • MediaItemId: added nameFilter (HiveField 4) and pageStartIndex (HiveField 5) to encode browse state inside Android Auto node IDs
  • Docs: added docs/android_auto_browsing.md with implementation notes and Android Auto platform learnings

Test plan

  • Browse Albums/Artists on Android Auto with a large library (500+ items) — confirm no crash
  • Scroll through paginated pages using "More…" nodes
  • Open "Browse by Letter", select a letter, confirm correct items shown
  • Confirm "More…" appears within a letter page when > 200 results
  • Test offline mode — letter filtering and pagination should work from downloaded items
  • Confirm Playlists, Genres, and Tracks are unaffected (no Browse by Letter node)

padraigbutler and others added 8 commits April 4, 2026 12:11
Fixes UnicornsOnLSD#1576

Returning entire album/artist collections in a single Android Auto
getChildren response causes a FAILED BINDER TRANSACTION crash due to
the ~1MB Binder IPC buffer limit. This replaces the unbounded fetch
with a 200-item paginated approach and adds a Browse-by-Letter hybrid
for navigating large libraries by initial letter.

Changes:
- Limit root collection responses to 200 items per page; append a
  "More… (N remaining)" browse node when further pages exist
- Add a "Browse by Letter" node at the top of page 1 for Albums and
  Artists, opening an A–Z + # index; each letter page is independently
  paginated with its own "More…" node
- Add nameFilter and pageStartIndex fields to MediaItemId (HiveFields
  4 & 5) to encode browse state inside Android Auto node IDs
- Add NameStartsWith query parameter to getItems, getArtists, and
  getAlbumArtists Jellyfin API calls for server-side letter filtering
- Set CONTENT_STYLE_BROWSABLE_HINT=4 (category grid) on Albums and
  Artists root nodes to hint at non-alphabetical rendering
- Add docs/android_auto_browsing.md with implementation notes and
  Android Auto platform learnings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a new private method that fetches up to 20 recently played albums
or artists from Jellyfin (sorted by DatePlayed descending, filtered to
IsPlayed), and wires it into getMediaItems() via a recentlyPlayed
parentType dispatch branch. Offline mode returns a non-playable
placeholder item.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Jellyfin's IsPlayed filter requires all tracks to have been played,
which is far too strict for albums/artists. Sort by DatePlayed
descending and filter client-side on userData.lastPlayedDate != null
to match the pattern used elsewhere in the app.
@Chaphasilor
Copy link
Copy Markdown
Collaborator

@pdebuitlear thanks for the PR!
The approach using special items is clever, although it's of course frustrating that we have to resort to this.

I'm guessing this doesn't play well with the built-in letter selector of AA? If so, can we disable it (maybe you did that already)?

Jellyfin only sets DatePlayed on track items, not on albums or artists.
Query recently played tracks (Filters=IsPlayed works for tracks), deduplicate
by albumId or albumArtist, then fetch the actual album/artist items by ID.
@pdebuitlear
Copy link
Copy Markdown
Author

@pdebuitlear thanks for the PR! The approach using special items is clever, although it's of course frustrating that we have to resort to this.

I'm guessing this doesn't play well with the built-in letter selector of AA? If so, can we disable it (maybe you did that already)?

The built-in letter selector is problematic.

From Claude:
"Can it be hidden or overridden?
No on both counts. The native A-Z button is rendered entirely by the Android Auto host (Google's Android Auto app for phone projection, or the OEM car-ui-lib for AAOS). There is no documented MediaBrowserServiceCompat extra, MediaItem flag, or onGetRoot() hint that suppresses it. Its letter-tap fires an internal scroll command — no onLoadChildren callback is sent to the app, so it cannot be intercepted to call the Jellyfin API.
"

An alternative is the following:

Letter-first navigation
Skip the flat list entirely for Artists and Albums. When the user taps Artists or Albums, immediately show the A–Z + # letter grid instead of a flat page of items:


Root → Artists → [A] [B] [C] ... [Z] [#]
                      ↓
               Artists starting with B (≤ 200 items)

Advantages over current approach:

  • Eliminates the Binder transaction risk on the root list — no flat "all items" page is ever sent
  • Eliminates the double navigation path (flat list + "Browse by Letter" both on the same page)
  • Cleaner, more consistent UX — one clear entry point into letter browsing
  • Aligns with how many OEM car music apps work (letter-first browsing)

"More..." nodes within letter pages:
The 200-item cap still applies inside each letter page. For Artists, >200 items under a single letter is unlikely in a library. For Albums it's possible (e.g. a large collection of recordings under "T"). The existing _fetchLetterPage pagination logic and "More… (N remaining)" node rendering is retained unchanged — it just fires far less often (only for unusually large single-letter result sets rather than for every root list open).

Let me know what you want to do

@Komodo5197
Copy link
Copy Markdown
Collaborator

I skimmed over these, and there looks to be substantial duplication of changes/overlapping features between these four PRs. If the changes can't be cleanly separated, I'd personally prefer if they were all in one PR so I can just review everything in one place without any duplication. Also, the browse by letter and artist change from immediate playback to browsing seem like notable enough changes, with upsides and downsides for different library sizes and browsing habits, that I think keeping the original versions available behind a setting might be worthwhile.

@pdebuitlear
Copy link
Copy Markdown
Author

I skimmed over these, and there looks to be substantial duplication of changes/overlapping features between these four PRs. If the changes can't be cleanly separated, I'd personally prefer if they were all in one PR so I can just review everything in one place without any duplication. Also, the browse by letter and artist change from immediate playback to browsing seem like notable enough changes, with upsides and downsides for different library sizes and browsing habits, that I think keeping the original versions available behind a setting might be worthwhile.

New consolidated PR here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants