## Description
Replaces the generic user icon with the user's profile image on the nav
drawer
If there's no image it will fall back to the generated color & first
letter just like on the user list page.
### Related issues
Closes#631
### Screenshots
<img width="54" height="160" alt="image"
src="https://github.com/user-attachments/assets/bee21e56-104a-420f-bb7d-aae717f8aeb0"
/>
## Description
Adds the [`dav1d` decoder
module](https://github.com/androidx/media/tree/release/libraries/decoder_av1)
for ExoPlayer as an option. It can be enabled in advanced settings.
Performance will vary between devices.
### Related issues
Closes#103
### Dev notes
As with the ffmpeg module, this one is not required to build the entire
app so it's easier for developers to get started.
This also introduces a customized `DefaultRenderersFactory` for
ExoPlayer, but its largely copied from the super class's implementation.
## Description
Fixes focus jumping back a few cards on rows when returning to a page
I think some behavior changes occurred in a recent compose release.
`requestFocus` doesn't throw an exception anymore for example. Focus in
compose has always been finicky.
This PR basically adds some delegating parent focus down its to
children. Also, most rows now maintain an internal position state to
track where the focus requester is applied.
### Related issues
Closes#632
## Description
- Fixes a copy-paste error where checking if Seerr support 4K for TV
accidently used the movie 4K value
- Always show the loading indicator while waiting for playback to begin
- This has the side effect of fixing MPV playback not starting until the
UI changes (recomposes). That's a hacky fix, but should work while I try
to figure out the real cause
### Related issues
Fixes#715
## Description
If an error occurs while fetching data for the grid, leave the controls
accessible. This is useful in case a particular sort or filter causes an
error because the app remembers those values between page loads.
Essentially the error message is shown in place of the grid content
instead of the entire tab.
### Related issues
Fixes#712
## Description
- Fixes when to show/hide Request & Cancel buttons
- Removes the More button
- Uses a larger 16x9 backdrop so that the fade is better
- Fix reloading the backdrop on series pages
### Related issues
Fixes#706
## Description
This PR optimizes the rendering of "quick details" in headers. That's
the dot separated row of Season/Episode/Date/Runtime, etc
The UI is slightly different because I moved the "Ends at" part to the
end of the row. The dots & spacing is also slightly different.
In testing this on my CCwGTV HD, a pretty low powered device, the PR
improved the time to render by over 3x (59.42ms->17.72ms). Note: these
times are during profiling and are not real world performance, but the
relative change should be applicable in the real world.
### Dev details
- Changes the dot separated row of multiple Text & Box composables into
a row of two Texts
- The text is an `AnnotatedString` which is created on the IO thread
when `BaseItem` is created
- A separate composable creates the "Ends at" to prevent recompositions
of the other strings
## Description
Fixes "Pause with one click" so the desired behavior isn't reversed and
now shows the controls when pausing.
This bug happened because MPV commands are async, so there is a tiny
delay before the pausing occurs and the playback state is updated.
However, the logic to decided whether to show the controls or not checks
the playback state immediately.
### Related issues
Fixes#697
## 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
Use only TV series genres for a TV library's genre tab. This ensures
only series are shown, not episodes, which removes some of the seemingly
empty genres.
Also fixes the image filtering to ensure an item with a backdrop is
always used if available.
### Related issues
Fixes#689Fixes#690
**Description**
Fixes a race condition in the Seerr integration.
When you added a new Seerr server and logged in successfully, the app
would immediately show the "Disable Seerr?" dialog instead of a success
message. The state was getting mixed up during recomposition and
thinking you wanted to remove the server you just added.
Fixed by being more explicit about what mode the dialog is in (adding vs
removing) so it can't get confused by background state changes. Also
added a success toast when login works.
**AI/LLM usage**
Used Claude Code to analyze the issue.
## Description
Clips person images to a circle. The rectangle is still used in grid
pages like favorite people.
This PR also updates the fallback image to use a person icon if there is
no image for that person
Also adds role/job to discover pages' people rows
### Related issues
Closes#682
### Screenshots

## Description
On any grid that wasn't showing movies, tv shows, or videos, the jump to
letter functionality did not work correctly.
This PR fixes jumping for other media types, eg collections or episodes.
Additionally, the jump now also takes filters into account, so if
filtering Movies, it will jump to the right place now within the
available movies.
### Related issues
Fixes#683
## Description
This PR add the ability to integrate with a [Jellyseerr/Seerr
server](https://github.com/seerr-team/seerr).
### Features
- Login to the Seerr server using API Key, Jellyfin credentials, or
local Seerr credentials
- Separate Seerr logins per user profile
- Search Seerr from the search page
- Discover media from Seerr on the nav drawer `Discover` page
- Also from movie, series, or person pages
- Seamless integration with existing media, i.e. selecting media found
from Seerr that already exists on the Jellyfin server, will go directly
to the regular media page
- Mostly consistent UI between Jellyfin media & Seerr media
- Submit requests to Seerr
- Cancel submitted requests
### Screenshots

### Related issues
Closes#54
## Acknowledgements
The `app/src/main/seerr/seerr-api.yml` file is copied & modified from
https://github.com/seerr-team/seerr/blob/develop/seerr-api.yml. I hope
to try to upstream some of the changes in the future.
**Description**
Fixes a regression where season numbers in the tab strip were invisible
when scrolling through long series (15+ seasons).
**Related issues**
Fixes#677
**AI/LLM usage**
Used Claude Code to analyze diffs from recent commits in order to find
which commit caused the regression.
---------
Co-authored-by: Damontecres <damontecres@gmail.com>
## Description
Previously, if you clicked on a tab and the resulting grid had no
results, the nav drawer would open and focus which is not good.
This PR fixes that and instead returns focus back to the tab row.
### Related issues
Fixes#543
## Description
A few devices, like the Nvidia Shield Pro 2019, are able to correctly
handle Dolby Vision Profile 7. However, when a Full Enhancement Layer
(FEL) is present, these devices effectively ignore the enhancement layer
and only apply the RPU metadata.
Due to this behavior, device capabilities are reported as not supporting
`DOVIWithEL`, which causes the Jellyfin server to strip the DV layer
entirely with a remux.
This commit adds an option to force direct play of Dolby Vision Profile
7 content, preventing the server from stripping the Dolby Vision layer
and allowing compatible devices to handle playback as intended.
### Related issues
Fixes https://github.com/damontecres/Wholphin/issues/449
### Screenshots
<img width="1280" height="720" alt="dovi7"
src="https://github.com/user-attachments/assets/849e9897-93b6-419f-a1fb-7f361f9c88dd"
/>
### AI/LLM usage
None
Co-authored-by: Ray <154766448+damontecres@users.noreply.github.com>
## Description
Check if playback is for a remote source and, if so, play that remote
URL directly. Works for both ExoPlayer & MPV.
Also adds support for direct playback for DASH in ExoPlayer.
### Related issues
Closes#471Closes#666
## Description
- Always update home page backdrop
- Use smaller font size for episode names & force single line
- Use smaller cards for episode cards on person page
## Description
Adds a dark gradient scrim behind the season tabs on the TV Show Details
screen to improve text readability against bright backdrop images. This
follows the same pattern used in PlaybackOverlay.kt for the playback
controls.
The scrim uses two combined gradients:
- Vertical: Dark at top (0.80α → 0.5α) fading to transparent at bottom
- Horizontal: Fades in from transparent (left) to full opacity (right),
starting at 15% screen width to avoid overlapping with the nav rail
The gradients are composited using BlendMode.DstIn to create a smooth 2D
fade effect.
### Related issues
Fixes#547
### Screenshots
<img width="1920" height="1084" alt="season_darkened_background"
src="https://github.com/user-attachments/assets/a85237e4-2812-433c-902b-36f9860448c5"
/>
### AI/LLM usage
AI used to draft PR description and documentation
## Description
Just a UI change to disable the Search & Download subtitles button if
the user doesn't have permission to manage subtitles.
This avoids the 403 error if the user attempts to use it.
### Related issues
Closes#483
## 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 duplicate play/pause commands from a remote button press and
`MediaSession`.
This ended up being a bit complicated because Wholphin overrides the
default play/pause behavior for the skip back setting and to show the
controller on pause. This means using a forwarding player which meant
implementing more functionality into `MpvPlayer`.
### Related issues
Fixes#653
## 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
Processing segments happens in a separate coroutine which wasn't
cancelled until _after_ playback of the next item begins. So in some
cases the outro from a previous item might be triggered during the very
beginning of the newly starting item.
So this PR instead cancels segment processing immediately when starting
the process for playback of an item. Additionally, the processing also
now checks that the current segment is for the current playback item as
an additional safeguard.
### Related issues
I believe this should fix both of these issues:
Fixes#369Fixes#647
## Description
Creates a `MediaSession` during playback which allows for external
control such as via smart speakers, Google Home app, etc
Note: one limitation is that seeking previous/next items in the playlist
isn't supported. This is because Wholphin manages that independent of
the `Player`, so additional dev work is needed for this.
### Related issues
Closes#622
Might help with #415
## Description
When switching resolution (and possibly refresh rate less frequently),
the initial surface is not always valid. This means, the MPV playback
treated it as detaching the surface and would never update.
This PR instead subscribes to the `SurfaceHolder`'s callbacks which will
provide a new, valid surface once the switch is ready. Then the surface
is attached and queued commands execute to start playback.
Also temporarily disables content scale options for MPV since the
compose implementation conflicts with MPV rendering.
### Related issues
Fixes#626
## Description
This is an embarrassing bug... In #619, a change was made to avoid a
race condition and wait for the video output surface to be ready by
re-enqueuing commands. Except I missed cancelling executing the current
command.
This meant the command would both be executed and queued again. So, if
attaching the video surface takes a while, such as needing to wait for a
refresh rate change, _a lot_ of commands could have been added to the
queue and trigger a constant start-stop playback loop.
I think technically it would eventually resolve itself, but can take a
while.
### Related issues
Should fix#626
## Description
Removes an incorrect optimization for showing the home page header
Separates logic for focusing on rows on the home page
### Related issues
Fixes#624Fixes#620
## Description
Fixes a race condition where `libmpv` is beginning playback before it
has been attached to the output video surface.
The solution is for the handler to re-enqueue commands if MPV is either
not initialized or not attached yet. Once `libmpv` is ready, the
commands will be processed.
### Related issues
Should fix#576
## Description
On both movie & series detail pages, show a button to play trailers
instead of the row of cards below.
The button works like this:
- If no trailers, the button shows "No trailers" and is
disabled/non-clickable
- If only a single trailer, the button shows "Play trailer" and clicking
it plays the trailer
- Otherwise, shows "Trailers" and clicking displays a dialog to choose
one
Local trailers are shown first in the list. Then there is an attempt to
sort the trailers for the dialog so that names with "Official Trailer"
or "Official Theatrical Trailer" are listed first and "Teaser" is listed
last. This isn't perfect and doesn't work for non-English, but there's
no other metadata to use.
### Related issues
Closes#408
## Description
Makes sure the `ServerEventListener` and `DatePlayedInvalidationService`
are injected so that they run. This was broken in #538.
Also fixes the a delayed stopping of theme music
Also fixes for detecting a non-seamless refresh rate switch
### Related issues
Fixes#615Fixes#568