Commit graph

80 commits

Author SHA1 Message Date
327f631f0d Cache DNS answers and paint saved people before launch revalidation
Some checks failed
review/security-claude BLOCK: DnsConnectionRetry crashes app on Jellyfin WebSocket (NoSuchElementException)
2026-11-07 00:09:57 +01:00
f60a22e435 Build the Wolfskin TV shell and navigation 2026-11-05 14:38:38 +01:00
670e95f2fa v1.1.0
All checks were successful
review/code-claude Claude cross-family review: approve, no blocking findings
-----BEGIN SSH SIGNATURE-----
 U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgrqffh93Kw9xITbVISXK/jkih9K
 Q1NSQeC6TBqiLCg2oAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
 AAAAQOwlHRfQqaoMZESsY18BqmXMIMwtJzU8cAEB2FWHNv5x2j/ZsGi8ES7VNW+rYtgV/B
 KF9ctdqeDoocL8r8da+AY=
 -----END SSH SIGNATURE-----

Merge upstream v1.1.0 while preserving Wolfskin customizations
2026-11-05 01:39:09 +01:00
2ccce98dc1 Show PWA source ratings on every surface through one shared rating row
All checks were successful
review/code-claude Claude code review: pass, no blocking findings
2026-10-29 21:04:39 +01:00
e499c3f118 Rebrand the TV app as Wolfskin 2026-10-22 15:05:37 +02:00
Damontecres
f01c5d367c
Updates to session restoration (#1905)
## 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
2026-10-11 20:11:09 -04:00
Damontecres
616565f671
Fixes to v1.0.7 (#1953)
## Description
Collection of minor bug fixes for `v1.0.7`

- Fixes a possible race condition crash when navigating to the search
page
- Uses friendlier error message when login fails due to parental
restrictions (based on
[`jellyfin-web`](https://github.com/jellyfin/jellyfin-web/blob/v10.11.11/src/controllers/session/login/index.js#L38-L41))
- Don't re-sort search results by name, just by relevance scoring
- Consume the key event when dim is active (ie if the screen is dimmed,
pressing a key will un-dim and not perform another action)

### Related issues
Fixes #1922 
Fixes #1951
Related to #1848 (search sorting)

### Testing
Emulator & nvidia shield

## Screenshots
N/A

## AI or LLM usage
None
2026-09-11 15:40:11 -04:00
Damontecres
786b95a393
Fix white flash on app start (#1909)
## Description
This fixes the brief full screen white flash when the app starts or
reloads.

I bisected to find the cause which was #1653. The newly added `Surface`
uses `MaterialTheme` to set its colors. `MaterialTheme` defaults to a
generic _light_ color scheme. So this PR applies the Wholphin's default
purple theme, which will then be overridden once the app preferences are
loaded.

### Related issues
Mentioned in
https://github.com/damontecres/Wholphin/issues/1576#issuecomment-5376373039

### Testing
Used emulator with "Don't keep activities" and repeatedly opened &
closed the app to see if the white flash visually occurred

## Screenshots
N/A

## AI or LLM usage
None
2026-08-26 21:46:48 -04:00
Damontecres
4b927e6b7b
Fix Next Up channel launch sign in (#1886)
## Description
When parsing intents, not every path for authentication was checked, so
launching via the "Next Up" OS channel wouldn't check for a profile PIN.
This PR fixes that.

Additionally, the "Next Up" OS channel launch wasn't checking the
correct parameter to ensure the back stack was written properly.

### Related issues
Fixes #1883

### Testing
I manually test on the emulator with these test cases:
- No profile PIN, cold start
- No profile PIN, hot start
- Profile PIN, cold start
- Profile PIN, hot start
- No profile PIN, launch via "Next Up" channel
- No profile PIN, launch via intent
- Profile PIN, launch via "Next Up" channel
- Profile PIN, launch via intent

Also added unit tests for intent-based and regular launching which
covers those cases and more.

## Screenshots
N/A

## AI or LLM usage
None
2026-08-21 15:04:07 -04:00
Damontecres
54966f9124
Handle density configuration changes to prevent unnecessary app restarts (#1828)
## Description
Add `density` to `configChanges` and handle any changes to density that
occur. Thankfully, Compose handles density changes itself, so only
changes where a density calculation is "remembered" needed to be
updated.

Without this, some devices will recreate `MainActivity` when switching
resolutions for playback. That recreation would cause playback to end
immediately.

### Related issues
Fixes #1809

### Testing
Tested on Onn 4k Pro & nvidia shield

I was able to reproduce on the Onn, but not the Shield, so this might be
device and/or API level specific

## Screenshots
N/A

## AI or LLM usage
None
2026-08-12 21:01:28 -04:00
calebvetter
c1abb57cd2
Add back stack for deep link navigation (#1756)
## Description
When testing the new deep link feature from this PR
[#1688](https://github.com/damontecres/Wholphin/pull/1688)
I noticed that the back button would close the app. As in, all routes
are cleared and only the playback is in the nav stack.

I figured a more desirable behavior would be to create a nav stack,
always starting with the home screen, then also inserting the media
item's details page, if available.

To achieve this, I made the return Target.destinations a list of
destinations, and updated the other two uses in IntentService.

Open to implementation tweaks, or scrap it if this isn't how it should
work, haha.

### Testing

Tested deep links, which produced the following nav stacks:
- Movie view
  - Home → Movie details
- Movie playback (with and without position)
  - Home → Movie details → Playback
- Series view
  - Home → Series details
- Season view
  - Home → Season details
- Episode view
  - Home → Episode details
- Episode playback (with and without position)
  - Home → Episode details → Playback
- Trailer/extras playback (no details page available)
  - Home → Playback
- TV channel view
  - Home → Playback
- TV channel playback
  - Home → Playback

## AI or LLM usage
AI was used to generate the initial code, debug and test, then manual
refactoring and cleanup was done.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-08-11 10:59:20 -04:00
Damontecres
8bae9c7f23
Fixes for error handling, UI padding, & more (#1759)
## Description
Another collection of small individual fixes that don't warrant a full
PR:
- Better error handling for series and collection detail pages
- Better error handling on Discover search tab
- Fix issues when changing view options on card grid without reloading
the page
- Better error handling when changing the refresh rate or resolution
- Fix some nav drawer items disappearing briefly when restarting the app
- Fix that the discover search row would not clear when the query is
cleared

And some UI tweaks:
- Use consistent padding for the different customize home page sections
- Convert the "Continue watching click behavior" setting from a toggle
to choice dialog (the options & behaviors have not changed)

### Related issues
I think the error handling when changing the refresh rate might help
with #1737

### Testing
Emulator mostly

## Screenshots
N/A, UI changes are minor

## AI or LLM usage
None
2026-07-29 10:21:49 -04:00
Damontecres
b1f9c32a19
Parse intents to search, view, or play media (#1688)
## Description
Adds parsing for intents to search, view, or play media based on the
action and parameters. This allows for remotely opening Wholphin and
starting playback.

This is considered experimental, so the syntax or behaviors may change!

See the `Intents.md` file in this PR for syntax and examples.

### Related issues
Replaces https://github.com/damontecres/Wholphin/pull/738
Closes https://github.com/damontecres/Wholphin/discussions/1095

### Testing
Emulator with the test commands in the docs

## Screenshots
N/A

## AI or LLM usage
None
2026-07-20 10:04:34 -04:00
Damontecres
3f60fa7550
Add option to override user profile settings for audio & subtitle languages (#1680)
## Description
Adds settings under the "User profile settings" section which allows for
specifying preferred languages for audio and subtitles and the preferred
subtitle mode.

By default (matching existing Wholphin behavior), the preferred
languages and subtitle mode are derived from the user's profile settings
provided by the server which can be set on the WebUI:
User->Settings->Playback or Subtitles. However, this isn't well
understood and Wholphin doesn't display these values anywhere, so many
users were confused.

The preferred languages and subtitle mode can now be configured from
within Wholphin. Setting these will **not** update the server. The
settings are **only** used locally.

### Related issues
Related to #319
Closes #790

### Testing
Emulator manually changing some settings, added additional unit tests to
ensure the overrides are respected when choosing tracks

## Screenshots
### Settings page
<img width="1280" height="720" alt="settings_01 Large"
src="https://github.com/user-attachments/assets/28cc2f6b-3a60-42a4-b268-48695086d566"
/>

### Clicking into "More user profile settings"
<img width="1280" height="720" alt="settings_02 Large"
src="https://github.com/user-attachments/assets/6c3be3aa-c177-4d44-8060-b76d15fb10d7"
/>

### Language choice dialog

Can filter languages by typing in the search box at the top

<img width="1280" height="720" alt="settings_03 Large"
src="https://github.com/user-attachments/assets/c014d0ba-8d5b-4201-a6ee-4876b478148c"
/>


## AI or LLM usage
None
2026-07-14 14:17:31 -04:00
Damontecres
0e62b9582c
Improved lifecycle handling & code clean up (#1653)
## Description
Add some improvements related to life cycle handling whenever the app is
resumed:
- Refreshing the server-side user config 
- Refreshing the home page automatically

Also 401/403 errors accessing recording folders will be skipped since
this typically means the user lost access while the app was running.
Refreshing the server-side user config (above) will also help with this.

Finally, there's some general code clean up in `MainActivity` to move
non-UI related code out of Compose.

### Related issues
Closes #1647

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-07-01 19:18:16 -04:00
Damontecres
ae49e977ce
Fix crash on certain grid pages when the app is sent to the background (#1605)
## Description
Fixes a crash when backgrounding/restoring the app on certain grid pages

### Related issues
Fixes https://github.com/damontecres/Wholphin/issues/1598
Replaces #1599

### Testing
Emulator

It's easy to reproduce by going to a library's recommended tab,
scrolling to any row's view more, then pressing home to background the
app

## Screenshots
N/A

## AI or LLM usage
None, but includes a commit from #1599 which did, see the note there

---------

Co-authored-by: Christian Tismer <tismer@stackless.com>
2026-06-20 20:06:34 -04:00
Damontecres
e17ae3eb8a
Use Wholphin-specific dispatchers (#1583)
## Description
Switches all usages of `kotlinx.coroutines.Dispatchers` to
`WholphinDispatchers`.

`WholphinDispatchers` provides an easier and more reliable mechanism to
use a test dispatcher in unit tests. For regular app usage,
`WholphinDispatchers` just delegates to `Dispatchers`.

There are no user facing changes

### Related issues
None

### Testing
Basic emulator testing & unit tests

## Screenshots
N/A

## AI or LLM usage
None
2026-06-18 11:17:19 -04:00
Damontecres
e2a6ae6be4
Two more bug fixes (#1521)
## Description
Fixes bugs:
- Follow up to #1487 since the 404 exception can be ignored in some
cases
- Fixes a only-on-main-thread exception when clearing the secure flag in
some cases

### Related issues
Related to #1469

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-06-05 10:01:08 -04:00
Damontecres
0b450fc0ff
Remove dependency on livedata libraries (#1453)
## Description
Refactors all usages of `LiveData` into flows in keeping with best
practices. This also includes refactoring several `ViewModel`s to
combine multiple `LiveData` into a new single `StateFlow`.

There are no user facing changes

### Related issues
None

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-25 08:27:19 -04:00
Damontecres
6f11a3711f
Few minor fixes (#1427)
## Description
- Fix always showing the user list on app cold start even with auto sign
in enabled
- Ignore unknown JSON keys when restoring view options or filters from
the database making them more backward compatible for devs switching
branches
- Don't show a view more card on collection detail page rows, it's not
necessary

### Related issues
First bug was introduced by #1367

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-21 17:06:46 -04:00
Damontecres
45d466d76f
Add option to require server login for user profile (#1367)
## Description
This PR adds a new method of protecting a user profile when switching to
it: server login. When enabled, switching to the user requires
authenticating with the server again via Quick Connect or username &
password. This is basically the same as the official client.

### Related issues
Closes #321
Related to #1330 - might cause merge conflicts
Related to #1287 - definitely causes merge conflicts

### Testing
Emulator with various uses cases including auto sign-in, switching
between server login & PIN protection, and removing the protection

## Screenshots
N/A, the login looks identical as adding a new user

## AI or LLM usage
None
2026-05-20 12:00:02 -04:00
Damontecres
ab5c884072
External player follow up fixes (#1294)
## Description
Fixes some issues when using an external player:
- Send playback started before launching so the last played date is
updated
- Only use VLC's position if >0 since it returns 0 when playback
completes

### Related issues
Follow up to #1256 

### Testing
Emulator & nvidia shield

## Screenshots
N/A

## AI or LLM usage
None
2026-04-23 15:08:25 -04:00
Ray
d373b32da5
Fix crash when OS screensaver started before opening Wholphin (#1285)
## Description
If the Wholphin OS screensaver starts before Wholphin (the app) is
opened, a default image loader is used. Then when Wholphin is opened, it
will crash when trying to set the app's image loader.

This PR adds configuring the image loader to the screensaver as well. It
is safe to do this multiple times. It's only an error to try to replace
the default image loader.

### Related issues
None

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-04-20 17:41:24 -04:00
Ray
790069d818
Add support for external player playback (#1256)
## Description
Adds a new playback "backend" to play media in another app such as VLC.

By default, Wholphin will use the system default app. Typically if you
haven't chosen one, a dialog will show to pick which app. You can also
set a specific external app to use in Wholphin independent of the system
default.

Currently, this only support single item playback, ie playing next up
episodes is not supported yet.

Also since there is no standardized way to send resume position,
external subtitle info, etc, Wholphin makes a best effort. VLC,
mpv-android, & MX-Player are specifically tested/supported.

### Related issues
Closes #85

### Testing
Emulator & nvidia shield w/ VLC, mpv-android, & MX-player

## Screenshots
<img width="543" height="407" alt="image"
src="https://github.com/user-attachments/assets/ab37d41e-2909-40ed-b537-191ebb54d979"
/>

## AI or LLM usage
None
2026-04-19 14:41:54 -04:00
Ray
4eabaa9a52
Add option to remove item from Continue Watching and Next Up rows (#1140)
## Description
Adds context menu options for "Remove from continue watching" and
"Remove series from next up"

### Remove from continue watching

This marks the item as unplayed so it will not be shown on the Continue
Watching row anymore. It may still show on a Next Up or combined row
though.

### Remove series from next up

This first does the same as "Remove from continue watching" above. But
it also stores the series' ID in your display preferences as removed.
Wholphin will filter out this series from appearing in the Next Up row
until you watch _any_ episode in the series in the future.

This only works for Wholphin clients!

There's a dialog to view which series have been removed and to re-add
them manually. Settings->Customize Home->Settings->View removed next up

### Related issues
Closes #906

### Testing
Mostly emulator testing

## Screenshots

## AI or LLM usage
None
2026-04-07 16:04:21 -04:00
Ray
feb8c31791
Show toast & add option to fetch release notes when app updates (#1182)
## Description
When Wholphin is updated, it will show a short toast message. This works
for both app store and side loaded installs.

You can now click on the "Installed version" in settings to fetch the
release notes for the current version.

### Related issues
Closes #1058

### Testing
Emulator

## Screenshots
![release_notes
Large](https://github.com/user-attachments/assets/2c03eb74-abb2-4b45-8d6f-01e8ef72116c)

## AI or LLM usage
None
2026-04-03 12:44:39 -04:00
Ray
f9ff04c5b7
Fix issues when the system kills the app during playback (#1116)
## Description
This fixes some issues restoring the app state after it is killed by the
system, such as to free up memory while the screensaver is showing.

### Dev notes

Reproduce:
1. Start playing media from beginning
2. Pause at 10 minute mark
3. Wait for screensaver to start
4. Press a button
5. Media starts from beginning

Note: It may take a few iterations of 2-4 before the OS destroys the
app.

If the app is killed during playback, ideally Wholphin restores state to
_previous_ page, not playback since restoring playback is complex.
Before this PR, the back stack was maintained by Compose's
`rememberSerializable` and would be restored _after_ the
`PlaybackLifecycleObserver` cleans up the playback destination.

This meant that the app would be restored, from scratch, to the playback
page. The `PlaybackViewModel` would be initialized from scratch as it
was originally called, ie play from beginning.

The fix is to save and restore the back stack outside of Compose.

### Related issues
Fixes #767

### Testing
Emulator & nvidia shield

## Screenshots
N/A

## AI or LLM usage
None
2026-03-18 18:09:13 -04:00
Ray
627b89f1db
Updates to Seerr image fetching (#1086)
## Description
This PR makes changes to how images are fetched for discover/Seerr
items.

If the discovered item is available on the Jellyfin server, the images
will be fetched from the Jellyfin server.

Then if the Seerr server has image caching enabled, the images will be
fetched from the Seerr server. Otherwise, images are fetched from TMDB .
TMDB was the previous behavior for all images.

### Dev notes

This PR also updates the Seerr server URLs stored in the database, so
it's not backwards compatible.

### Related issues
Fixes #1079

### Testing
Emulator against Jellyseer 2.7.3 & Seerr 3.0.1

## Screenshots
N/A

## AI or LLM usage
None
2026-03-14 16:47:08 -04:00
Ray
ce44e9593b
Screensaver fixes (#1047)
## Description
Fixes a few issues with the in-app & OS screensavers

- Make sure clock is updating for in-app screensaver
- Fix crash if app is force quit before the OS screensaver starts

### Related issues
Fixes #1045 

### Testing
NVIDIA shield

## Screenshots
N/A

## AI or LLM usage
None
2026-03-06 08:46:47 -05:00
Ray
0004701296
Dev: add simple automated UI test setup (#1027)
## 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
2026-03-03 13:46:46 -05:00
Ray
9b995bf6ba
Fix some IO related crashes (#1008)
## Description
- Catch network exceptions when querying to populate the nav drawer
- Perform screensaver work on IO thread
- Fix possible crash if screensaver service runs while regular activity
is destroyed
- Fix genre images & backdrop images not loading

This PR also adds an Jellyfin SDK `ApiClient` wrapper to push all
network calls onto the IO thread. And tries to use a more strict
network-on-main policy for debug builds.

### Related issues
Fixes #994
Fixes #1001
Should fix #1003

### Testing
Emulator with simulated exception throws

## Screenshots
N/A

## AI or LLM usage
None
2026-02-26 15:48:39 -05:00
Ray
a7e86fbc15
Add in-app & Android TV screensaver (#946)
## Description
Adds both an optional in-app and Android TV OS (DreamService)
screensaver

Has settings for:
- Starting delay, options from 30s to 1 hour
- Duration to show each image, 30s to 2 minutes
- Show clock (separate from app-wide clock)
- Toggle animated zooming
- Set a max age rating to limit what content is shown
- Included media types: movies, tv shows, and/or photos

By default only Movies & TV Shows are included. But if you have like
family albums on your server, you could enable Photos too.

There's also an button to start the screensaver so you can see what it
looks like.

### Related issues
Closes #230

### Testing
Emulator, Onn 4k pro, nvidia shield using both in-app and OS
screensavers

## Screenshots
![screensaver_settings
Large](https://github.com/user-attachments/assets/9b3240dd-81cb-4c20-bc4e-b2bf38698b2f)

## AI or LLM usage
None

## Acknowledgements

Some of the skeleton code for setting up the `DreamService` with the
right lifecycle was copied and modified from the [official app's
implementation](https://github.com/jellyfin/jellyfin-androidtv/blob/master/app/src/main/java/org/jellyfin/androidtv/integration/dream/DreamServiceCompat.kt).
2026-02-25 14:11:00 -05:00
Ray
2a371e7d6b
Fix strings missing from translations (#959)
## Description
Weblate's Android string parser (aka
https://translate.codeberg.org/projects/wholphin/) doesn't support
direct translations of string arrays.

Many of Wholphin's preference options are defined in string arrays and
therefore could not be translated. So this PR implements the [suggested
workaround](https://docs.weblate.org/en/latest/formats/android.html) to
move the string into individual entries and reference them in arrays.

Also moves some hardcoded strings into `strings.xml` so they can be
translated. Note: virtually all error messages are still hardcoded, but
they are only for exceptional cases, so it's probably not a huge
problem.

A minor bug where the backdrop would show briefly after switching users
is fixed.

### Related issues
Fixes #957

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-23 11:17:57 -05:00
Ray
dc3c6dc739
Fix some cases when refresh rate switching didn't work/timed out (#901)
## Description
An attempt to fix #769

1. Always fetch the `DisplayManager` when switching refresh rates
2. Use the Activity context instead of Application context

### Related issues
Fixes #769

### Testing
I still haven't reproduced #769 myself, but this change works fine on my
shield + LG C2

## Screenshots
N/A

## AI or LLM usage
None
2026-02-16 09:04:28 -05:00
Justin Caveda
aeaecc0f59
Fix suggestions mixing content from different libraries and update recommendation logic (#644)
## 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>
2026-02-04 20:50:29 -05:00
Ray
ee440698b0
Set a different style for HDR subtitles & adjust image subtitles opacity (#827)
## Description
Can now configure a different subtitle style for HDR playback.

There is also a new setting to change the opacity/dim image based
subtitles. This is primarily for HDR to dim very bright PGS subtitles,
but the setting is available for SDR too.

Please remember that the styles only apply to unstyled text formats like
SRT.

I haven't been able to consistently get the preview to switch the
display into HDR mode, so the preview is not 100% accurate.

### Related issues
Closes #583

### Testing
Shield pro 2019 w/ LG C2

## AI or LLM usage
None
2026-02-04 18:22:11 -05:00
Ray
733213d6a2
Simplify how display mode is changed (#823)
## Description
Simplifies the refresh rate/display mode change code by removing the
live data go between.

Also, instead of a `CountDownLatch` that blocks the thread, use a
`Deferred` to suspend the coroutine instead.

### Related issues
Might help with #769

### Testing
Tested on nvidia shield
2026-02-03 15:30:24 -05:00
Ray
7c985ae2ac
Fixes not requiring PIN if the app is forced closed (#773)
## Description
Checks if a PIN is needed when restoring the previous session

### Related issues
Addresses
https://github.com/damontecres/Wholphin/issues/321#issuecomment-3797445005
2026-01-26 15:07:27 -05:00
Ray
81d7aad0c6
Fix crash when starting app from next up (#730)
## Description
Fixes a possible crash starting the app from the next up row

### Related issues
Fixes #729
2026-01-20 13:49:46 -05:00
Ray
0f52616d21
Add user PIN protection (#719)
## Description
Adds (back) the ability to set a per-user PIN. This is required to
switch to the user. If the user has a PIN, this overrides and ignores
the "Sign in automatically" setting for that user.

The PIN consists of any combination of D-Pad directional buttons. If you
forget the PIN, you can click the button to login via the server instead
(ie QuickConnect or username/password) which will also remove the PIN so
you can set a new one if desired.

### Related issues
Closes #268 
Replaces #398
Related to #321 & #356

### Screenshots
![enter_pin
Large](https://github.com/user-attachments/assets/aa25364d-1266-410d-846e-8dc32c56a794)
2026-01-19 11:39:29 -05:00
Ray
4c7c465c67
Add integration with Jellyseerr/Seerr (#558)
## 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

![discover](https://github.com/user-attachments/assets/f830b48f-e2f1-43f4-9680-9c6d4e071765)

### 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.
2026-01-12 16:35:27 -05:00
Ray
0958578082
Fixes websocket subscription, date invalidation, & theme music (#616)
## 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 #615
Fixes #568
2026-01-01 22:19:36 -05:00
Ray
977db0474a
Small bug fixes (#614)
## Description
Fixes the screen showing endless loading when toggling on automatic sign
in

Ports a bug fix to the device profile from the official app
(https://github.com/jellyfin/jellyfin-androidtv/pull/5268)
2026-01-01 15:07:32 -05:00
Ray
26a913b05e
Support resolution switching (#597)
## 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 #428 
Fixes #531
2025-12-31 17:11:30 -05:00
Ray
9ae4965c2a
Add resume/continue watching items to Android TV watch next row (#372)
## Details

Adds resume and continue watching items for the logged in user to the
Android TV OS level watch next "channel"
(https://developer.android.com/training/tv/discovery/watch-next-add-programs).

## Issues
Closes #206
Related to #581
2025-12-27 15:40:45 -05:00
Ray
639ce0de71
Don't flash previous app content when resuming app & auto sign-in is off (#585)
## Description
When the app is put into the background with auto sign-in disabled, it
will remove the current page and show a loading indicator. This way when
the app is resumed, the previous content won't flash on screen for a
split second.

The above should technically fix #584 on its own, but as additional safe
guide, this PR also makes sure the `ApiClient` is configured before
trying to create image URLs.

### Related issues
Fixes #584
2025-12-27 15:13:45 -05:00
Ray
a797bd8267
Further improvements to resuming the app (#577)
## Description
Fixes issues when the app is resumed, such as after the screensaver is
dismissed.

For example, if you pause during playback and the screensaver starts,
Wholphin will stop playback and go back to the previous page. But with
this PR, instead of focus being lost and defaulting to teh nav drawer,
focus will restore on the play button or episode card.

### Related issues
Related to #552 &
https://github.com/damontecres/Wholphin/issues/552#issuecomment-3690614491
2025-12-26 15:40:52 -05:00
Damontecres
5d1f4b6154
Merge branch 'release/v0.3.9' 2025-12-25 08:57:20 -05:00
Damontecres
c00c7b8aa9
Catch exceptions during app startup 2025-12-25 08:49:57 -05:00
Ray
b671e14826
Better restoration for auto sign in (#566)
## Description
Improves how the app restores state when auto sign in is enabled. If
auto sign in is enabled, the app will restore back to the last page.

#538 altered the app start up logic. This caused the app to reset state
more often when it goes into the background.

### Related issues
Related to #552
2025-12-24 16:44:41 -05:00