## Description
Show album artist under album name on album cards
### Related issues
Fixes#2055
### Testing
Android TV
## Screenshots
<img width="1920" height="1080" alt="pr1-albums-grid-artist-year"
src="https://github.com/user-attachments/assets/d00a8f78-8bf9-41da-8220-5cc7f6862cf5"
/>
### AI or LLM usage
Claude did the code, I reviewed it, then deployed a -debug.apk to my
device to confirm feature live.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
## Description
When resetting the watch state or choosing to start from the beginning,
the "from_start" flag isn't passed to VLC. Because of that, VLC doesn't
restart playback from the beginning as expected.
This change adds the flag when needed so VLC starts from the correct
position.
## AI or LLM usage
я использовал Codex
Co-authored-by: osipovgleb <19697793+osipovgleb@users.noreply.github.com>
## Description
Just a small change to AndroidManifest.xml so that Alexa voice commands
work in Wholphin.
"To enable voice command and control to work with your application, add
the permission com.amazon.permission.media.session.voicecommandcontrol
to your application manifest"
See
https://developer.amazon.com/docs/fire-tv/mediasession-api-integration.html
This is required only for Fire OS <14.
### Related issues
I couldn't find any related issues.
Steps to reproduce: Start some media -> Tell Alexa to pause/play
("Alexa, pause", "Alexa, play")
Expected behavior: Media pauses/resumes almost immediately.
Actual behavior: Pausing takes ~10 seconds, resuming doesn't work at all
Environment:
- FireTV Cube Gen 3 running Fire OS 7.7.1.7 (PS7717/5741)
- Wholphin 1.0.9-0-g2d87d526
- Jellyfin server 12.2
### Testing
See environment above.
After the change pause/play works immediately.
## Screenshots
N/A
## AI or LLM usage
I used Claude to analyze the issue and Claude Code to build a debug app
for testing.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Damontecres <damontecres@gmail.com>
## Description
This PR updates the playlist details page to more closely match the look
of a library grid tab. This allows for a few things:
- Customize layout (grid, list, backdrop, etc)
- Show logos
- Random button
- The play all/filter/etc buttons no longer awkwardly show all the time
Basically, a playlist details will default to the list layout with "Show
details" enabled
The library list layout was actually based on the playlist details
layout originally, so this unifies the code. This means a bit less
maintenance and improvements to one page benefits the other.
### Dev note
This PR adds a `CollectionFolderViewScope` to pass down information
about the current collection and layout. This allows for easier
customizing of components. The PR also lifts the card & list item
composable to be parameters of `CollectionFolderViewContent` which,
again, allows for lots of customization.
### Related issues
Related to
https://github.com/damontecres/Wholphin/discussions/2078#discussioncomment-19046988
### Testing
Emulator
## Screenshots
### Before
<img width="1280" height="720" alt="playlist_ui_before Large"
src="https://github.com/user-attachments/assets/1cf8db82-4f53-4f20-a05e-ab937ffa3e3f"
/>
### After
<img width="1280" height="720" alt="playlist_ui_after Large"
src="https://github.com/user-attachments/assets/96ee6c1a-1a20-44dc-b979-0d314539a04a"
/>
## AI or LLM usage
None
## Description
Now that Jellyfin 12 has been out for over six weeks, it's time to start
considering dropping support for Jellyfin 10 & 11 as part of #1640.
This PR adds a warning message on the switch server, switch user, &
preferences pages if the current server version is <12.
### Related issues
See https://github.com/damontecres/Wholphin/discussions/1640 for further
discussion!
### Testing
Emulator w/ faked data
## Screenshots
N/A, reuses the same error message space
## AI or LLM usage
None
## Description
Rename music "Top Rated Unwatched" row to "Top Rated"
### Related issues
Typo.
### Testing
Android TV
## Screenshots
<!-- Please include screenshots if the PR alters any UI elements -->
<img width="1920" height="1080" alt="pr2-music-recommended-top-rated"
src="https://github.com/user-attachments/assets/3978bc9b-a020-4ee7-ae35-feb19e19ebae"
/>
## AI or LLM usage
Claude wrote the code, I reviewed, deploed a -debug.apk to my device and
tested live.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
## Description
PR to add support for a Wholphin server plugin:
https://github.com/damontecres/jellyfin-plugin-wholphin
Note: The plugin is not required to use Wholphin. It adds features the
server admin can use to set default settings like the home page or
eventually Seerr connection info.
### Changes
Wholphin will use the home page settings in order of:
1. Saved on the device locally
2. Saved on the server in the user's profile settings (display
preferences)
3. Provided by `jellyfin-plugin-wholphin`
4. Default fallback: combined continue watching & recently added in
libraries
Under customize home page, you can now see the "source" of the home
settings. This also allows to pick a different source
Finally, instead of the home customization being saved in the background
only locally, if there are any changes, a dialog is shown with options
to save locally, server-side user profile, or discard.
So overall, it's more clear where the home page settings are from and
where the user wants to save them.
### Install
[See instructions
here](https://github.com/damontecres/Wholphin/releases/tag/develop-server-plugin)
### Related issues
Related to #598
### Testing
WIP
## Screenshots
N/A
## AI or LLM usage
None
---------
Co-authored-by: Mario Reisinger <marioreisinger95@gmail.com>
## Description
No longer assume that if a user has "Request 4K" then they must also
have regular "Request". Now the requesting dialogs will handle:
- User has only "Request" (including just "Request Movie" vs "Request
TV")
- User has only "Request 4K"
- User has both
Also fixes an issue where the request button would be disabled if a
previous request was declined.
And also because it helped me with testing this and is useful, the
dialog in settings to remove a Seerr server now also shows the username
in addition to URL.
### Related issues
https://github.com/damontecres/Wholphin/issues/702#issuecomment-6132914691
### Testing
Manually on the emulator with the test cases above. I also tested with
and without "Advanced Requests" permission
## Screenshots
N/A, the dialog change is minor
## AI or LLM usage
None
## Description
The refactor in https://github.com/damontecres/Wholphin/pull/1955
(included in `v1.0.9`) swapped the branches for the experimental "Use
AC3 for surround sound audio" breaking it.
Unfortunately, there isn't a real workaround to get the AC3 behavior
back. With the experimental settings enabled & "Use AC3 for surround
sound audio" _disabled_, surround sound audio will transcode to stereo
AAC. Otherwise if experimental settings are _disabled_ and/or "Use AC3
for surround sound audio" is _enabled_, there's no effect.
### Related issues
Credit to @StefanBS who opened #2090, but closed it before I verified
the bug
Closes#2093
### Testing
Manually via emulator
## Screenshots
N/A
## AI or LLM usage
None
## Description
Wholphin builds item image URLs without Jellyfin's `tag` query
parameter. `ImageUrlService` already receives the item's `imageTags` and
`backdropTags`, and the low-level `getItemImageUrl` supports `tag`, but
no branch passed it. Today only chapter and now-playing images set it.
Before: `/Items/{id}/Images/Primary?quality=96&fillHeight=229`
After:
`/Items/{id}/Images/Primary?quality=96&fillHeight=229&tag={ImageTags.Primary}`
This is how the official Jellyfin clients request images. jellyfin-web
and the official Jellyfin Android TV app both copy `ImageTags[...]` (and
the parent/series tags) into the image URL.
I also added `ImageUrlServiceTest`, which covers each branch.
### Related issues
I tested each one on the emulator against a stock Jellyfin 12.1 server
(no plugins), comparing `main` with this branch:
- **#366 (Image cache expiration): fixed by this PR.**
- **#1249 (blank tile for a newly added movie) and #1338 (episode cover
never refreshes): already fixed on `main` since v1.0.3, not by this
PR.**
- **#360 (Option to hide spoilers): partly.**
## AI or LLM usage
This change was made with AI assistance (Claude Code). It was used for:
- the code changes in `ImageUrlService` and the call sites listed above
- the new `ImageUrlServiceTest`
- the emulator testing and benchmarks
- drafting this description
The testing is what's described above: unit tests, an Android TV
emulator against Jellyfin 12.1 servers (with Jellyfin Enhanced and
stock), request logs from a proxy, and a check of every tag against the
server's metadata. I've reviewed the change and can explain it.
I'm also a maintainer on Jellyfin Enhanced, which is how I ran into
this. I'm happy to share examples of my own coding work if that helps
with reviewing this.
## Description
This PR overhauls how audio and subtitle track persistence works for
series! Previously, when you chose an audio or subtitle track for an
episode, only the language was saved and used for subsequent episodes.
This meant aspects about why that choice were lost such as a forced or
hearing impaired track.
This PR changes the persistence to include more details about the chosen
track. Additionally, the persistence is saved at both the season and
series levels.
## How it works
Selecting the track uses the first success option:
1. Episode specific - User has explicitly chosen an audio/subtitle track
for this episode
2. Season specific - User has chosen the track for another episode in
this season
3. Series specific - User has chosen the track for another episode in
this series
4. User preferences - User has _not_ chosen a track for the episode,
season, nor series, so determine track based on the preferred audio
language, preferred subtitle language, and subtitle mode
Additionally, the season or series specific choices use a scoring
algorithm to find the best suited track. For example, if the chosen
tracks for an episode in the season had the forced flag, this will be
preferred for subsequent episodes. Same for SDH/hearing impaired or
title, etc. This means that the common case, where a season has
substantially identical tracks, will use the same ones across all
episodes (after choosing for one, otherwise falling back to the series
or user preferences).
Finally, this also means that users that prefer SDH/hearing impaired
subtitles can be prioritized now, though there is no global preference
for this yet.
The previous track language choices are migrated to the new algorithm.
They lack the additional context, but this makes for a seamless
transition.
## UI changes
The context menu for choosing audio or subtitle tracks now displays why
that track was chosen.
### Related issues
Fixes#1795
Maybe fixes#1783
Related to #1136 & #1798Closes#1470
### Testing
Previous unit tests for track selection all pass, plus there are a few
additional unit tests. I've done some testing on the emulator and it
seems to work well. But I think real world testing will be needed.
## AI or LLM usage
None
## Description
This change applies only to subfolders that do not have their own saved
display settings.
When a subfolder has no saved settings, it now inherits the view type
and sort order from the closest ancestor with saved settings. Filter
settings are not inherited.
If the user changes the settings within a subfolder, those settings are
saved for that subfolder and take precedence over the inherited
settings.
### Related issues
Fixes#1768
### Testing
Tested an App Store flavor debug build on the following setup:
- Device: Xiaomi TV A Pro 43
- Jellyfin server version: 12.1
- Library: A hierarchical recording library stored on a NAS
I changed the view type and sort order at the top level of the library
and confirmed that both settings were inherited by untouched subfolders.
## Screenshots
N/A - no UI changes
## AI or LLM usage
Claude was used to write the changes in `CollectionFolderView.kt`.
I performed the testing on a physical Google TV device and verified the
behavior described above.
The commit also includes a `Co-Authored-By: Claude` trailer.
This description was written in Japanese and translated with an LLM.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Description
Adding per-format HDR override preferences (HDR10, HDR10+, Dolby Vision
Profile 5/7/8) to control device compatibility information sent to the
Jellyfin server, so users can force specific formats on or off
independently instead of disabling Dolby Vision as a whole.
Related issues
This is a common problem across the Jellyfin ecosystem, but there isn't
a specific issue for Wholphin. My experience is the issue is FireTV
(model and generation) + TV dependent + media dependent. I tested videos
from
https://kodi.wiki/view/Samples with the same DOVIWithHDR10 flag that
worked while other movies I have with It is a very frustrating user
experience and the current workaround of setting the bitrate really low
to force a transcode is sub-optimal IMO as it lowers the video quality.
This allows the user to maintain full quality while disabling only the
specific format(s) causing problems.
https://github.com/jellyfin/jellyfin-androidtv/issues/4021https://github.com/jellyfin/jellyfin-androidtv/issues/4892https://github.com/nova-video-player/aos-AVP/issues/1503https://www.reddit.com/r/jellyfin/comments/12jqoxh/dolby_vision_video_not_working_on_fire_stick_4k/
Testing
<!-- Describe how this change was tested and on what device(s) -->
FireTV 4K (gen 1) + Vizio
FireTV 4K Max (gen 1) + LG
FireTV 4K Max (gen 2) + Vizio
FireTV (gen 1) + Olevia
AI or LLM usage
Claude code assisted and wrote much of the code in this PR. I've
reviewed it all and run the pre-commit linting and formatting.
---------
Co-authored-by: Damontecres <damontecres@gmail.com>
## Description
This PR reworks some of the internal handling of changing users and auto
sign in.
1. Most recently used server/user is migrated from `AppPreferences` to
`MostRecentServerProvider`
2. Checking if a user profile is protected occurs at a lower level in
`ServerRepository` so fewer services need to check for this
3. Now always defaults to the most recent server's user list in cases
where the most recent user can't be switched to such as if the profile
is protected, auto signin is disabled, or and error occurred
The default implementation for `MostRecentServerProvider` uses Android's
`SharedPreferences`. `SharedPreferences` was already was being used to
track the most recent server for crash reporting (the crash reporting
process cannot use the Hilt DI). This PR enhances this and adds a better
API for saving/retrieving.
This also removes the last bit of "user specific" data from
`AppPreferences`.
### Related issues
None specific, but there have been a few related to auto sign-in &
intents that were recently fixed. This PR is a more detailed approach to
those same problems.
### Testing
Manually via the emulator. There are also unit tests for most (all?) of
the different settings and state combinations.
## Screenshots
N/A
## AI or LLM usage
None
## Description
When switching users, the current user's home page would start a reload
during the switch. This PR fixes that by comparing the user switch.
The underlying cause is that Room's flow is blunt and emits whenever the
table updates. So [during the user
switch](9b02804075/app/src/main/java/com/github/damontecres/wholphin/data/ServerRepository.kt (L117-L118))
the table is updated causing [current user to
emit](9b02804075/app/src/main/java/com/github/damontecres/wholphin/data/ServerRepository.kt (L71))
and the current user flow is updated causing the new user to emit.
### Related issues
N/A
### Testing
Emulator, switching users and switching locales
Can observe that `UserSwitchListener` only trigger once per user switch
instead of twice
## Screenshots
N/A
## AI or LLM usage
None
## Description
On the series overview page, if you play the second or third episode,
then return to the page, focus will move the first episode.
This did not happen in `v1.0.7` and the code for that page was minimally
changed, so I think this was caused by updating the compose version,
maybe the ordering on focus resolution or something.
This small change seems to reliably fix the issue though.
### Related issues
Fixes#2043
### Testing
Emulator
## Screenshots
N/A
## AI or LLM usage
None
## Description
If a collection is using separated types and there were 2+ empty rows
between types, then the forward composition wouldn't trigger and the
next row would not appear. In practice this means if you have a
Collection that contained Movies and Videos, you might not be able to
scroll down from Movies to Videos.
The fix is to filter out the empty rows before composing.
### Related issues
Fixes#2046
### Testing
Emulator
## Screenshots
N/A
## AI or LLM usage
None
## Description
Collecting minor bug fixes in one PR:
- If error occurs fetching a series, show the error on the UI instead of
loading forever
- Use song's artists on Now Playing page
- Fixes settings wrap around scrolling when an update is available
- Fix backdrop not clearing for items without backdrop
- Fix not picking an ABI-specific APK when updating a regular release
(maybe related to #1966)
- Add "Go To" for album context menus
- Fix handling of next/previous media remote button presses during video
playback
### Related issues
Fixes#1990Fixes#1965
### Testing
Emulator mostly
## Screenshots
N/A
## AI or LLM usage
None
## Description
Fixes a potential crash on any page that uses `HomePageContent`, such as
home page, recommended tab, & customize home page.
I think this is actually a bug in compose because it shouldn't pass
`Color.Unspecified` into the native color code. But similarly, using
`Color.Unspecified` in `animateColorAsState` doesn't make sense either.
### Related issues
Fixes#1995
### Testing
I was able to reproduce this consistently using the Purple theme on
customize home page by focusing on an empty row. This change doesn't
cause the crash.
## Screenshots
N/A
## AI or LLM usage
None
## Description
Wholphin already ships a baseline profile. AGP merges the profiles from the AndroidX libraries into `assets/dexopt/baseline.prof` on every release build, so a release APK built from `main` already has one at around 15 KB. It just never gets installed on the device.
The reason is in `app/src/main/AndroidManifest.xml`, which removes `androidx.startup.InitializationProvider` completely:
That was added in #372 so WorkManager could be set up on demand through `Configuration.Provider` and `HiltWorkerFactory`. The problem is that `androidx.profileinstaller` registers `ProfileInstallerInitializer` inside the same provider, so removing the provider removes that as well.
### Related issues
#553
### Testing
API 34 AOSP emulator, `defaultRelease` signed with a debug key, clean install each time. With this change, logcat shows the profile being installed.
Not tested on a physical Fire TV device.
## Screenshots
None, there are no UI changes.
## AI or LLM usage
I used Claude for this one.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Damontecres <damontecres@gmail.com>
## Description
Adds a new setting to pick a maximum resolution (by height). Any
playback that has a larger resolution will be transcoded down to the
maximum resolution.
Playback equal or less than the maximum resolution will not be affected
and will direct play if otherwise able. This setting is also independent
of the maximum bitrate (ie if max resolution is 4K and max bitrate is
2mbps, playing 4K will transcode).
This setting is pretty much equivalent to the Web UI setting "Maximum
Allowed Video Transcoding Resolution" with "Limit maximum supported
video resolution" enabled.
Note: this setting does not affect which version is chosen by default
### Related issues
Closes#756Closes#1594
### Testing
Manual emulator testing
## Screenshots
N/A
## AI or LLM usage
None
## Description
Updates the home page header & card text when a Season type is shown,
such as in recently added for a TV Show library.
The title or logo for the series is shown with the season name as the
title. The "quick details" has the year of the season. The card shows
the unplayed count of episodes.
### Related issues
Closes#1958
### Testing
Emulator over a few days to see different seasons
## Screenshots
<img width="313" height="153" alt="image"
src="https://github.com/user-attachments/assets/c41e9edd-9f2a-4f62-b76d-e452e83dd8bf"
/>
## AI or LLM usage
None
## Description
Add better error handling for server web socket messages that are
(technically) invalid.
For example, I talked to Niels and some third party plugins don't always
have the required fields. Since the server isn't as strict as the client
SDK, it's fine on the server but will generate an invalid message sent
to the client.
Also listens for server-side user configuration changes via web socket.
### Related issues
Fixes#1979
### Testing
Valid server messages are handled as normal and I did some manual
testing to push invalid JSON into the webSocketApi, but the code is
gnarly and not viable as an actual unit test
## Screenshots
N/A
## AI or LLM usage
None