## Description
This PR improves the logic used to choose a display mode when refresh
rate and resolution switching are enabled.
As before, matching the refresh rate is prioritized over picking the
resolution. For example, with the display modes of 1080@30 & 720@60 for
a 720@30 video, 1080@30 is used because the frame rate exactly matches.
### Related issues
Fixes#1036
### Testing
Unit tests, added test cases for more situations
## Screenshots
N/A
## AI or LLM usage
None
## Description
Fixes some issues with Jellyseerr login. Also makes error message more
clear by showing the URLs that were tested and the error that occurred.
Also fixes an issue where you could not remove a Jellyseerr server if it
was previously configured but can no longer connect.
### Related issues
Fixes#1028
Should fix#1022
Related to #747
### Testing
Emulator, tested adding and removing valid & invalid IPs
## Screenshots
N/A
## AI or LLM usage
None
## Description
Fixes several small issues throughout the app
- Adds 1440p as a resolution option
- Removes the delay when d-pad seeking on the seek bar & reduces it
slightly when controls are hidden
- Fixes padding one home customize pages
- Adds a title to the search for dialog
### Related issues
Fixes#1017
### Testing
Emulator mostly
## Screenshots
N/A
## AI or LLM usage
None
## Description
This PR is mostly a proof-of-concept for setting up automated UI
testing. The tests use Roboelectric so they can run without an emulator.
There is also a sample instrumented test for running on emulators or
devices for future tests.
It adds two basic automated UI tests for adding a server. These are sort
integration tests because they use the actual implementations for the
view model and services like `ServerRepository` and `JellyfinServerDao`.
Also, moves the bulk of `MainActivity`'s compose code into a function
for future tests.
### Related issues
N/A
### Testing
Local testing
## Screenshots
N/A
## AI or LLM usage
None
## Description
- Aims to further optimize performance for the suggestions fetching
### Related issues
Fixes#833
### Testing
Android Emulator
NVIDIA Shield Pro 2019
## Screenshots
N/A
## AI or LLM usage
AI being used to assist with updating/creating unit tests
## Description
- Adds hold-to-seek acceleration for left/right DPAD input during
playback skip handling.
- Reuses one shared, duration-aware acceleration profile for both
playback DPAD and seekbar skipping.
- Extracts acceleration logic into a shared helper to keep playback and
seekbar behavior consistent.
- Slows the acceleration ramp (about one-third) to improve control
during longer holds.
- Preserves tap-on-release behavior: single taps seek on key release,
while holds seek on repeated key-down without an extra release step.
- Normalizes unknown/unset duration values to use the shortest-content
acceleration profile.
- Added unit coverage for shared seek acceleration math to prevent
accidental regression in ramp thresholds.
### Related issues
Supersedes #846
Incorporates #784Closes#522
### Testing
Tested on Nvidia Shield Pro 2019.
- Quick left/right tap still performs normal skip behavior.
- Holding left/right seeks repeatedly with progressive acceleration.
- Seekbar hold uses the same acceleration profile as playback hold.
- Releasing after a hold does not trigger an extra seek step.
## AI or LLM usage
Codex 5.3 and Claude Opus 4.6 were used for implementation, debugging,
and cross-review. Changes were manually validated on device.
---------
Co-authored-by: Ray <154766448+damontecres@users.noreply.github.com>
## Description
Improves home page customization for Live TV & Recordings
- Add row for live tv channels
- Add row for "on now" programs
- Clicking on a TV channel or TV program starts playback
- Better name for "Recently recorded" instead of "Recently added in
Recordings"
Also fixes a back stack issue after adding rows
### Related issues
Follow up from #803
### Testing
Emulator
## Screenshots
<img width="663" height="190" alt="image"
src="https://github.com/user-attachments/assets/49d41953-42ab-4621-92f1-b7936aab7e97"
/>
## AI or LLM usage
None
## Description
Customizing the home page (#803) and nav drawer (#886) were developed
independently, so this PR just cleans up somethings so that those two
changes work together.
This PR restores the default home page when there is no customization to
be the same as before: continue watching/next up row(s) followed by the
order of the nav drawer libraries' recently added rows.
Finally gets rid of `NavDrawerIteRepository` and related functions which
all had weird, hard to follow code.
### Related issues
Related to #803 & #886
### Testing
Emulator testing
## Screenshots
N/A
## AI or LLM usage
None
## Description
This PR adds the ability to customize the home page in-app
### Features
- Add, remove, & reorder rows
- Persist the configuration locally
- Save the configuration to the server, allowing to pull down on other
devices
- Adjust view options for rows such as card height, image type,
preferring series images, or aspect ratio (similar to libraries)
- Pull down the web client's home rows (no plugins are supported yet!)
- Preview of the home page which is usable & updated as changes are made
### Row options
These are row types that can be added in-app via the UI:
- Continue watching
- Next up
- Combined continue waiting & next up
- Recently added in a library
- Recently released in a library
- Genres in a library
- Suggestions for a library (movie & TV show libraries only)
- Favorite movies, tv shows, episodes, etc
Additionally, there are more row types that don't have a UI to add them
(yet):
- Simple query to get items from a parent ID such as a collection or
playlist
- Complex query to get arbitrary items via the `/Items` API endpoint
### Dev notes
Settings are loaded in order:
1. Locally saved
2. Remote saved
3. Fallback to default similar to Wholphin's original home rows
The remote saved settings are stored via the display preferences API.
I know some server admins would prefer to push a default setup to their
clients. This PR does not have that ability, but it does define a
straightforward API for defining the settings. Something like the
potential server plugin work started in #625 could be slimmed down to
expose a URL to be added in the load order.
I'm also investigating integration with popular home page plugins to
allow for further customization, but will take more time.
### Related issues
Closes#399Closes#361Closes#282
Related to #340
## Description
Fix a bug where movie suggestions would get stuck loading when the
on-disk cache file doesn't exist.
### Related issues
Fixes#876
### Testing
Ran unit tests locally.
## Screenshots
N/A
## AI or LLM usage
Used AI to add testing for this scenario to unit test
---------
Co-authored-by: Ray <154766448+damontecres@users.noreply.github.com>
## Description
A rewrite of the `SuggestionsCache` to simplify it as a read-through
cache. The downside of this is that updated suggestions won't be
reflected in the UI unless the user reloads the page, but I think that's
an okay trade off.
Also adds parallelism limits to fetching suggestions to reduce the
number of simultaneous API calls. And moved the combining logic to use
the `Default` dispatcher to free up an IO one.
Also adds an initial delay to both the suggestions & tv provider
workers, so a cold start of the app isn't starved.
### Related issues
Related to #849
### Testing
Emulator & unit testing
## Screenshots
N/A
## AI or LLM usage
None
## Description
Add a settings to set the maximum amount of time items can appear in
Next Up.
Options range from 7 days to a year, or no limit (current & default
behavior)
### Related issues
Closes#662
### Testing
Tested on the emulator and verified URLs, added unit tests
## Screenshots
N/A, just a new slider bar setting
## AI or LLM usage
None
## Description
Addresses suggestion logic oversight in recently merged suggestions fix,
and improve performance (especially app homepage startup time)
- Re-implements the logic that sorts/categorizes tailored suggestions
based on the seed items that are queried. This was implemented in the
original PR at first, but I overlooked moving that logic
- Removes `OneTimeWorkRequestBuilder` and replaces with trigger in
`PeriodicWorkRequest` to fire with a 30 seconds delay if cache is empty.
### Related issues
Fixes#833
### Testing
Will be testing in:
Android TV emulator in Android Studio
NVIDIA Shield TV Pro 2019
## AI or LLM usage
I will use AI to help with writing the tests/testing services.
## Description
This PR aims to fix#638 where the "Suggestions" row was mixing up
content from different libraries that shared the same library type. For
example you were in the Anime library, you'd see regular TV shows
showing up, as the TV Shows and Anime libraries would both be "Show"
libraries.
As @damontecres mentioned in the issue discussion, the
`/Items/Suggestions` endpoint Wholphin is using just returns random
items from the same library type, with no special logic or
recommendation algorithm.
I also looked at how the official WebUI handles this. It basically just
mixes "Resume," "Next Up," and "Latest Items" together. That felt a bit
redundant since we usually have dedicated rows for those things anyway;
I wanted to try something that helps discover new content.
I replaced the broken endpoint with a few custom `GetItemsRequest`
calls. These work correctly with the `ParentId` filter, so we only get
results from the library we are actually looking at. Additionally, I
added in some logic that takes into account what the user has been
watching to tailor the suggestions a little bit.
The new logic uses a 40/30/30 mix (this can of course be tweaked as
needed)
- Tailored (40%): Grabs your recently watched items from this library,
checks their genres, and returns items from the same genre.
- Random (30%): Random unwatched stuff from this library.
- New (30%): Recently added items.
This is implemented using only `GetItemsRequest` API calls using
filters, rather than any specialized endpoints like the
`/Items/Suggestions` endpoint. This means the feature should be
predictable and stable. If Jellyfin ever adds an actual recommendation
algorithm and endpoint to call it, I imagine we would want to switch to
that in the future though.
I also added some failsafe logic to make sure there will be diversity in
the "recently watched" items that get pulled, in case a user might have
recently binged a TV show. We wouldn't want every single recommendation
to be based off of that one show.
To fix this, I changed the query to fetch the last 20 history items
instead of just the top 3. Then, we filter that list client-side to find
unique Series IDs. If the user watched 10 episodes of One Piece in a
row, the logic collapses them into a single "One Piece" entry and moves
on to the next distinct show they watched. This guarantees we get
variety in the source material for recommendations.
This is a work in progress right now, mainly due to performance. For my
server it takes about 10-15 seconds to load the Suggestions row when I
tested. This is unacceptable in my opinion, especially if we ever intend
to add a Suggestions row like this to the home page like Plex does. I
have some ideas on how to improve performance, but I'm open to
suggestions.
### Related issues
Resolves#638
### Screenshots
Before:
<img width="1953" height="1162" alt="suggestions1"
src="https://github.com/user-attachments/assets/6e301c67-1a46-46b5-8184-3adb9e52b330"
/>
After:
<img width="1937" height="1108" alt="suggestions3"
src="https://github.com/user-attachments/assets/b9563865-4055-40b6-b452-f94c26c8b6e9"
/>
### AI/LLM usage
I used Claude Code to help write the Kotlin Coroutines so the API calls
run at the same time. I also used it to write the tests in
`TestSuggestionsLogic.kt`.
---------
Co-authored-by: Damontecres <damontecres@gmail.com>
## Description
Fixes several bugs and makes it easier to log in to a Jellyseer instance
- Fix initial focus & next focus on the add server dialog
- Don't use an API key for non-API key auth (credit to @voc0der in #734
for suggesting this)
- Show trailers for TV shows
- Fix incorrect/missing similar and recommended rows for both movies &
TV shows
- Allow empty password for Jellyfin users without a password
Log in will now try variations of the input URL such as add the protocol
and/or default port. This is similar to the regular Jellyfin login
process.
### Related issues
Fixes#778Fixes#750Fixes#740
Might help with #747
Fixes
https://github.com/damontecres/Wholphin/issues/764#issuecomment-3801324648
## Description
This PR is a rewrite of a previously submitted PR for this same feature.
It implements native Voice Search for Android TV using Android's
SpeechRecognizer API.
Key Changes
New Files:
- VoiceInputManager.kt - Handles SpeechRecognizer lifecycle, permission
management, and audio level normalization
- VoiceSearchButton.kt - Composable button with audio-reactive pulse and
full-screen listening dialog
UI/UX:
- Audio-reactive microphone button that pulses based on input volume
- Full-screen listening overlay with animated states:
- Listening - Pulsing bubble with ripple rings, shows partial
transcription
- Processing - Animated dots while waiting for final result
- Error - Red bubble with localized error message, auto-dismisses after
3s
### Related issues
Resolves https://github.com/damontecres/Wholphin/issues/515
### AI/LLM usage
The initial draft of this change from the prior PR was created with the
assistance of Claude Code, which was heavily leaned on for the overlay.
I have taken the initial draft, gone through it, and re-implemented by
hand to clean it up, narrow the scope (AI loves to touch and change
things unrelated to what you're working on) and make it more readable.
AI was used in the last stage to assist with finding any logic
errors/bugs I may have missed (by looking through code and also by
looking over ADB logs from testing on Nvidia Shield).. AI was also used
to create the tests found in TestVoiceInputManager.kt. Lastly, I fed the
diffs to an AI to help summarize the changes for this PR in an easy to
read manner.
---------
Co-authored-by: Damontecres <damontecres@gmail.com>
## Description
Adds a client-side "Only Forced Subtitles" option when selecting
subtitles, per the discussion in #571.
### Changes
- **TrackIndex.ONLY_FORCED** (`-3`): New constant for the special case
- **StreamChoiceService**: Intercepts `ONLY_FORCED` at the top of
`chooseSubtitleStream()` before server-side `SubtitlePlaybackMode` logic
(which remains untouched)
- **UI**: Adds "Only Forced Subtitles" option to both:
- In-player subtitle menu (`PlaybackDialog.kt`)
- Pre-playback stream selection dialog (`Dialogs.kt`)
### Forced track detection
Uses metadata `isForced` flag as primary check, with title-based
fallback ("forced", "signs", "songs" patterns) when language matches
audio.
### Related issues
Closes#571
### Screenshots
N/A - subtitle selection menu only
### AI/LLM usage
Used Claude Code for assistance and to draft PR description.
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Damontecres <damontecres@gmail.com>
## Description
Fixes track selection issues with MPV. This was especially focused on
libraries that disable embedded subtitles, but also fixes issues where
the audio streams are listed before video streams.
Also includes test media source JSON and unit tests to verify many
scenarios.
### Related issues
Fixes#494
## Description
Adds a settings for automatic resolution switching. When enabled, the
app tries to output content at the video stream's resolution.
For example, if playing 1080p content on a 4K TV, the actual output by
the Android TV device will be 1080p.
This works in hand-in-hand with automatic refresh rate switching. So
enabling both allows for the output to match both refresh rate and
content resolution. Outputting the best refresh rate is preferred over
best resolution.
Also, shows the current display mode in the playback debug info.
### Related issues
Closes#428Fixes#531
Adds a settings allowing for adjusting the thickness of unstyled
subtitle outlines, if outline is enabled.
This is applied for both ExoPlayer & MPV
Closes#179

Dev note: the ExoPlayer implementation is bad code, don't write code
like this. It works though