* Render a higher resolution bitmap in the notification background art
Previously only METADATA_KEY_ALBUM_ART_URI/ART_URI were set, leaving notification/lock-screen/Android Auto consumers to resolve the cover art themselves. Likely due to internal scaling, a lower quality bitmap was loaded.
This commit adds a shared resolveUriAsBitmap() helper (extracted from AbMediaDescriptionAdapter.kt) and uses it to:
- Resolve and cache a bitmap on PlaybackSession once per
session (synchronously for local covers, asynchronously for server covers), then set it on METADATA_KEY_ALBUM_ART/ART once available, falling back to the URI keys until it resolves.
- Reuse the same helper in AbMediaDescriptionAdapter so the notification icon is resolved consistently.
PlayerNotificationService tracks the resolution Job and cancels/replaces it on each new playback session, invalidating the media session metadata once the bitmap is ready.
* Remove the size override in Glide
The override was not needed. It wasn't in the original code, so I'll remove it for consistency.
* Update notification icon to reuse cover bitmap if already loaded on session
---------
Co-authored-by: advplyr <advplyr@protonmail.com>
Android Auto browsing could freeze and crash the app (phone app "closes",
Android Auto shows a generic connection error) whenever the Capacitor
webview had not yet established the server connection.
Root cause: onLoadChildren runs on the media browser service main thread.
It calls loadAndroidAutoItems -> checkSetValidServerConnectionConfig, which
used runBlocking to ping every saved server config and authorize the user.
That blocked the main thread on network I/O. Android Auto (plus Assistant)
re-request the browse root every ~2s, and every onGetRoot flags a reload
while disconnected, so the blocking work was triggered repeatedly and
re-entrantly, producing an ANR.
Changes:
- checkSetValidServerConnectionConfig now runs on a dedicated
Dispatchers.IO scope instead of runBlocking, so pings/authorize never
block the caller. The callback contract is unchanged; downstream Android
Auto callbacks already ran off the main thread.
- loadAndroidAutoItems coalesces overlapping calls into a single in-flight
load and fires all queued callbacks on completion, so the browse-root
polling storm can no longer spawn concurrent loads racing shared state.
- Fix latent bug in checkResetServerItems: server config id was compared
with !== (reference identity) instead of != (value), causing spurious
cache resets that fed the reload storm.
Co-authored-by: databoy2k <18686442+databoy2k@users.noreply.github.com>
Previously only METADATA_KEY_ALBUM_ART_URI/ART_URI were set, leaving notification/lock-screen/Android Auto consumers to resolve the cover art themselves. Likely due to internal scaling, a lower quality bitmap was loaded.
This commit adds a shared resolveUriAsBitmap() helper (extracted from AbMediaDescriptionAdapter.kt) and uses it to:
- Resolve and cache a bitmap on PlaybackSession once per
session (synchronously for local covers, asynchronously for server covers), then set it on METADATA_KEY_ALBUM_ART/ART once available, falling back to the URI keys until it resolves.
- Reuse the same helper in AbMediaDescriptionAdapter so the notification icon is resolved consistently.
PlayerNotificationService tracks the resolution Job and cancels/replaces it on each new playback session, invalidating the media session metadata once the bitmap is ready.
The POST /api/items/<id>/play/<episodeId> response inlines every episode
of a podcast with an audioTrack per episode. Server-side, audioTrack.mimeType
is derived from audioFile.mimeType, which is null when the row was created
without a successful audio probe (direct DB inserts, import scripts,
partial probe failures).
The Jackson deserializer hits one null mimeType among the inlined
episodes, throws MissingKotlinParameterException, and the entire
play-session response fails to deserialize. ExoPlayer is never called,
the UI silently stalls.
Make mimeType nullable on the data class to tolerate this. ExoPlayer's
MediaItem.Builder.setMimeType already accepts nullable. Defensively
coerce null to empty string for Cast's MediaInfo.Builder.setContentType,
whose Java signature is non-null.
Fixes#1905
When we are backgrounded, these queue events build up and can cause
performance issues as the queue is processed. We don't need to process
these when backgrounded, so skip them.
Also add small delay when resuming for webview to load before sending it
update events
- Route hardware volume keys to Cast via MediaSession VolumeProvider
- Use CastSession.volume (0–1) mapped to 0–100; clamp and step by 1
- Update provider with actual device volume after each change (prevents stalls)
- Drop stream-volume/polling attempts and other leftovers
- Expose get/set/adjust device volume commands to the player
Removed blocking `while (!browseTree.isInitialized){}` in
`onLoadChildren`. Added `waitForBrowseTree` and `onBrowseTreeInitialized`
helpers to queue pending results until browseTree is ready. All
browseTree assignments now call `onBrowseTreeInitialized()`. This avoids
ANRs and high CPU when Android Auto requests children before init.
Replaced the infinite `while(libraryPersonalizationsDone > 0){}` spin‑loop
with an async counter callback. This prevents pegging the CPU if one
personalization never completes, and allows completion to trigger via
AtomicInteger decrement. Now the final callback fires only when all
libraries have finished loading.
Adds meta-data to the widget, allowing it to be used on the cover screen
of Samsung devices such as the Flip.
The widget renders as a 4x4 grid (not an optimal layout) but functions
for launching the most recent audiobook or controlling playback.