Compare commits

..

1920 commits
v0.2.0 ... main

Author SHA1 Message Date
deb24e3c9f Merge pull request 'Persist the debug signing key across container builds' (#45) from fm/tv-stable-key into main 2026-11-06 19:54:26 +01:00
556d633e88 Persist the debug signing key across container builds
All checks were successful
review/code-gpt Build-script only (docker/gradle.sh + README): persistent debug keystore outside the checkout, readonly bind-mount, fails loudly; firstmate read the diff; lane showed identical signer SHA-256 across two optimized builds and the debug build
2026-11-06 17:37:36 +01:00
34087d8eb5 Merge pull request 'Unify TV control focus rings with the approved card style' (#44) from fm/tv-focus-style into main 2026-11-06 15:58:25 +01:00
33dc65bf18 Merge remote-tracking branch 'origin/main' into fm/tv-focus-style
All checks were successful
review/code-gpt Firstmate read the PR evidence and viewed the before/after pairs and native captures against the approved card ring (4 px straddle, no gap); measured ring bounds, inventory with unchanged fill surfaces listed; 20+ targeted native captures and key/session tests; device verification follows on the optimized build
review/design-claude Firstmate read the PR evidence and viewed the before/after pairs and native captures against the approved card ring (4 px straddle, no gap); measured ring bounds, inventory with unchanged fill surfaces listed; 20+ targeted native captures and key/session tests; device verification follows on the optimized build
2026-11-06 15:52:02 +01:00
ef3ae3f21b Unify focus rings on the approved accent straddle style 2026-11-06 15:52:02 +01:00
d0e38306b3 Merge pull request 'Add a release-derived optimized TV comparison build' (#43) from fm/tv-opt-build into main 2026-11-06 15:45:03 +01:00
5306f8ddc4 Keep optimized versions compatible with upgrade and update parsing
All checks were successful
review/code-gpt Build-only change (new optimized build type, own package, baseline seed, version parser test) read by firstmate; release and debug unchanged; the optimized APK was installed and run on the real box (sign-in, picker, Home, Movies work under R8; 0.4 s cold start, 14x lower p99 frame time than debug); version-suffix defect found on the box and fixed in this head
2026-11-06 15:36:20 +01:00
59f4a06815 Register the optimized baseline profile source explicitly 2026-11-06 15:21:19 +01:00
427b18cdc5 Add a release-derived optimized TV comparison variant 2026-11-06 15:12:01 +01:00
93a41ea2d3 Merge pull request 'Keep the person picker closed after switching users' (#42) from fm/tv-picker-ok into main 2026-11-06 15:01:46 +01:00
1c21f50acb Keep the person picker closed after switching users
All checks were successful
review/code-gpt Picker double-OK fix verified on the real box (B9): three single-OK person switches justin/mindel/shared, no crash, logcat clean; 45 targeted tests, final-head apk build
2026-11-06 14:48:36 +01:00
e8d0c52088 Merge pull request 'Keep TV person switches off the main-thread socket close path' (#41) from fm/tv-person-switch-fix into main 2026-11-06 14:18:51 +01:00
db2541feff Move person-switch client disposal off the main thread
All checks were successful
review/code-gpt Person-switch crash fix verified on the real box (B8: justin/mindel/shared switched via the picker three times, no crash, logcat clean); firstmate read the ConductarrSession disposal-scope delta (generation-guarded, IO disposal, no double close); 53 targeted tests
2026-11-06 14:05:01 +01:00
f37ad0f0fe Merge pull request 'Give TV cards roomy spacing and edge-straddling focus rings' (#40) from fm/tv-spacing-b7 into main 2026-11-06 13:24:16 +01:00
92c5661be0 Preserve poster progress and focus across library rail changes
All checks were successful
review/code-gpt GPT review blockers fixed and read by firstmate: poster progress inset back to Title.cardProgressInset (220 px track) with test tag + regression; library focus-follow effect keyed on columns/focusedRow/header/rowHeight with a 199<->200 regression; token values/ring bounds/toolbar gap passed review on c17447c5; walked on the real box
2026-11-06 13:21:06 +01:00
b9ea593735 Merge remote-tracking branch 'origin/main' into fm/tv-spacing-b7 2026-11-06 13:17:37 +01:00
711dbebf28 Merge pull request 'Fix TV startup navigation and complete title pages' (#39) from fm/tv-walk-fix1 into main 2026-11-06 13:16:43 +01:00
6e0f9952cf Reconcile removed Anime roots without discarding pushed pages
All checks were successful
review/code-gpt GPT review blocker B1 (Anime root removal strands Back) fixed: delta read by firstmate (ShellState reconciles route/root to Home, preservePushed replaces stack root, 111-line regression, 16 targeted tests green); all other review areas cleared on 9e15dbee
2026-11-06 13:11:08 +01:00
c17447c528 Give TV cards roomy spacing and edge-straddling focus rings
Some checks failed
review/code-gpt Spacing review: progress-bar inset regression; rail transition focus invalidation
2026-11-06 12:53:51 +01:00
9e15dbee8c Preserve navigation focus when Anime availability changes
Some checks failed
review/code-gpt Anime removal leaves active root invalid; Back pops a singleton stack (B1).
2026-11-06 12:33:10 +01:00
53b8358317 Hide Anime destinations without visible library titles 2026-11-06 12:25:51 +01:00
5a84666074 Synchronize fresh-sign-in navigation and restore test globals 2026-11-06 12:11:42 +01:00
22850632ec Restrict shell route and Back handling to the current entry 2026-11-06 12:03:20 +01:00
afff383b75 Read live shell state inside cached navigation entries 2026-11-06 11:52:48 +01:00
d401ebc355 Fix TV startup navigation and complete title pages 2026-11-06 11:27:35 +01:00
e794dec253 Merge pull request 'Fix post-sign-in crash from snapshot state writes' (#38) from fm/tv-hotfix-snapshot-write into main 2026-11-06 11:09:51 +01:00
4e6377b628 Keep shell state reads safe in read-only snapshots
All checks were successful
review/code-claude Hotfix verified on the real Xiaomi box (B3 runs, no crash across Home/Discover/Movies/Shows/title/Settings/Requests); 5-file delta read by firstmate: ShellState.rememberedView no longer writes on read, 3 regression tests incl. snapshotFlow and an audit
2026-11-06 10:50:56 +01:00
e9a92eba49 Merge pull request 'Align TV fidelity and recovery behavior' (#37) from fm/tv-fidelity-followups into main 2026-11-06 02:35:14 +01:00
81073c410d Synchronize progressive focus assertions with UI publication
All checks were successful
review/design-claude Claude review: PASS, no blocking finding (head 81073c41)
2026-11-06 02:20:13 +01:00
04b1bdd6aa Align TV layouts and close recovery gaps 2026-11-06 02:15:09 +01:00
bfe3dbbb16 Merge pull request 'Wire real Conductarr data into Wolfskin TV' (#36) from fm/tv-conductarr-wiring into main 2026-11-06 01:41:47 +01:00
ec32346c71 Isolate the artwork test singleton lifecycle
All checks were successful
review/code-claude B1 (artwork credential scoped to the exact Jellyfin scheme/host/port by a network interceptor, fresh ImageLoader, redirect-safe, 2-host test) and B2 (audiences null keeps the name rule, foreground recovery, test) verified by firstmate reading the 9-file delta; final gates green
2026-11-06 01:32:59 +01:00
34ea983a51 Scope artwork credentials and preserve library recovery 2026-11-06 01:25:47 +01:00
09f1d72f30 Keep Home responsive and resolve authorized request recipients
Some checks failed
review/code-claude Block: token sent to image hosts via Coil auth client; failed audiences read hides Jellyfin libraries
2026-11-06 00:31:48 +01:00
78322f588a Integrate real Conductarr TV title and recovery providers 2026-11-06 00:10:27 +01:00
a00db96dc8 Merge current TV recovery states into Conductarr wiring 2026-11-05 23:24:21 +01:00
71a055987c Merge pull request 'Build the Wolfskin TV title, episode and person pages' (#34) from fm/tv-slice5-titles into main 2026-11-05 23:21:03 +01:00
844530404d Integrate shared offline recovery and saved-state isolation
All checks were successful
review/design-claude 13 fixes passed Sonnet re-review at cd625b42; merged head 84453040 = main merge + shared RecoveryToast reuse: 33/34 pairs byte-identical, st-048 adopts the shared toast, 830 tests green (firstmate check)
2026-11-05 22:59:45 +01:00
59c2728d9c Wire selected Jellyfin users to Conductarr TV data 2026-11-05 22:55:54 +01:00
f53defad90 Update Conductarr TV contracts and ratings reads 2026-11-05 22:55:25 +01:00
abec496305 Merge pull request 'Add TV offline recovery and per-person saved-state isolation' (#35) from fm/tv-slice8-states into main 2026-11-05 22:43:13 +01:00
a01c0a78fb Add TV offline recovery and per-person state isolation
All checks were successful
review/design-claude Claude Sonnet cross-family review: pass, no blockers
2026-11-05 22:11:00 +01:00
cd625b4227 Remove unreachable duplicate glyph branches and assets
All checks were successful
review/design-claude Round 2: 13/13 round-1 blockers fixed, no blockers on cd625b42
2026-11-05 21:55:04 +01:00
fc02ce8646 Integrate native Home and replay full title launch paths 2026-11-05 21:49:34 +01:00
2fdd459037 Match title CSS geometry and fix modal focus and subtitle choices 2026-11-05 21:38:46 +01:00
9dd321f07e Merge pull request 'Build the Wolfskin TV Home feed' (#31) from fm/tv-slice3-home into main 2026-11-05 21:34:29 +01:00
061670848f Keep overlapping screen captures in separate namespaces
All checks were successful
review/design-claude Design re-review: B1-B3 fixed, no blocking findings (head 06167084)
2026-11-05 21:07:22 +01:00
575103ce98 Integrate Discover with canonical shared UI 2026-11-05 20:57:57 +01:00
e243894a20 Integrate reviewed Requests and Discover surfaces
Some checks failed
review/design-claude Changes requested: 13 blocking design/behaviour findings (see PR comment)
2026-11-05 20:46:58 +01:00
86d3432fb0 Merge pull request 'Build Discover TV picks, hidden titles and request flows' (#33) from fm/tv-slice6-discover into main 2026-11-05 20:35:19 +01:00
43ae716157 Preserve native title line boxes and async focus 2026-11-05 20:26:43 +01:00
5033c171cb Preserve Requests recovery button activation 2026-11-05 20:24:10 +01:00
130697c2eb Clamp Discover text boxes and preserve recovery button height
All checks were successful
review/design-claude B1-B3 re-measured by firstmate from the lane measurement file against the locked numbers (all within 2 px); delta is the three scoped fixes plus an additive main merge; N1/N2 3-4 px offsets remain as listed follow-ups
2026-11-05 20:22:47 +01:00
4cd75cfa26 Integrate Requests with the shared image cache 2026-11-05 20:20:06 +01:00
bfd4002dd1 Merge Requests screens from main 2026-11-05 20:11:00 +01:00
1c77f21df3 Merge pull request 'Build TV Requests under Settings' (#32) from fm/tv-slice7-requests into main 2026-11-05 20:06:36 +01:00
a8951827ba Reuse shared artwork and correct title focus edges 2026-11-05 19:58:36 +01:00
e99ed2d394 Use the locked Home See all spacing 2026-11-05 19:53:51 +01:00
924c90a9e0 Align readiness glyphs to inline text baselines
All checks were successful
review/design-claude B1-B5/B7 verified fixed by Sonnet re-review at b8d5d8a1; B8 readiness block-line replication checked by firstmate: 6-file delta, baseline/no-gap code only, st-086 pair viewed, two-line wrap is font-caused and recorded; B6 approved by Justin
2026-11-05 19:53:32 +01:00
1299034991 Match Discover shadows, text rhythm and asynchronous focus
Some checks failed
review/design-claude Changes requested: 3 blocking design deviations (st-032 sheet rhythm, st-036 button squeeze, hero stack offset)
2026-11-05 19:39:13 +01:00
9c8fceb18c Restore Home viewport across See all navigation 2026-11-05 19:28:09 +01:00
b8d5d8a124 Preserve contained rating marks inside dark chips
Some checks failed
review/design-claude B1-B5,B7 fixed; B8 readiness item glyph/gap geometry deviates from locked st-086
2026-11-05 19:10:25 +01:00
14a74ac43b Integrate libraries and correct Home review blockers 2026-11-05 19:03:33 +01:00
d9578ac405 Match Requests sheets to locked design frames 2026-11-05 18:58:06 +01:00
4e62aad638 Reuse baseline-aligned headers with source subtitle typography 2026-11-05 18:37:58 +01:00
b1b15b384b Keep catalogue outcomes and recovery consistent across views 2026-11-05 18:32:09 +01:00
c1559e2513 Merge Libraries and reuse the shared catalogue surface 2026-11-05 17:55:49 +01:00
3bfb8cd779 Merge shared library surfaces and connect title navigation 2026-11-05 17:54:34 +01:00
439eea3ff8 Merge library slice and reuse shared image policy
Some checks failed
review/design-claude Changes requested: 7 design-parity findings (separator opacity, line-box rhythm, stage gap, mark chips, avatar, count, thumb fringe)
2026-11-05 17:41:43 +01:00
fc7eb94cc1 Record native title metrics and reuse SDK fixtures 2026-11-05 17:40:55 +01:00
03ff22ea86 Preserve request outcomes and align Discover shelf windows 2026-11-05 17:37:39 +01:00
5b0703aec5 Build household requests under Settings 2026-11-05 17:35:42 +01:00
96538f3c7a Merge pull request 'Build TV libraries and continuous See all grids' (#30) from fm/tv-slice4-libraries into main 2026-11-05 17:35:12 +01:00
b54bcc148c Follow visible card centers and remember Home tracks
Some checks failed
review/design-claude Request changes: separator dot colour, vertical rhythm, focus entry after load
2026-11-05 17:16:06 +01:00
0ca13e684f Merge main after TV shell lands 2026-11-05 17:14:09 +01:00
6d7df0fb19 Preserve title data and focus across updates 2026-11-05 17:13:53 +01:00
321f2d41bb Match library header initials to the locked muted colour
All checks were successful
review/design-claude B1/B2 passed Sonnet re-review at 60a0da13; B3 (header avatar initial muted) checked by firstmate: 3 files, default-preserving letterColor param, only LibraryHeader passes muted
2026-11-05 17:13:47 +01:00
90e956b157 Measure native Discover typography and focus boundaries 2026-11-05 17:04:45 +01:00
2b65ee7b53 Preserve the person identity guard on Home 2026-11-05 16:57:20 +01:00
60a0da13a2 Document visible tail navigation and library typography
Some checks failed
review/design-claude Header avatar initial must be muted like the locked frames; tail Down and separator dot fixed
2026-11-05 16:55:12 +01:00
5a0497c9ec Merge the reviewed shell from main 2026-11-05 16:50:59 +01:00
6f430439a6 Merge the shipped TV shell into Discover 2026-11-05 16:50:52 +01:00
4b28b4f044 Match Discover captions and catalogue collection states 2026-11-05 16:50:38 +01:00
3b7249888f Match Home focus and recovery details 2026-11-05 16:50:32 +01:00
e5bb4ec47f Merge remote-tracking branch 'origin/main' into fm/tv-slice4-libraries 2026-11-05 16:50:24 +01:00
7a878b2d81 Merge pull request 'Build the Wolfskin TV shell and navigation' (#29) from fm/tv-slice2-shell into main 2026-11-05 16:49:28 +01:00
b24c404cc8 Merge the reviewed settings pill border correction
# Conflicts:
#	wolfskin-design/tokens.sha256
2026-11-05 16:44:52 +01:00
a6dd04f390 Keep library focus on visible rows and match caption facts 2026-11-05 16:44:21 +01:00
74033ee1da Match the settings pill border to the prototype
All checks were successful
review/design-claude B1/B2/N1/N4 passed Sonnet re-review at 2ea93010; B3 pill border one-frame re-check by firstmate: dash rhythm and 1dp stroke match, delta additive (6 files, pill only)
2026-11-05 16:28:09 +01:00
7e8e232d47 Merge the corrected TV shell into Discover 2026-11-05 16:19:00 +01:00
c534ea75c2 Use resolved weights for episode rating labels 2026-11-05 16:18:40 +01:00
6a02887dc9 Build the Discover catalogue and request flow 2026-11-05 16:18:19 +01:00
0c94e8f910 Merge reviewed TV shell focus and text fixes 2026-11-05 16:17:42 +01:00
c07bb2ca1d Merge the reviewed shell updates 2026-11-05 16:17:38 +01:00
0db05174b1 Build native title and person surfaces 2026-11-05 16:16:02 +01:00
0fd0ec035d Build the Wolfskin Home feed 2026-11-05 16:15:40 +01:00
3da635b4cb Merge reviewed TV shell changes into libraries
Some checks failed
review/design-claude Changes requested: tail-card Down focus bug and caption separator opacity
2026-11-05 16:11:29 +01:00
27fd174aed Preserve full line boxes in library recovery panels 2026-11-05 16:05:25 +01:00
2ea9301069 Assert the declared owned-screen background colour
Some checks failed
review/design-claude B1/B2 fixed; settings pill dashed stroke 2x spec (blocking)
2026-11-05 15:52:12 +01:00
380c423376 Build TV libraries and continuous See all grids 2026-11-05 15:51:24 +01:00
4b89a73908 Restore upstream content focus and deterministic shell typography 2026-11-05 15:39:56 +01:00
9be55c570a Integrate the Conductarr client with the Wolfskin shell
Some checks failed
review/design-claude Changes requested: switch-off path loses backdrop/Up focus; font weight deviation in captures
2026-11-05 14:50:04 +01:00
f60a22e435 Build the Wolfskin TV shell and navigation 2026-11-05 14:38:38 +01:00
2be4e30826 Merge pull request 'Add the pinned Conductarr TV client and disk-backed saved data' (#28) from fm/tv-conductarr-client into main 2026-11-05 13:18:47 +01:00
adbfe5d0e7 Add the pinned Conductarr TV client and disk cache
All checks were successful
review/code-claude Claude cross-family review: clean, no blocking findings
2026-11-05 12:56:06 +01:00
de452448ba Merge pull request 'Merge Wholphin v1.1.0 while preserving Wolfskin features' (#27) from fm/wolfskin-upstream-1-1-0 into main 2026-11-05 01:49:35 +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
Damontecres
2bb662fd60
Release v1.1.0 2026-11-03 17:55:49 -05:00
Damontecres
1fdc7f7d84
Translations 2026-11-03 (#2169)
Update translations as of 2026-11-03

A big thank you to all of the contributors!
2026-11-03 16:45:35 -05:00
Damontecres
a3bd0137b3
Fix style 2026-11-03 16:35:02 -05:00
Damontecres
1d880c9562
Release v1.1.0 2026-11-03 15:28:48 -05:00
Damontecres
d711760dbe
Minor bug fix (#2167)
## Description
- Fixes typo that could prevent restoring a page with artists or album
artists
- Clarified some javadocs

### Related issues
N/A

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-11-03 14:52:56 -05:00
coulterj
06c1d622bb
Fix HDHomeRun Live TV direct playback by including the live stream ID (#2163)
## Description

I was having trouble playing HDHomeRun channels through Jellyfin in
Wholphin. They started quickly in the official Jellyfin app, but
Wholphin would sometimes spin or fall back when switching channels.

This change includes the live stream ID returned by Jellyfin when
building the Direct Play URL. It also adds a regression test.

### Related issues

Related to #2014 and possibly #1903. My testing was with an HDHomeRun; I
haven’t verified the TVHeadend setups in those reports.

### Testing

Tested on a Google TV Streamer with multiple local channels, FAST
channels, and movie playback. The tested content used Direct Play, and
the HDHomeRun tuners were released after stopping.

All 18 playback tests and the formatting check passed after the
requested test cleanup.

## Screenshots

No UI changes.

## AI or LLM usage

I used OpenAI Codex to help investigate the issue, implement the change
in PlaybackViewModel.kt, add the regression test in
PlaybackViewModelTests.kt, and run validation. Codex also helped draft
this description.
2026-11-03 11:18:41 -05:00
TheMatrixan
e53e8b7c67
Translated using Weblate (Polish)
Currently translated at 100.0% (696 of 696 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-11-03 07:27:18 +00:00
ymir
c5a07f1ab8
Translated using Weblate (French)
Currently translated at 100.0% (696 of 696 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-11-03 07:27:16 +00:00
Priit Jõerüüt
b04cba4d6e
Translated using Weblate (Estonian)
Currently translated at 100.0% (696 of 696 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-11-03 07:27:16 +00:00
idezentas
3a8fa272f4
Translated using Weblate (Turkish)
Currently translated at 100.0% (696 of 696 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-11-02 06:27:17 +00:00
American_Jesus
5dd30ea93f
Translated using Weblate (Portuguese)
Currently translated at 99.8% (695 of 696 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-11-02 06:27:16 +00:00
North-DaCoder
66ca418261
Translated using Weblate (German)
Currently translated at 100.0% (696 of 696 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-11-02 06:27:15 +00:00
SimonHung
4b2c64dcb5
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 99.1% (689 of 695 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-10-31 19:35:01 +00:00
Priit Jõerüüt
7fbee0dff7
Translated using Weblate (Estonian)
Currently translated at 100.0% (695 of 695 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-10-31 19:35:01 +00:00
idezentas
509f566514
Translated using Weblate (Turkish)
Currently translated at 100.0% (693 of 693 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-10-31 19:35:01 +00:00
arcker95
45392b0563
Translated using Weblate (Italian)
Currently translated at 100.0% (693 of 693 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-10-31 19:35:01 +00:00
North-DaCoder
1208699b6e
Translated using Weblate (German)
Currently translated at 99.5% (690 of 693 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-10-31 19:35:01 +00:00
Greeg0ry
78c18b09aa
Translated using Weblate (Russian)
Currently translated at 100.0% (693 of 693 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ru/
2026-10-31 19:35:01 +00:00
Priit Jõerüüt
3a6d75618e
Translated using Weblate (Estonian)
Currently translated at 100.0% (693 of 693 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-10-31 19:35:01 +00:00
PreAcc
0cbcfd9269
Translated using Weblate (Hungarian)
Currently translated at 82.9% (573 of 691 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2026-10-31 19:35:01 +00:00
TheMatrixan
53de7073d4
Translated using Weblate (Polish)
Currently translated at 100.0% (691 of 691 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-10-31 19:35:01 +00:00
SimonHung
b918055b34
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 99.4% (687 of 691 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-10-31 19:35:01 +00:00
ymir
6d37c36c1c
Translated using Weblate (French)
Currently translated at 100.0% (691 of 691 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-10-31 19:35:01 +00:00
Priit Jõerüüt
03c6c78903
Translated using Weblate (Estonian)
Currently translated at 100.0% (691 of 691 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-10-31 19:35:01 +00:00
Prahlis
b25aa56277
Translated using Weblate (Swedish)
Currently translated at 91.0% (629 of 691 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-10-31 19:35:01 +00:00
harupong
43a4e6d296
Added translation using Weblate (Japanese) 2026-10-31 19:35:01 +00:00
tal_sarid
ecbf1e68df
Translated using Weblate (Hebrew)
Currently translated at 100.0% (670 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-10-31 19:35:01 +00:00
danpergal84
6ba2d8ced5
Translated using Weblate (Spanish)
Currently translated at 100.0% (670 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-10-31 19:35:01 +00:00
PreAcc
02261f3993
Translated using Weblate (Hungarian)
Currently translated at 72.9% (489 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2026-10-31 19:35:01 +00:00
filippvl
546c00ba1a
Translated using Weblate (Serbian)
Currently translated at 20.4% (137 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sr/
2026-10-31 19:35:01 +00:00
opakholis
a272cea6a0
Translated using Weblate (Indonesian)
Currently translated at 100.0% (670 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-10-31 19:35:01 +00:00
TheMatrixan
a4abd56274
Translated using Weblate (Polish)
Currently translated at 100.0% (670 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-10-31 19:35:01 +00:00
ymir
545ef636ff
Translated using Weblate (French)
Currently translated at 100.0% (670 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-10-31 19:35:01 +00:00
n8llcaster
b4c5697755
Translated using Weblate (Slovak)
Currently translated at 100.0% (670 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-10-31 19:35:01 +00:00
Priit Jõerüüt
f4178ce803
Translated using Weblate (Estonian)
Currently translated at 100.0% (670 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-10-31 19:35:00 +00:00
North-DaCoder
3fe6054813
Translated using Weblate (German)
Currently translated at 100.0% (670 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-10-31 19:35:00 +00:00
idezentas
e9c8f8483d
Translated using Weblate (Turkish)
Currently translated at 100.0% (670 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-10-31 19:35:00 +00:00
arcker95
ffc4d2fd5e
Translated using Weblate (Italian)
Currently translated at 100.0% (670 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-10-31 19:35:00 +00:00
SimonHung
d03538c8bb
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (670 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-10-31 19:35:00 +00:00
Priit Jõerüüt
6b3a0872b1
Translated using Weblate (Estonian)
Currently translated at 99.8% (669 of 670 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-10-31 19:35:00 +00:00
Damontecres
38bd690c85
Update dependencies (#2157)
Updates dependencies to max versions still supporting API level 23

#2152 will allow for further updates
2026-10-31 15:34:57 -04:00
Ali Alaei
46bf78aac4
Allow play/pause from untrusted media controllers (#2145)
## Description
- From media3 1.11, controllers that aren't trusted get read-only player
commands by default.
- Google Home sends its commands through
com.google.android.apps.mediashell (Google's Cast service on the TV),
which isn't trusted, so play/pause from it was dropped.
- Logcat showed: MediaSessionLegacyStub: Calling play() omitted due to
COMMAND_PLAY_PAUSE not being available
- The change: a MediaSession.Callback (TrustAllControllersCallback in
PlayerFactory.kt) that gives every controller all player commands again.
It uses onConnectAsync and AcceptedResultBuilder(session, controller),
so untrusted controllers keep the restricted session commands.
- It's applied to the video session (createMediaSession) and the music
session (MusicService).
- It probably started with the media3 1.11 bump in #1814.

### Related issues
<!-- If this is a new feature or a change, there must be a discussion in
an issue first, reference the issue here -->
<!-- If fixing a bug, reference the bug issue here, or describe the bug,
including steps to reproduce -->
- Fixes #2087 

### Testing
- JMGO N1S 4K, Google TV, Android 14, ExoPlayer.
- Before the fix: play/pause from Google Home did nothing, and logcat
showed the "omitted" warning.
- After the fix: pause and play from Google Home work. Logcat shows each
command reaching handleSetPlayWhenReady, and the warning is gone.
- The TV remote still works.
- I didn't test music playback; leave that out or test it first.

## Screenshots
- None; no UI changes.

## AI or LLM usage
- Claude Code read the logcat output, found the media3 1.11
untrusted-controller change, and wrote TrustAllControllersCallback plus
the two setCallback(...) calls.
2026-10-31 15:24:07 -04:00
Clint Bird
d2713ff2f5
Can Select Artist or Album Artist to be displayed on Music > Artists Page (#2127)
## Description
The Music / Artists view currently appears to use the artist metadata
from individual tracks. While at a track level this makes perfect sense,
it can result in a very noisy and fragmented artists view.

For example, an album by Eminem might contain tracks with artists such
as:
- Eminem
- Eminem feat. 50 Cent
- Eminem ft. Rihanna
- Eminem & Dr. Dre

Each of these seem to appear as a separate artist entry (even though
they are all tracks from the same album by Eminem)

This becomes particularly noticeable in large music libraries, where
featured artists, collaborations, remixes, and slightly different artist
metadata ends up resulting in a large number of sparsely populated or
duplicate / strange looking artist entries.

This PR adds an option / toggle to determine which metadata field is
used to populate the artists view. This option sits neatly within the
current artist view settings e.g. Music > Artists > View options >
Artist source
- Artist (current behavior)
- Album Artist (use the album artist metadata instead)

That Artist setting stays as the default choice (so nobody sees a change
unless they pick it) but now gives the option to filter down only to
Album Artists if desired - this choice is saved per library and applies
immediately.

### Related issues
Closes #1868

### Testing
I've tested it extensively on an Amazon Fire TV Cube Gen 2 (using the
firetv debug variant) the following manual tests were performed:
- The list changes when you pick Album artist and importantly the
selection survives a full app restart
- It was tested on a large library and can confirm only the Album
Artists remained (e.g. only one Eminem entry remained)

Automated checks were run (testFiretvDebugUnitTest & pre-commit) As well
as two new tests being added to the CollectionFolderViewModelTest.kt:
- For letter counting its checking via the album artist endpoint (and
not the standard one) when the source is set as ALBUM_ARTIST
- When the source is changed via the saveViewOptions its triggering a
load from the album artist endpoint

## Screenshots
Please see the screenshots below showing the UI impact / modification:
<img width="4624" height="3472" alt="1"
src="https://github.com/user-attachments/assets/6b53575e-ad15-4f66-b9c0-074657128a1d"
/>
<img width="4624" height="3472" alt="2"
src="https://github.com/user-attachments/assets/528c5766-44d8-43d3-8519-778f5aab7441"
/>
<img width="4624" height="3472" alt="3"
src="https://github.com/user-attachments/assets/5de2fb6d-173d-4ac3-ad1b-e08f27f4dafd"
/>

## AI or LLM usage
The code was written / supported with Claude - the approach (e.g. design
/ storing the choice in ViewOptions) endpoint research etc was done by
myself - I reviewed the approaches / refactoring and than deployed a
debug.apk to my local device to confirm everything was working as
expected.

I've reviewed each line and can explain it / the thinking to approaches
/ logic etc and am comfortable submitting the PR.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-10-31 14:21:41 -04:00
hawkinslabdev
c45c662a96
Request audio focus for video playback so background music pauses (#2151)
## Description
The video player passes false to setAudioAttributes (since #1258 I
think), as ExoPlayer never requests audio focus. This will pass `true`.

### Related issues
#447, #1258.

### Testing
Not tested on a device.

### Screenshots
Not applicable

### AI or LLM usage
None.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-10-31 13:56:06 -04:00
6bb382a126 Merge pull request 'Show PWA source ratings on every surface through one shared rating row' (#26) from fm/wolfskin-25-ratings-everywhere into main 2026-10-29 21:51:24 +01:00
85365aa3c6 Show each episode's own TMDB score next to the series ratings
All checks were successful
review/code-claude Claude code review: pass, no blocking findings (episode TMDB badge)
2026-10-29 21:35:13 +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
853a3989b2 Merge pull request 'Show PWA source ratings (RT, IMDb, TMDB from Seerr) on the TV' (#24) from fm/wolfskin-23-ratings into main 2026-10-29 20:29:43 +01:00
04d659b115 Tolerate unknown RT labels, expire cached ratings, avoid duplicate collectors and Jellyfin flash
All checks were successful
review/code-claude Reviewed c54acc9c (PASS); review-fix delta re-checked by firstmate
2026-10-29 20:28:51 +01:00
c54acc9c1d Show Rotten Tomatoes before IMDb like the PWA
All checks were successful
review/code-claude Claude code review: pass, no blocking findings
2026-10-29 20:15:08 +01:00
38db9e5127 Show Seerr source ratings on Jellyfin movie and series details 2026-10-29 20:00:31 +01:00
15b082ccd8 Show PWA source ratings on Discover screens 2026-10-29 20:00:31 +01:00
e92ade99df Add source ratings model and Seerr ratings service matching the PWA 2026-10-29 20:00:31 +01:00
Damontecres
5f2f9a8106
Fixes to v1.0.9 (#2072)
## Description
As usual, a PR to fix various smaller bugs in the latest release.

- Fixes not showing the Videos tab on favorites page
- Fixes the Random button not working for photos
- Long nav drawer item names use a marquee scroll if needed
- Fixes a crash when clicking "View More" on a studios rows on the home
page

### Related issues
Fixes #2070
Fixes #2069
Fixes #2135

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-10-28 15:17:24 -04:00
Damontecres
811eaed87e
Change max bitrate during playback (#2097)
## Description
Adds an option to the change the max bitrate during playback. It
defaults to the global setting and can be changed in the settings (gear
icon) on the playback controls.

It is applied for the entire playback session. So if you start an
episode and change it, will be used for each next up episode. If you
back out of playback and start again, it will reset back to the global
default.

### Related issues
Closes #1382

### Testing
Testing on the emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-10-28 15:17:02 -04:00
SergeiAK
2f7804fd5e
Convert Dolby Vision profile 7 to 8.1 on devices without a profile 7 decoder (#2017)
## Description

**On Amlogic boxes (S905X4 and similar) Dolby Vision Profile 7 does not
play as Dolby Vision.**
The server transcodes it, or, with "Direct play Dolby Vision Profile 7"
switched on by hand, the
decoder answers `NoSupport` and fallback ends in HDR10. Devices with a
native Profile 7 decoder,
such as the Shield, are not affected and nothing changes for them.

This PR converts Profile 7 to 8.1 on the fly. It has two halves and one
decision.

The preference `convertDolbyVisionProfile7` is proto field 6 in the
experimental settings, off by
default. In `WholphinRenderersFactory` the filter comes first in the
list, followed by the HDR10+
masking from #1876.

**What this PR does not do.** The enhancement layer is dropped; that is
the price of the
conversion, which is why devices with a native Profile 7 decoder are
left alone.
`BlockAdditional` (an enhancement layer outside the stream) is not read
— like the rest of
Wholphin today — so such a file stays HDR10, as it is now.

### Related issues

Closes #244

Builds on #1876 (the `BitstreamFilter` list). Related to #1540. @fanni42
asked for exactly this on
an S905X4 in #1876.

### Testing

**Hardware** — a debug build of this branch, Homatics box (Amlogic
S905X4, Android 14) + LG BX,
Jellyfin 12.1.0, a Profile 7 FEL remux with the enhancement layer in
band, played from the start
for 39 seconds. Only the new preference was switched on; manual "Direct
play Dolby Vision
Profile 7" was **off**:

* `DolbyVisionProfile7FilterTest` (14) — a fake rewriter on a direct
buffer: offset ≠ 0,
position/limit untouched, every "bytes stay as they were" branch,
three-byte start codes,
  `UnsatisfiedLinkError` tried once;
* `DolbyVisionProfile7ExtractorsFactoryTest` (5, mockk) — only the video
track is rewritten;
* `DolbyVisionProfile7ConversionTest` (6) — codec strings, DRM, `dvh1`.

CI in my fork ran on this branch plus a debug signing commit:
`pre-commit`,
`testDefaultDebugUnitTest` and `assembleDefaultDebug` are all green. The
hardware measurement
above is that build.

## Screenshots

The only UI change is one toggle in the experimental settings.

## AI or LLM usage

The code was written with Claude (Claude Code): the filter, the
extractor wrapper, the condition
and the tests. I reviewed every line and can explain it. It was tested
with the unit tests and
the hardware measurement above. This description was written by me in
Russian and translated
into English with the same assistant.

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Damontecres <damontecres@gmail.com>
2026-10-28 15:16:42 -04:00
Damontecres
6d029086e9
Add detected HDR info to debug page (#2140)
## Description
Adds information to the debug page about detected HDR format for both
the device and the display.

I also wanted to try out the new `Grid` layout composable! It's much
better than the `SimpleTable`/`TableRow` composables I wrote previously.

### Related issues
There's a few issues/PR for improvements to HDR/DV support, so this will
help with debugging

### Testing
Emulator & nvidia shield on HDR TV

## Screenshots
N/A, just tables of info

## AI or LLM usage
None
2026-10-27 18:47:02 -04:00
d78a23a80a Merge pull request 'Serialize Gradle containers sharing a cache volume' (#22) from fm/wolfskin-14-gradle-lock into main 2026-10-27 17:03:03 +01:00
221f623c87 Serialize Gradle containers sharing a cache volume
All checks were successful
review/code-claude Approve: lock held across container, no deadlock, releases on all exits
2026-10-27 15:30:44 +01:00
Damontecres
cecfdaeba9
Fix unit tests after merge 2026-10-26 16:45:42 -04:00
Damontecres
f66746cf47
Auto-configure Discover from Wholphin server plugin (#2134)
## Description
https://github.com/damontecres/jellyfin-plugin-wholphin/pull/3 added a
new endpoint for Seerr configuration in Wholphin server plugin.

This PR adds support for reading that configuration and either
pre-filling the URL or automatically logging in.

Some code was copied from #1418 (thanks @matschi95!), but that PR worked
by inserting a DB record for the plugin provided server. Since the local
DB was preferred, this meant plugin config changes wouldn't be reflected
in the client. This PR instead attempts the connection and only updates
the in-memory connection state, so user switching and app restarts will
re-fetch from the plugin.

If the server plugin is not installed or no Seerr server is configured,
nothing in Wholphin changes.

### Related issues
#598 

This PR replaces #1418


### Testing
Emulator with different scenarios

## Screenshots
N/A

## AI or LLM usage
None
2026-10-26 14:18:56 -04:00
bobberb
90a2d6d459
Show album artist under album name on album cards (#2117)
## 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>
2026-10-26 14:15:29 -04:00
osipovgleb
de67bccb39
Fix: VLC doesn't restart playback from the beginning as expected (#2050)
## 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>
2026-10-26 14:05:37 -04:00
Gabelschnabler
962a14d5db
Add Fire OS voice command permission for MediaSession (#2076)
## 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>
2026-10-26 11:45:59 -04:00
Damontecres
52c26bc0fc
Update playlist details to match library grid (#2113)
## 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
2026-10-25 20:56:18 -04:00
Damontecres
6cde8ce8a6
Show warning for future unsupported server version (#2115)
## 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
2026-10-25 20:52:20 -04:00
bobberb
9218940a69
Rename music "Top Rated Unwatched" row to "Top Rated" (#2118)
## 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>
2026-10-25 20:52:04 -04:00
renovate[bot]
0aab839852
Update Gradle to v9.8.2 (#2103) 2026-10-22 18:53:26 -04:00
renovate[bot]
f2a76a962a
Update serialization to v1.12.0 (#2104) 2026-10-22 18:53:02 -04:00
renovate[bot]
766269da98
Update Github Actions (#2083) 2026-10-22 18:46:53 -04:00
761a5a6145 Merge pull request 'Use Forgejo releases for Wolfskin updates and match installed versions consistently' (#20) from fm/wolfskin-5-updater-gpt into main 2026-10-22 16:13:20 +02:00
1715041df3 Match updater versions by ordering and cover Wolfskin asset selection
All checks were successful
review/code-claude Approved: no blocking findings
2026-10-22 16:06:09 +02:00
be7268cc96 Merge pull request 'Default Seerr login to Jellyfin and prefill the Wolfskin Seerr URL' (#19) from fm/wolfskin-6-seerr-default into main 2026-10-22 16:00:23 +02:00
4ae2171061 Merge pull request 'Change application id to org.justinvisser.wolfskin.tv' (#18) from fm/wolfskin-3-package-id into main 2026-10-22 15:58:32 +02:00
bc220021a8 Merge pull request 'Rebrand the Android TV app as Wolfskin' (#16) from fm/wolfskin-2-rebrand into main 2026-10-22 15:23:03 +02:00
df8614bf8b Merge pull request 'Focus the active profile in the Select User list' (#15) from fm/wolfskin-7-profile-focus into main 2026-10-22 15:11:45 +02:00
c98a9756ac Integrate Wolfskin into theme-specific controls
All checks were successful
review/design-claude Approved at c98a9756; no blockers, see PR comment
review/code-claude Approved at c98a9756; no blockers, see PR comment
2026-10-22 15:07:58 +02:00
e499c3f118 Rebrand the TV app as Wolfskin 2026-10-22 15:05:37 +02:00
0f7a23faa8 Point updater at Forgejo releases and Wolfskin assets
Some checks failed
review/code-gpt Request changes: version equivalence and lost positive asset-selection coverage
2026-10-22 14:47:50 +02:00
af885eae58 Default Seerr login to Jellyfin and prefill Wolfskin Seerr URL
All checks were successful
review/firstmate Exact-diff check: JELLYFIN first, API key last; URL prefill from one constant; unit test; apk-debug+test green
2026-10-22 14:46:41 +02:00
06c783a44a Change application id to org.justinvisser.wolfskin.tv
All checks were successful
review/firstmate Exact-diff check: only applicationId line + Intents.md adb examples; aapt2 badging proof org.justinvisser.wolfskin.tv.debug; apk-debug+test green
2026-10-22 14:46:18 +02:00
344aa9990d Focus the active profile in the user selector
All checks were successful
review/firstmate Diff byte-identical to Justin's upstream commit f816ef03 (Wholphin#2003); worker ran make test + apk-debug green
2026-10-22 14:46:05 +02:00
7d6f041b55 Merge pull request 'Add Wolfskin fork header to README' (#12) from fm/wolfskin-1-readme into main 2026-10-22 14:44:39 +02:00
f312138a75 Merge pull request 'Add pinned Docker APK builds and release signing' (#13) from fm/wolfskin-4-build into main 2026-10-22 14:44:21 +02:00
19adf54c23 Pin platform tools and use the upstream debug unit task
All checks were successful
review/security-claude Carried from reviewed head: identical tree (re-pushed only to shed stale Actions statuses)
review/code-claude Carried from reviewed head: identical tree (re-pushed only to shed stale Actions statuses)
2026-10-22 14:43:07 +02:00
1db83e98d4 Add Wolfskin fork header to README
All checks were successful
review/firstmate Carried from reviewed head: identical tree (re-pushed only to shed stale Actions statuses)
2026-10-22 14:42:47 +02:00
3d6f8bae1a Add pinned container builds and Wolfskin release signing 2026-10-22 12:45:29 +02:00
Damontecres
551501fa75
Add support for server plugin (#1384)
## 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>
2026-10-19 14:41:08 -04:00
Damontecres
e273195e93
Better handling for Seerr request permissions & minor discover UI tweaks (#2084)
## 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
2026-10-19 13:35:51 -04:00
Damontecres
ebf44699ca
Fix experimental settings for AC3 surround sound (#2101)
## 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
2026-10-19 13:35:00 -04:00
Jake
621686d8e2
Include image tags in item image URLs (#2031)
## 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.
2026-10-16 11:28:01 -04:00
Damontecres
3ef221bf91
Improve track persistence for series & episodes (#1802)
## 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 & #1798
Closes #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
2026-10-16 10:53:44 -04:00
mizuka123
55960c2462
Inherit parent view options and sort for sub-folders (#2045)
## 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>
2026-10-16 10:32:12 -04:00
Jeff Whiting
9f557416ea
New user preference to force enable or disable common HDR and Dolby Vision formats (#1644)
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/4021
https://github.com/jellyfin/jellyfin-androidtv/issues/4892
https://github.com/nova-video-player/aos-AVP/issues/1503

https://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>
2026-10-13 15:41:53 -04: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
renovate[bot]
a11cd9fb74
Update Gradle to v9.8.1 (#1818) 2026-10-11 20:10:35 -04:00
renovate[bot]
26659b3faf
Update dependency org.openapi.generator to v7.26.0 (#2058) 2026-10-11 20:09:39 -04:00
renovate[bot]
56836f6203
Update actions/upload-artifact digest to cf430e0 (#2056) 2026-10-11 20:09:26 -04:00
renovate[bot]
d0640f148c
Update Kotlin to v2.4.21 (#2057) 2026-10-11 20:08:51 -04:00
Damontecres
2d87d526db
Release v1.0.9
Some checks failed
Development build / build (push) Has been cancelled
Create release / publish (push) Has been cancelled
2026-10-11 17:15:01 -04:00
Damontecres
5bfbba193a
Fix issue with home reloading on user switch (#2064)
## 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
2026-10-11 09:59:50 -04:00
Damontecres
9b02804075
Translations 2026-10 -10 (#2054)
Updates translations as of 2026-10-10

Thanks to everyone who contributed!

---------

Co-authored-by: n8llcaster <n8llcaster@noreply.codeberg.org>
Co-authored-by: SimonHung <simonhung@noreply.codeberg.org>
Co-authored-by: Sk4hnt42 <sk4hnt42@noreply.codeberg.org>
Co-authored-by: Priit Jõerüüt <jrtcdbrg@noreply.codeberg.org>
Co-authored-by: American_Jesus <american_jesus@noreply.codeberg.org>
Co-authored-by: idezentas <idezentas@noreply.codeberg.org>
Co-authored-by: Greeg0ry <greeg0ry@noreply.codeberg.org>
Co-authored-by: MrGegaz <mrgegaz@noreply.codeberg.org>
Co-authored-by: Andi Chandler <andi@gowling.com>
Co-authored-by: Prahlis <prahlis@noreply.codeberg.org>
Co-authored-by: SergeiAK <sergeiak@noreply.codeberg.org>
Co-authored-by: Outbreak2096 <outbreak2096@noreply.codeberg.org>
Co-authored-by: danpergal84 <danpergal84@noreply.codeberg.org>
Co-authored-by: TheMatrixan <thematrixan@noreply.codeberg.org>
2026-10-10 20:39:02 -04:00
Prahlis
1571591a12
Translated using Weblate (Swedish)
Currently translated at 94.9% (626 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-10-10 10:52:06 +00:00
TheMatrixan
db77aaa3a4
Translated using Weblate (Polish)
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-10-10 10:52:06 +00:00
Prahlis
55d3254b1e
Translated using Weblate (Swedish)
Currently translated at 94.8% (625 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-10-10 10:52:06 +00:00
danpergal84
f14e5a5756
Translated using Weblate (Spanish)
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-10-10 10:52:06 +00:00
danpergal84
68c48f154c
Translated using Weblate (Spanish)
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-10-10 10:52:06 +00:00
Outbreak2096
c879a5713f
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-10-10 10:52:06 +00:00
SergeiAK
6308ad85a9
Translated using Weblate (Russian)
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ru/
2026-10-10 10:52:06 +00:00
Prahlis
ba5351f8dc
Translated using Weblate (Swedish)
Currently translated at 94.6% (624 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-10-10 10:52:06 +00:00
Prahlis
4502a12dcb
Translated using Weblate (Swedish)
Currently translated at 92.7% (611 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-10-10 10:52:06 +00:00
Andi Chandler
b03325accb
Translated using Weblate (English (United Kingdom))
Currently translated at 9.4% (62 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/en_GB/
2026-10-10 10:52:06 +00:00
MrGegaz
e4c3257c13
Translated using Weblate (Croatian)
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hr/
2026-10-10 10:52:06 +00:00
Greeg0ry
26d3e5b6fc
Translated using Weblate (Russian)
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ru/
2026-10-10 10:52:06 +00:00
idezentas
121208e021
Translated using Weblate (Turkish)
Currently translated at 99.8% (658 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-10-10 10:52:05 +00:00
American_Jesus
0e0d50753b
Translated using Weblate (Portuguese)
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-10-10 10:52:05 +00:00
Priit Jõerüüt
29e235ffa5
Translated using Weblate (Estonian)
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-10-10 10:52:05 +00:00
Sk4hnt42
c0eceba16c
Translated using Weblate (German)
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-10-10 10:52:05 +00:00
SimonHung
22254bac32
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-10-10 10:52:05 +00:00
n8llcaster
0e11b8ff6f
Translated using Weblate (Slovak)
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-10-10 10:52:05 +00:00
Damontecres
d6523ce856
Fixes restoring focus on the correct episode (#2051)
## 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
2026-10-09 17:58:12 -04:00
Damontecres
e23c446bb7
Fix issue when scrolling on collection details page (#2048)
## 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
2026-10-08 09:51:26 -04:00
Damontecres
b99f21efa8
Various bug fixes (#2010)
## 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 #1990 
Fixes #1965

### Testing
Emulator mostly

## Screenshots
N/A

## AI or LLM usage
None
2026-10-08 08:09:20 -04:00
Damontecres
a38eb3e3d8
Fix possible crash on a few pages due to color space (#2036)
## 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
2026-10-07 19:46:14 -04:00
Ayaz Patel
f5dedeab81
Fix baseline profile never being installed (#1984)
## 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>
2026-10-04 18:18:08 -04:00
Damontecres
559abe2458
Add setting to force transcoding above a max resolution (#1955)
## 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 #756
Closes #1594

### Testing
Manual emulator testing

## Screenshots
N/A

## AI or LLM usage
None
2026-10-03 18:41:18 -04:00
Damontecres
b0d270380d
Better home page header for Seasons (#2016)
## 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
2026-09-30 17:35:29 -04:00
Damontecres
cf74736579
Listen for user config changes and improved websocket error handling (#2012)
## 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
2026-09-29 12:43:13 -04:00
Jycreyn
a577ae9f1a
Add option to prefer Dolby Vision over HDR10+ on hybrid videos (#1876)
## Description

Adds a "Prefer Dolby Vision over HDR10+" switch (off by default) under
Experimental settings. That screen only shows up once "Experimental
settings" is enabled under Advanced, and until then the switch is read
as off: it goes through `experimentalPreferences.get {}`, so nothing is
wrapped and the playback path is untouched.

Most current 4K web releases carry both Dolby Vision (profile 8.1) RPUs
and HDR10+ dynamic metadata in the same HEVC stream. Some MediaTek based
devices (Philips and TCL TVs, some Fire TV models) latch onto the HDR10+
metadata on those files and never engage Dolby Vision, even though a
Dolby Vision decoder is selected.

I isolated this on my Philips 55OLED810 by playing the same episode
three ways, direct play in all three cases:

| | Stream | TV shows |
|---|---|---|
| A | MKV with DV 8.1 + HDR10+ | HDR10+ |
| B | Same MKV with the HDR10+ metadata removed using
[hdr10plus_tool](https://github.com/quietvoid/hdr10plus_tool), DV
untouched | Dolby Vision |
| C | Same stream remuxed to MP4, both kinds of metadata present |
HDR10+ |

The container doesn't matter and the Dolby Vision signaling is picked up
correctly. The only thing the app can do is keep the HDR10+ metadata
away from the decoder.

When the switch is on, and only when a Dolby Vision decoder was actually
selected, the HDR10+ SEI messages (SMPTE ST 2094-40, recognized by their
ITU-T T.35 header `B5 003C 0001`) are masked in each access unit before
it is queued to MediaCodec. The SEI payload type byte is overwritten
with an unused value (254), so the decoder skips the message. That is
one byte rewritten in place per message: the masking filter returns the
size it was given, so the access unit is queued exactly as long as it
came out of the extractor, which is what makes it safe with both the
synchronous and asynchronous MediaCodec adapters. media3 does the same
thing for HAGC metadata in `HevcSeiUtil`; this filter only targets ST
2094-40, so other T.35 SEI such as CEA-708 captions are not affected.
The filter was written from the H.265 spec, no Kodi or media3 code was
copied.

The hook is generic rather than HDR10+ specific, as asked in the thread.
`BitstreamFilteringCodecAdapterFactory` wraps the codec adapter with a
`List<BitstreamFilter>`, and a filter is `(ByteBuffer, offset, size) ->
Int` where the returned `Int` is the new size of the access unit: the
adapter queues the size the chain reports, and hands each filter the
size the previous one reported. A filter which only overwrites bytes
returns the size unchanged, which is what the HDR10+ masking does; a
filter which drops NAL units compacts what it keeps and reports the
shorter size. A reported size which is negative or larger than the one
the filter was given is ignored rather than queued. That is what lets
the Dolby Vision profile 7 to 8.1 conversion of #244 arrive later as one
more filter instead of a second copy of the adapter plumbing.

When the switch is off, the playback path is unchanged. On a device
without a Dolby Vision decoder (or a display without DV support) the
wrapper is never installed, so HDR10+ only devices keep their metadata.
Encrypted samples, codec config buffers and empty buffers are passed
through untouched.

Kodi has an equivalent option for the same reason: xbmc/xbmc@032cbfeb
and xbmc/xbmc@e2c54e7f.

### Related issues

Implements what was requested in #1855. The filter list is what #244
(Dolby Vision profile 7 direct play) needs, so that conversion can be
added as another filter. #1895 implemented the same masking
independently and was closed in favour of this PR; the device profile
part of it was not carried over, see the note under Testing. Upstream:
androidx/media#1895, where the maintainers say the bug is in the device
codecs and that they don't plan a workaround in ExoPlayer itself.

## AI or LLM usage

The implementation (bitstream filter, MediaCodecAdapter wiring, unit
tests) were written with AI assistance (Claude). I reviewed the changes
and validated the behavior myself on a real MediaTek TV (Philips
55OLED810), with the setting on and off, on hybrid and DV-only content.

---------

Co-authored-by: Jycreyn <github.2ilp7@passmail.com>
2026-09-29 12:41:40 -04:00
Damontecres
65954a99eb
Fix unnecessary home page reloads (#2008)
## Description
Fixes the home page reloading from scratch when the app is resumed

This happened because the `userDto` is updated on app resume and since
it has a last accessed timestamp, it would always different and trigger
a reload.

### Related issues
Bug introduced by #1981

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-09-28 13:18:41 -04:00
renovate[bot]
365f8f8ada
Update android-actions/setup-android digest to be39fa8 (#1986) 2026-09-18 11:16:50 -04:00
Damontecres
170989c31e
Guard against crash if episode has seasonId but no seriesId (#1964)
## Description
When clicking on an episode, the app assumes if there is a season ID for
the episode, then there must be a series ID. However, on my JF 12.0
server, an episode (with no metadata) appeared on my Continue Watching
and it has a season ID (which in turn has a series ID) but no series ID.

I'm not sure if this really a valid situation or if it's a server bug,
but Wholphin shouldn't crash at least. So this PR checks both `seasonId`
and `seriesId` and if either is null, falls back to the generic "Episode
Page". Similarly, if a season has no `seriesId` (again, not sure if a
valid situation), a crash won't occur, however there is no generic
"Season Page", so an error page is shown.

### Related issues
TODO, check JF server issues, maybe fixed in 12.1?

### Testing
Emulator

## Screenshots
N/a

## AI or LLM usage
None
2026-09-17 14:41:45 -04:00
Damontecres
32a56e8387
Wait for switched user's home settings before fetching data (#1981)
## Description
There is a race condition when switching users between
`HomeViewModel.init` and `HomeSetttingsService.loadCurrentSettings`.
This happens more often if the new user doesn't have a customized home
page. The result is that the previous user's home page settings are used
for the initial load of the home page.

This PR fixes this by collecting the flows for current user & home
settings, waiting until both are valid and refer to the same user, then
fetch the data.

### Related issues
Fixes #1976
Maybe related (and fixes) #1980, but need to investigate more

### Testing
Emulator. It's easy to reproduce by adding a manual `delay(2.seconds)`
at the start of `HomeSetttingsService.loadCurrentSettings`.

## Screenshots
N/A

## AI or LLM usage
None
2026-09-17 14:41:36 -04:00
Damontecres
ffdebd43c3
Update intent parsing to allow for user switching to home page (#1962)
## Description
Makes the `itemId` parameter optional allowing just passing
`serverId`/`userId` to switch the user and load the app at the home page
for that user.

Dev note: I added a new assert function `assertIs` which asserts that an
object `is` of a type. It also has a contract so the compiler will
smart-cast usages after the `assertIs`. Checking a result type is a very
common pattern in the unit tests, so this assert will simplify a lot of
current & future tests.

### Related issues
Closes https://github.com/damontecres/Wholphin/discussions/1824

### Testing
Emulator and added unit tests for `parseIntent`

## Screenshots
N/A

## AI or LLM usage
None
2026-09-15 13:07:08 -04:00
renovate[bot]
8effd2b1e7
Update actions/setup-java action to v6 (#1907) 2026-09-15 12:46:02 -04:00
renovate[bot]
61f7f70d36
Update dependency org.robolectric:robolectric to v4.17 (#1446) 2026-09-15 12:45:06 -04:00
Damontecres
62965eae48
Fix CI setup step (#1974)
## Description
Looks like `tools` was replaced by `cmdline-tools`
(https://developer.android.com/tools#tools-sdk), so updating the setup
action for this.

### Related issues
https://github.com/android-actions/setup-android/issues/537

### Testing
N/A

## Screenshots
N/A

## AI or LLM usage
None
2026-09-15 10:53:14 -04:00
Damontecres
7af7bc0aa7
Release v1.0.8
Some checks failed
Create release / publish (push) Has been cancelled
2026-09-14 10:04:09 -04:00
Damontecres
45c1ceca27
Fix favorites not querying for people & fix restoring focus on search page for discover items (#1961)
## Description
Fix two bugs:
- Favorites page didn't query for favorite people, introduced by #1864
- Fixes restoring focus on the search page after clicking a discover
item and going back

### Related issues
N/A

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-09-13 20:09:08 -04:00
Damontecres
2fb295cbed
Translations 2026-09-12 (#1957)
Updates translations as of 2026-09-12

Thanks again to all of the contributors!
2026-09-12 18:53:43 -04:00
Damontecres
190c6be41a
Run pre-commit 2026-09-12 18:43:14 -04:00
Prahlis
af6a3b9916
Translated using Weblate (Swedish)
Currently translated at 92.2% (608 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-09-12 22:39:03 +00:00
TheMatrixan
a60433dbbf
Translated using Weblate (Polish)
Currently translated at 99.6% (657 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-09-12 22:39:03 +00:00
arcker95
f181770870
Translated using Weblate (Italian)
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-09-12 22:39:03 +00:00
SimonHung
923f0b7ea3
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (659 of 659 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-09-12 22:39:03 +00:00
TheMatrixan
391a38e777
Translated using Weblate (Polish)
Currently translated at 98.6% (648 of 657 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-09-12 22:39:03 +00:00
SimonHung
cd0128e1db
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (657 of 657 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-09-12 22:39:03 +00:00
Priit Jõerüüt
0db4d45dbc
Translated using Weblate (Estonian)
Currently translated at 100.0% (657 of 657 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-09-12 22:39:02 +00:00
Prahlis
584c3566d6
Translated using Weblate (Swedish)
Currently translated at 90.7% (595 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-09-12 22:39:02 +00:00
American_Jesus
f79c0bdfe7
Translated using Weblate (Portuguese)
Currently translated at 100.0% (656 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-09-12 22:39:02 +00:00
MrGegaz
bad47eb638
Translated using Weblate (Croatian)
Currently translated at 100.0% (656 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hr/
2026-09-12 22:39:02 +00:00
Andi Chandler
8a570b89fb
Translated using Weblate (English (United Kingdom))
Currently translated at 8.6% (57 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/en_GB/
2026-09-12 22:39:02 +00:00
opakholis
7bd1e993d0
Translated using Weblate (Indonesian)
Currently translated at 100.0% (656 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-09-12 22:39:02 +00:00
n8llcaster
c0f6293ea7
Translated using Weblate (Slovak)
Currently translated at 100.0% (656 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-09-12 22:39:02 +00:00
Prahlis
6647cf338c
Translated using Weblate (Swedish)
Currently translated at 90.3% (593 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-09-12 22:39:02 +00:00
marinosGR
3598e3010b
Translated using Weblate (Greek)
Currently translated at 51.0% (335 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/el/
2026-09-12 22:39:02 +00:00
Priit Jõerüüt
60a0d633bd
Translated using Weblate (Estonian)
Currently translated at 100.0% (656 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-09-12 22:39:02 +00:00
danpergal84
5ee6b31018
Translated using Weblate (Spanish)
Currently translated at 100.0% (656 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-09-12 22:39:02 +00:00
idezentas
1e86616d2a
Translated using Weblate (Turkish)
Currently translated at 99.8% (655 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-09-12 22:39:02 +00:00
tal_sarid
7b39854621
Translated using Weblate (Hebrew)
Currently translated at 100.0% (656 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-09-12 22:39:02 +00:00
lednurb
8e050a0830
Translated using Weblate (Dutch)
Currently translated at 91.6% (601 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-09-12 22:39:02 +00:00
SimonHung
114df65de3
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (656 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-09-12 22:39:02 +00:00
North-DaCoder
d6b1d77c28
Translated using Weblate (German)
Currently translated at 100.0% (656 of 656 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-09-12 22:39:02 +00:00
lednurb
6e12b0c776
Translated using Weblate (Dutch)
Currently translated at 92.6% (601 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-09-12 22:39:02 +00:00
Bone NI
026f8c7592
Added translation using Weblate (Lao) 2026-09-12 22:39:02 +00:00
alexkahler
2ade8173ff
Translated using Weblate (Danish)
Currently translated at 99.8% (648 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-09-12 22:39:02 +00:00
Andi Chandler
8c2cbcb7b2
Translated using Weblate (English (United Kingdom))
Currently translated at 7.8% (51 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/en_GB/
2026-09-12 22:39:02 +00:00
American_Jesus
de6cf7fb59
Translated using Weblate (Portuguese)
Currently translated at 100.0% (649 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-09-12 22:39:02 +00:00
TheMatrixan
c8d202c3fa
Translated using Weblate (Polish)
Currently translated at 99.6% (647 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-09-12 22:39:02 +00:00
brdaustra
7f587b9f50
Translated using Weblate (Norwegian Bokmål)
Currently translated at 99.3% (645 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nb_NO/
2026-09-12 22:39:01 +00:00
Prahlis
cf83266e4a
Translated using Weblate (Swedish)
Currently translated at 90.4% (587 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-09-12 22:39:01 +00:00
n8llcaster
0398f3f689
Translated using Weblate (Slovak)
Currently translated at 100.0% (649 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-09-12 22:39:01 +00:00
tal_sarid
cbebc9e63c
Translated using Weblate (Hebrew)
Currently translated at 100.0% (649 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-09-12 22:39:01 +00:00
arcker95
e73d0961ab
Translated using Weblate (Italian)
Currently translated at 100.0% (649 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-09-12 22:39:01 +00:00
Outbreak2096
5a9e5c406f
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (649 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-09-12 22:39:01 +00:00
SimonHung
c0401914a7
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (649 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-09-12 22:39:01 +00:00
Priit Jõerüüt
0b275a2daa
Translated using Weblate (Estonian)
Currently translated at 100.0% (649 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-09-12 22:39:01 +00:00
danpergal84
8931bf5d7a
Translated using Weblate (Spanish)
Currently translated at 100.0% (649 of 649 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-09-12 22:39:01 +00:00
Priit Jõerüüt
4f4e13723c
Translated using Weblate (Estonian)
Currently translated at 100.0% (647 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-09-12 22:39:01 +00:00
tal_sarid
db7cfaee9b
Translated using Weblate (Hebrew)
Currently translated at 100.0% (647 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-09-12 22:39:01 +00:00
arcker95
048a5f35b6
Translated using Weblate (Italian)
Currently translated at 100.0% (647 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-09-12 22:39:01 +00:00
Prahlis
9602b4f3f0
Translated using Weblate (Swedish)
Currently translated at 90.5% (586 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-09-12 22:39:01 +00:00
olliveiraanderson
d6eb23d29f
Translated using Weblate (Portuguese (Brazil))
Currently translated at 96.1% (622 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt_BR/
2026-09-12 22:39:01 +00:00
true-seeker
f3239c7b15
Translated using Weblate (Russian)
Currently translated at 65.0% (421 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ru/
2026-09-12 22:39:01 +00:00
jatag41526
f7c391ad54
Translated using Weblate (Russian)
Currently translated at 65.0% (421 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ru/
2026-09-12 22:39:01 +00:00
SergeiAK
02163ba47c
Translated using Weblate (Russian)
Currently translated at 65.0% (421 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ru/
2026-09-12 22:39:01 +00:00
Damontecres
98fba1340f
Fix OS screensaver starting during playback with dim enabled (#1956)
## Description
Fixes some devices activating the OS screensaver during playback when
the in-app dim was enabled.

I was not able to reproduce this on Shield 2017/2019, but could easily
on a Fire TV Stick. #1834 added the dim feature and, when enabled, would
not pass the keep screen on flag to the OS during playback. It seems
maybe regular Android TV's screensaver also listened to active media
session to prevent starting but maybe Fire TV OS doesn't. I'm not 100%
sure. This PR changes the logic slightly so that the keep screen on flag
is always set during playback.

### Related issues
Fixes #1894
Related to dim feature from
https://github.com/damontecres/Wholphin/pull/1834

### Testing
Emulator, shield 2019, & fire tv stick 4k

## Screenshots
N/A

## AI or LLM usage
None
2026-09-12 18:38:54 -04:00
Damontecres
074eab22f3
Increase memory for CI builds (#1954)
## Description
Increases the memory available for CI builds and splits up builds a bit
more

There are no user facing changes

### Related issues
Recently dev builds failed due to OOM

### Testing
https://github.com/damontecres/Wholphin/actions/runs/34710759629

## Screenshots
N/A

## AI or LLM usage
None
2026-09-12 15:14:14 -04:00
Damontecres
4a118b6ddb
Update dependencies (#1952)
## Description
Now that the SDK level is updated, many dependencies can be updated as
well.

This includes a fix from `wholphin-extensions` for a TLS timeout issue
on some devices using MPV
(https://github.com/damontecres/wholphin-extensions/pull/13).

### Related issues
Fixes #1932

### Testing
Just basics on emulator & nvidia shield

## Screenshots
N/A

## AI or LLM usage
None
2026-09-11 15:40:38 -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
SergeiAK
6486bd19ed
Fix person credits: movie release dates, full filmography, sorted order (#1950)
<!-- By submitting this pull request, you acknowledge that you have read
the [contributing
guide](https://github.com/damontecres/Wholphin/blob/main/CONTRIBUTING.md,
including the AI/LLM policy, and [developer's
guide](https://github.com/damontecres/Wholphin/blob/main/DEVELOPMENT.md)
-->

## Description

Person pages that get a filmography from Seerr had three problems, all
in the same place.

The release date was taken from `firstAirDate` alone — the field TMDB
fills for series — so
movie cards showed no year at all. The library person page capped the
row at 25 cast + 25 crew while
`DiscoverPersonPage` had no cap, because each screen carried its own
copy of the same logic.
And `cast + crew` left the row in no order a viewer can read, with a
title appearing twice when
a person is both cast and crew on it.

This PR:

* maps credit dates as `releaseDate ?: firstAirDate` — the expression
the search result mapping
  already uses;
* moves the credits logic into a single `SeerrService.personCredits()`
used by both screens, so
  they cannot drift apart again;
* drops the 25 + 25 cap, de-duplicates by TMDB id and sorts by release
date descending, with
  undated entries last.

Rows on movie and series pages are untouched: only the
`BaseItemKind.PERSON` branch of
`similar()` changed.

Two things worth your opinion:

* Without the cap, the row for a person with a long filmography is long
— 135 cards for Cameron
Diaz, for example. If you would rather keep a cap, the row could keep
the newest 25 and gain a
`ViewMoreCard` opening the full grid — `Destination.DiscoveredItem` with
a `PERSON` item
already routes to `DiscoverPersonPage`. I am happy to add that in a
second commit.
* The sort is fixed to release date descending. It now lives in one
function, so a different key
  or a user-facing choice would be cheap to add later.

### Related issues

Fixes #1873

### Testing

CI in my fork ran the repository's own PR workflow on this branch:
`pre-commit` (ktlint) and
`assembleDefaultDebug` with `testDefaultDebugUnitTest` are both green.

I then installed a debug build of the same code on an NVIDIA Shield TV
Pro 2019 (Android 11),
against Jellyfin 10.11.11 with Jellyseerr, and opened Cameron Diaz from
the cast row of
*Charlie's Angels* before and after the change:

* movie cards show a year now; before, the only two cards in the row
with a year were the two
  TV entries;
* the row is ordered by release date descending, movies and series
interleaved, instead of the
  API order of all cast followed by all crew;
* the row is no longer cut at 25 + 25 — it now runs back to her 1952
credit, and the entries
  TMDB has no date for sit at the very end of the row.

Not tested on any other device; nothing in the change is device
specific.

## Screenshots

Person page of Cameron Diaz, reached from the cast row of *Charlie's
Angels*. The library rows
above the Discover row are the same in every shot and are useful as a
reference: they always had
their years.

Before, start of the Discover row — no year under any card:
<img width="1920" height="1080" alt="pr1873-1-before-start"
src="https://github.com/user-attachments/assets/585d0670-ae39-4214-9004-0c92262991da"
/>

Before, end of the Discover row — the row stops here, and the only two
years in it belong to the
two TV entries:
<img width="1920" height="1080" alt="pr1873-2-before-end"
src="https://github.com/user-attachments/assets/85c58575-2ad0-4369-a103-60f04c8d2350"
/>

After, start of the row — years on the movie cards, ordered by date
descending:
<img width="1920" height="1080" alt="pr1873-3-after-start"
src="https://github.com/user-attachments/assets/804745c5-e1a6-4fe4-93bd-dc6c97a7a876"
/>

After, end of the row — the list now reaches 1952, with the undated
entries last:
<img width="1920" height="1080" alt="pr1873-4-after-end"
src="https://github.com/user-attachments/assets/2c32389c-ec0b-4df5-b9d1-f564ef10d8b3"
/>

## AI or LLM usage

The code was written with Claude Opus 5 (Claude Code): the two mapping
lines, the extracted
`personCredits()` function and the two call sites. I reviewed every line
and can explain it.
This description was written by me in Russian and translated into
English with the same
assistant.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Damontecres <damontecres@gmail.com>
2026-09-11 15:22:02 -04:00
Damontecres
9c56965c82
Add controls to edit playlists (#1927)
## Description
Adds support to remove items from a playlist and move items within a
playlist

The remove option is in the context menu. Moving has buttons on the
right side. The buttons are available if the user has edit permissions
and the playlist is sorted by "Default" (the playlist order). Remove
from playlist option is available if the user has edit permissions.

Also, thumb images are now preferred instead of primary/poster images
since the layout is much wider than tall.

### Related issues
Closes #1881

### Testing
Emulator with both a user with & without permissions to edit

## Screenshots
### With move buttons
<img width="1280" height="720" alt="playlist_buttons Large"
src="https://github.com/user-attachments/assets/ce7880c3-b823-4b69-9ad4-cfc9e7ee5fd5"
/>

### Without move buttons
<img width="1280" height="720" alt="playlist_no_buttons Large"
src="https://github.com/user-attachments/assets/f2027052-4628-4d44-8257-6c2a5e8c3af4"
/>


## AI or LLM usage
None
2026-09-10 17:18:26 -04:00
Damontecres
0b995b5404
Configure playback controls for RTL (#1945)
## Description
Reverts/fixes parts of #1404 for the playback UI. Generally the playback
UI always uses left-to-right for seek bar & media controls even on RTL.
Other buttons or texts will be RTL.

### Related issues
Fixes #1397 

### Testing
Emulator w/ forced RTL English

## Screenshots
See
https://github.com/damontecres/Wholphin/issues/1397#issuecomment-5553711593

## AI or LLM usage
None
2026-09-08 22:51:54 -04:00
Damontecres
4a567cbcf4
Fixes to season/episode search with duplicates (#1946)
## Description
Fixes cases where the server has duplicate season IDs causing the season
index search to fail and default to the first season in the list.

### Related issues
Fixes #1837

### Testing
Emulator & unit tests

## Screenshots
N/A

## AI or LLM usage
None
2026-09-08 22:51:46 -04:00
Damontecres
1778bdb34c
Consolidate sending media reports & app logs (#1936)
## Description
Consolidates sending reports/logs to the server into
`ServerReportService` (renamed from `MediaReportService`)

Also ensures a consistent header is used including the build flavor.

### Related issues
N/A

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-09-05 12:11:38 -04:00
Damontecres
39239dd444
Add wrap around scrolling to settings pages (#1937)
## Description
Add scrolling from top to bottom and vice versa. This makes it quicker
to get to Advanced Settings for example.

### Related issues
N/A

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-09-05 12:11:30 -04:00
Tal Sarid
1f3a861c47
Localized formatting for season and episode display (#1884)
## Description
Added localized formatting for season/episode numbers across the UI
because it was in English-only in every UI language.

### Related issues
None

### Testing
Tested on a physical Google TV Streamer 4K device. Checked manually
relevant screens while browsing and during playback.

## Screenshots
No frontend changes are visible in English and new translations will be
contributed through Weblate for other languages later, as usual.

## AI or LLM usage
Generated initial code with AI, then manually passed over it while
cleaning and refactoring where needed.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-09-04 12:47:06 -04:00
Damontecres
826bbad49c
Fixes to Now Playing page (#1915)
## Description
Fixes several issues on the Now Playing page

- Keep controller focus after lyrics are focused
- Fix the queue not visually updating until recomposition
- The first focused queue shown/focused is now what's currently playing

### Related issues
Fixes #1821

### Testing
Emulator

## Screenshots
N/A, just behavior fixes

## AI or LLM usage
None
2026-09-02 02:50:42 -04:00
Damontecres
6fbf6209a1
Fixes to default home view options for music & videos (#1926)
## Description
This PR fixes how cards are displayed initially when creating the
default home page or loading it from the web config:
- Ensure view options are selected based on the library type (this is
how it's done already when manually adding a row)
- Support more home row types such as photos and music

### Related issues
Fixes https://github.com/damontecres/Wholphin/issues/1838

### Testing
Emulator, resetting to default and loading from web config

## Screenshots
N/A, music cards will be square instead of poster sized

## AI or LLM usage
None
2026-09-01 17:52:47 -04:00
Damontecres
cedca9b2b1
Add button to navigate to random item (#1916)
## Description
Adds a random button on library tab/grid pages which navigates to a
random item in the list. This goes to the item's detail page.

The random item is chosen with the filters applied. So you could filter
by unplayed Action movies with 7+ stars and the random button will
select from those movies.

Pressing back restores focus on the random button so it's easy to
quickly retry.

### Related issues
Closes #838

### Testing
Emulator

## Screenshots
<img width="935" height="105" alt="image"
src="https://github.com/user-attachments/assets/170b495c-b47d-41ce-93b5-99831f2064c7"
/>
<img width="936" height="114" alt="image"
src="https://github.com/user-attachments/assets/3efa6a11-ebe8-42d7-b023-354b1e6eda37"
/>



## AI or LLM usage
None
2026-09-01 16:07:26 -04:00
Damontecres
5363db0b80
Update API level to Android 17 (#1908)
## Description
Updates the compile & target SDKs to 37 (Android 17).

The only required change is to support the new `ACCESS_LOCAL_NETWORK`
permission. This is required for Android 17 devices to auto discover
Jellyfin servers on the local network. Wholphin will prompt for
permission and if granted, use the discover servers feature. If
permission is not granted, the user must enter the server URL manually.

### Related issues
N/A, but several dependency renovate PRs are blocked by this

### Testing
I tested with a regular Android 17 emulator. There are no Android TV 17
images (or probably even devices) available yet.

## Screenshots
N/A

## AI or LLM usage
None
2026-08-29 21:35:58 -04:00
Damontecres
cc2e7dfdda
Fix image loading issues related to VPN and/or Fire OS 6 (#1910)
## Description
This PR override Coil's built-in network connectivity checker with one
that allows returns true. The built-in one seems to be unreliable on
devices running Fire OS 6 (maybe other versions too) and/or devices
using a VPN such as Tailscale.

See more details here:
https://github.com/damontecres/Wholphin/issues/1800#issuecomment-5432941761

### Related issues
Should fix #1156
Should fix #1751
Should fix #1800

### Testing
Unfortunately, I've never been able to reproduce the original issue on
any of my devices, but hopefully someone who is affected can try this
branch out!

This change doesn't break anything on some emulators (tried Android 7.0
& Android TV 14) or NVIDIA shield

## Screenshots
N/A

## AI or LLM usage
None

## Try it

See instructions here:
https://github.com/damontecres/Wholphin/releases/tag/develop-image-fix
2026-08-29 16:07:09 -04:00
Damontecres
fdde986029
Update favorites page with more types (#1864)
## Description
This PR adds many more data types as tabs to the favorites page.
Additionally, only tabs for the types the user has favorites for will
appear. For example, if you have no favorited music videos, that tab
will not show.

Added types:
- Collections
- TV Channels
- Music albums
- Music artists
- Music videos
- Songs
- Home videos
- Photos
- Photo albums

Dev note: The tabs are implemented using keys instead of indexes which
could be useful for the series overview season tabs. Plus it maybe even
be adapted for the episode list to eliminate the terrible index search
logic. A major difference is that the possible favorites tabs are
known/static list where seasons/episodes are queried & paginated.

### Related issues
Closes #1041
Closes #1844

### Testing
Manually on emulator, plus unit tests

## Screenshots
N/A

## AI or LLM usage
None
2026-08-26 21:50:13 -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
f91bca18ff
Add more searchable types & choose what to search (#1857)
## Description
This PR adds several more data types to search. It also adds a setting
(per use profile) to choose which types to include/exclude when
searching. The combined results now uses the same types instead of a
separate list.

A user can only search for types that they have access to. For example,
they need Live TV access for TV channels/programs or need access to a
movies-type library to search movies.

Added types to search:
- Photos & Photo albums
- Playlists
- People
- TV channels
- Live TV programs

Clicking a TV channel will play it. Clicking a TV program shows the
program dialog allowing for recording or watching live if it's currently
airing.

On the main search page you can also toggle whether to search Seerr or
not. This does not apply to the discover search tab.

Dev note: Some minor refactoring to move setting up/canceling live tv
recordings into a new service so it can be shared between with the live
tv & search pages. Also search related files are moved into the same
package now.

### Related issues
Closes #409
Closes #646 
Closes #1668
Closes #1750

### Testing
Emulator using users with different permissions and library access

## Screenshots
<img width="475" height="584" alt="image"
src="https://github.com/user-attachments/assets/ebcff697-07ba-4ddd-8c26-57f147cfa02a"
/>


## AI or LLM usage
None
2026-08-26 14:39:02 -04:00
Damontecres
2b9af1e8bc
Release v1.0.7
Some checks failed
Create release / publish (push) Has been cancelled
2026-08-21 18:04:09 -04:00
Damontecres
088c276129
Translation 2026-08-21 (#1887)
Updates translations as of 2026-08-21

Thank you to all of the contributors! There is a large update to Danish
and a complete translation for Croatian, so special thanks to the
contributors for these languages!
2026-08-21 17:39:41 -04:00
alexkahler
bdef33c973
Translated using Weblate (Danish)
Currently translated at 99.5% (644 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-08-21 19:04:21 +00:00
SimonHung
ec67d51936
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (647 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-08-21 19:04:21 +00:00
Majkic666
adf299e1ee
Translated using Weblate (Danish)
Currently translated at 85.4% (553 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-08-21 19:04:21 +00:00
alexkahler
f84ea0e758
Translated using Weblate (Danish)
Currently translated at 85.4% (553 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-08-21 19:04:21 +00:00
Majkic666
f00622be13
Translated using Weblate (Danish)
Currently translated at 85.4% (553 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-08-21 19:04:21 +00:00
Majkic666
18174b6e91
Translated using Weblate (Danish)
Currently translated at 85.4% (553 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-08-21 19:04:21 +00:00
Majkic666
e1a6fcc2f5
Translated using Weblate (Danish)
Currently translated at 85.4% (553 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-08-21 19:04:21 +00:00
alexkahler
6ab1f96aca
Translated using Weblate (Danish)
Currently translated at 85.4% (553 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-08-21 19:04:20 +00:00
MrGegaz
7cef2204ea
Translated using Weblate (Croatian)
Currently translated at 100.0% (647 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hr/
2026-08-21 19:04:20 +00:00
idezentas
28d38258b4
Translated using Weblate (Turkish)
Currently translated at 99.8% (646 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-08-21 19:04:20 +00:00
m_ber
97b8c1f2e6
Translated using Weblate (Czech)
Currently translated at 78.9% (511 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-08-21 19:04:20 +00:00
TheMatrixan
797170d758
Translated using Weblate (Polish)
Currently translated at 99.6% (645 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-08-21 19:04:20 +00:00
Jaeensson
41443dd85d
Translated using Weblate (Swedish)
Currently translated at 90.5% (586 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-08-21 19:04:20 +00:00
Outbreak2096
1978947b25
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (647 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-08-21 19:04:20 +00:00
rgabbo
f06dba281d
Translated using Weblate (Italian)
Currently translated at 99.5% (644 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-08-21 19:04:20 +00:00
n8llcaster
9acc8251e2
Translated using Weblate (Slovak)
Currently translated at 100.0% (647 of 647 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-08-21 19:04:20 +00:00
m4st3r-0day
df3d6e2a78
Translated using Weblate (Albanian)
Currently translated at 6.0% (39 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sq/
2026-08-21 19:04:20 +00:00
m4st3r-0day
78a3f3b929
Added translation using Weblate (Albanian) 2026-08-21 19:04:20 +00:00
MrGegaz
4882ff87e1
Translated using Weblate (Croatian)
Currently translated at 100.0% (644 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hr/
2026-08-21 19:04:20 +00:00
tal_sarid
ad2a7109f7
Translated using Weblate (Hebrew)
Currently translated at 100.0% (644 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-08-21 19:04:20 +00:00
American_Jesus
e48a8e089f
Translated using Weblate (Portuguese)
Currently translated at 100.0% (644 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-08-21 19:04:20 +00:00
MrGegaz
c26fa32aa9
Translated using Weblate (Croatian)
Currently translated at 91.7% (591 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hr/
2026-08-21 19:04:20 +00:00
MrGegaz
7d0dd26f42
Translated using Weblate (Croatian)
Currently translated at 81.8% (527 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hr/
2026-08-21 19:04:20 +00: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
39523aa6e2
Several minor bug fixes (#1866)
## Description
Another PR to gather small bug fixes:
- Fix aspect ratio for episodes on person page
- Use the song's artist instead of the album's artist on album page
- Fix how the dim percentage is applied to the OS screensaver dim
percentage (#1834)

### Related issues
Fixes #1851 
Fixes #1863

### Testing
<!-- Describe how this change was tested and on what device(s) -->

## Screenshots
<!-- Please include screenshots if the PR alters any UI elements -->

## AI or LLM usage
<!-- If you used any AI or LLM assistance, please list where in the code
and how you tested it -->
2026-08-20 17:09:39 -04:00
Damontecres
ed204ef7e9
Fix experimental AC3 setting being used by default (#1879)
## Description
There were two problems with the experimental "Use AC3 for surround
sound audio" setting (#947):
1. Defaulted to true
2. Access wasn't guarded by enabling experimental settings

This caused unintentional audio transcoding

### Related issues
Fixes #1878

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-08-20 17:08:31 -04:00
Damontecres
b064af7c4e
Update item fields queried & expand usage of primary aspect ratio (#1852)
## Description
Slight optimization to remove "child count" for grid pages. Adds back
"primary image aspect ratio" for several queries where its necessary.

Also updates extras cards to use the "primary image aspect ratio"
instead of a hard coded value.

Dev note: some light refactoring is also in this PR including renaming
the item fields lists to better reflect the intended usage in code

### Related issues
Fixes #1843

### Testing
Emulator, I tested many pages and views, but probably need to do "real
world" testing to see if I missed anything

## Screenshots
N/A

## AI or LLM usage
None
2026-08-17 22:54:15 -04:00
Damontecres
c4264a3263
Adds option to dim the screensaver (#1834)
## Description
Adds a new sceensaver setting to add dimming. A larger value is darker

This works with or without the in-app screensaver. If dimming is
enabled, the screen be will be dimmed after inactivity. If both dimming
and the in-app screensaver are enabled, the screensaver will show dimmed
after inactivity.

### Related issues
Closes #1179

### Testing
Emulator

## Screenshots
N/A, it's just darker

## AI or LLM usage
None
2026-08-17 22:53:40 -04:00
Stefan Bullones
197af604d6
Pause music playback when another app takes audio focus (#1842)
## Description
The audio player passed handleAudioFocus = false when setting its audio
attributes, so ExoPlayer never requested audio focus. Music kept playing
and mixed with other audio when a call arrived, another app started
playback, or voice search took focus.

### Related issues
Fixes https://github.com/damontecres/Wholphin/issues/1841

### Testing
1. Play a music track
2. Open YouTube and play a video

## AI or LLM usage
Cursor and Claude Code
2026-08-17 22:51:51 -04:00
Damontecres
f89d752d2c
Updates & fixes to discover pages (#1836)
## Description
Fixes several issues on the discover pages:
- Fixes sorting by release date for tv shows
- Fixes automatic go-to for movie & tv show grid
- Fixes tv show discover page's go-to using a movie detail page
- Updates the alignment & padding for the headers on different discover
pages to match the reset of the app
- Use media's logo image if available and the setting is enabled

### Related issues
N/A

### Testing
Emulator mostly

## Screenshots
N/A, headers and logos are unchanged, just moved slightly to match
regular content

## AI or LLM usage
None
2026-08-16 10:29:03 -04:00
Damontecres
4e53cbaa7f
Preload next image during slideshow (#1847)
## Description
When viewing images in a slideshow, this PR enqueues the next image so
it will likely be cached and ready to display when the slideshow
advances.

This works for both automatic and manual scrolling as well as advancing
next or previous images.

### Related issues
Closes #1835 

### Testing
Emulator with large photos

## Screenshots
N/A

## AI or LLM usage
None
2026-08-16 10:20:46 -04:00
Damontecres
22a949bc76
Fixes slideshow images not being full screen (#1849)
## Description
I accidentally removed the original fix/workaround from #1089 during a
merge in #1052, so this PR restores the fix.

### Related issues
Fixes #1736
Related to the original fix #1089

### Testing
Emulator with portrait and landscape images

## Screenshots
N/A

## AI or LLM usage
None
2026-08-16 10:20:33 -04:00
Damontecres
007ac4801c
Release v1.0.6
Some checks failed
Create release / publish (push) Has been cancelled
2026-08-13 18:36:38 -04:00
Damontecres
4b5d44ef1c
Translations 2026-08-13 (#1833)
Updates translations as of 2026-08-13

Thanks to all of the contributions!
2026-08-13 18:15:21 -04:00
MrGegaz
8ea73b6f3a
Translated using Weblate (Croatian)
Currently translated at 32.4% (209 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hr/
2026-08-13 17:57:57 +00:00
MrGegaz
40d01b54ef
Translated using Weblate (Croatian)
Currently translated at 17.7% (114 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hr/
2026-08-13 17:57:57 +00:00
arcker95
2033f1857a
Translated using Weblate (Italian)
Currently translated at 100.0% (644 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-08-13 17:57:57 +00:00
n8llcaster
894e0db459
Translated using Weblate (Slovak)
Currently translated at 100.0% (644 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-08-13 17:57:57 +00:00
Priit Jõerüüt
14a1f2e110
Translated using Weblate (Estonian)
Currently translated at 100.0% (644 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-08-13 17:57:57 +00:00
MrGegaz
84185530c9
Translated using Weblate (Croatian)
Currently translated at 15.2% (98 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hr/
2026-08-13 17:57:57 +00:00
MrGegaz
ea28a2d58f
Added translation using Weblate (Croatian) 2026-08-13 17:57:57 +00:00
North-DaCoder
d463dbf876
Translated using Weblate (German)
Currently translated at 100.0% (644 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-08-13 17:57:57 +00:00
Outbreak2096
0e70fe030a
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (644 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-08-13 17:57:57 +00:00
SimonHung
e2a5f30ed5
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (644 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-08-13 17:57:56 +00:00
danpergal84
3cbb738af8
Translated using Weblate (Spanish)
Currently translated at 100.0% (644 of 644 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-08-13 17:57:56 +00:00
addisonv
88fbae9867
Translated using Weblate (Portuguese (Brazil))
Currently translated at 87.8% (564 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt_BR/
2026-08-13 17:57:56 +00:00
lednurb
8144b674e2
Translated using Weblate (Dutch)
Currently translated at 93.3% (599 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-08-13 17:57:56 +00:00
Priit Jõerüüt
a0ee9ce45c
Translated using Weblate (Estonian)
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-08-13 17:57:56 +00:00
American_Jesus
0cb2d10455
Translated using Weblate (Portuguese)
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-08-13 17:57:56 +00:00
SimonHung
486cf527bd
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-08-13 17:57:56 +00:00
danpergal84
9def1b964b
Translated using Weblate (Spanish)
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-08-13 17:57:56 +00:00
SimonHung
e6f93cd809
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-08-13 17:57:56 +00:00
TheMatrixan
c10e410fd3
Translated using Weblate (Polish)
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-08-13 17:57:56 +00:00
tal_sarid
884735df60
Translated using Weblate (Hebrew)
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-08-13 17:57:56 +00:00
outopik
f7509767b0
Translated using Weblate (French)
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-08-13 17:57:56 +00:00
ymir
fc9d2287d7
Translated using Weblate (French)
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-08-13 17:57:56 +00:00
n8llcaster
b12b078293
Translated using Weblate (Slovak)
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-08-13 17:57:56 +00:00
nofkclue
fe943fef4e
Translated using Weblate (German)
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-08-13 17:57:56 +00:00
Damontecres
2110d846c1
Fix some bugs on discover pages (#1832)
## Description
Fix some bugs on discover pages:
- UI fixes for available seasons
- Fix toggle not updating when removing a season from a pending request
- Don't request images if the path is null
- Better error handling when fetching ratings

### Related issues
Follow up to #1816

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-08-13 13:57:51 -04:00
renovate[bot]
ba52915cf0
Update actions/setup-python action to v7 (#1822) 2026-08-13 13:40:22 -04:00
Damontecres
51ab39ed3b
Several minor bug fixes (#1811)
## Description
Fixes several small bugs and does some minor refactoring:
- Use "Recently Added in XYZ" instead of "Recently recorded" for
recorded tv libraries
- Improve home page query performance by using fewer fields in the query
- When an error occurs connecting to discovered servers, show the error
instead of only logging
- Fix headers sometimes appearing briefly on some pages when navigating
back
- Catch exceptions in socket connections
- Use a larger buffer when downloading updates
- Split up ADB logging for media info

### Related issues
Related to
https://github.com/damontecres/Wholphin/issues/1517#issuecomment-5184316086
Maybe the larger download buffer will help with
https://github.com/damontecres/Wholphin/issues/1696
Will help show the error in #1801
Prevents the crash in #1786

### Testing
Emulator & nvidia shield

## Screenshots
N/A

## AI or LLM usage
None
2026-08-13 12:36:20 -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
Damontecres
e6304f79a2
Fix requesting new seasons after approval (#1816)
## Description
Fixes requesting additional seasons of a show if a previous request has
already been approved

Now if the previous request is already approved/completed/etc, a new
request will be created instead of trying to update the previous one.
Pending requests will still be updated as before instead of requesting a
new one.

### Related issues
Fixes #1702

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-08-12 16:47:08 -04:00
Damontecres
548ec09af3
Fix issue with scrolling to the right episode if there are specials in the season (#1827)
## Description
Fixes an edge case where clicking on an episode to go to Series Overview
wouldn't scroll to the episode if there is a special (season 0) episodes
in the episode list.

Dev note: to simplify things, if the entire dataset is cached, then just
search with `indexOf`. Often the data is cached because 20 seasons or
100 episodes are fetched initially which covers many shows.

### Related issues
Follow up to #1708 

### Testing
Emulator and unit tests

## Screenshots
N/A

## AI or LLM usage
None
2026-08-12 16:25:30 -04:00
Damontecres
c6d81dada8
Fix playlist & collection random sorting on home page (#1820)
## Description
Ensure that a playlist or collection's items are random sorted on the
home page, if applicable.

Previously, the request did sort by random then by sort name resulting
in only sorting by sort name.

However, there are still issues with random sorting in general, see
#1819

### Related issues
Technically fixes #1806
Further bugs in #1819

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-08-12 14:04:19 -04:00
Nehereus
1c0e9a5853
Add a preference option to disable hardware audio offloading (#1564)
## Description
By default, Wholphin plays music with hardware audio offloading enabled.
This delegates audio decoding to the device's hardware DSP, making
playback stability highly dependent on the manufacturer's system audio
implementation. On certain hardware (such as my TCL TVs, e.g., the
Chinese Model Q9L Pro), this implementation causes music playback to
hang indefinitely at 0 seconds.

### Related issues
#1372

### Testing
Tested on a TCL TV (Q9L Pro):
- Some musics playback hangs at 0s with offloading enabled.
- Disabling audio offloading fixes the issues for all medias.

## Screenshots
<!-- Please include screenshots if the PR alters any UI elements -->
<img width="4284" height="5712" alt="IMG_9486"
src="https://github.com/user-attachments/assets/ddf95e68-6ebd-42f9-9565-568de4fbfc42"
/>

## AI or LLM usage
Developed in pair with Gemini and human-reviewed, tested.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-08-11 13:36:03 -04:00
Damontecres
de30c2ccac
Improvements to Now Playing page, including artist backdrops (#1782)
## Description
This PR has updates the Now Playing page.

Now only the song's backdrops and the song's artists' backdrops will
show. First, if the song has any backdrops (probably pretty uncommon),
they will be shuffled and shown, then if the song's artists have
backdrop, those are shuffled and shown. The backdrop changes every 2
minutes unless there's <15s left in the song to prevent rapid changes.
Note: album backdrops and album artists not directly linked to the
specific song are not included.

If a song has a lyrics and the user is scrolling through them, if the
user doesn't scroll for 5 seconds, the focus leaves the lyrics and they
scroll to the current line if available.

Dev note: Both backdrop & lyric updates are refactored to use `Flow`s to
trigger lookups

### Related issues
Fixes #1170

### Testing
Emulator, I need to test it longer term

## Screenshots
N/A

## AI or LLM usage
None
2026-08-11 13:35:20 -04:00
Damontecres
5219b22fc7
Update wholphin-extensions & media3 (#1814)
Updates playback dependencies:
- `wholphin-extensions` to `v0.2.2`
- `media3` to `1.11.0`
- `libass-android` to `0.5.1`
2026-08-11 13:35:11 -04:00
Damontecres
bd1a6c44ee
Add shuffle parameter to playback intent (#1813)
## Description
Adds an optional boolean parameter `shuffle` that is read for the
playback intent. When true, it will cause the resulting media to be
shuffled if applicable.

Something can be shuffled if the `itemId` is a grouping on media such as
a playlist, series, season, or even library.

### Related issues
Depends on #1756

### Testing
Emulator with the documented command

## Screenshots
N/A

## AI or LLM usage
None
2026-08-11 13:33:49 -04:00
renovate[bot]
e94e82479b
Update dependency org.openapi.generator to v7.24.0 (#1731) 2026-08-11 13:00:33 -04:00
renovate[bot]
5f44f870df
Update Kotlin to v2.4.10 (#1697) 2026-08-11 13:00:15 -04:00
renovate[bot]
2745e76b82
Update Github Actions (#1672) 2026-08-11 12:59:55 -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
Stefan Bullones
a0966dd05a
Fix alphabet highlight for names starting with "The " (#1788)
## Description
The alphabet scroll bar highlighted the letter of an item's *display*
name while the grid sorted by the server's article-stripped `SortName`,
so "The Beatles" sat under **B** but highlighted **T**.

- **Artists:** the Artists tab never requested `SortName`, so the
highlight silently fell back to `Name`. It now asks for `SlimItemFields`
(which already includes `SORT_NAME`), matching the sibling `/Items`
branch. The count-only query used for letter positions passes `fields =
null`, since a `limit = 0` request has no use for them.
- **Genres and studios:** these grids bucketed on the display name
unconditionally. The sort-name fallback is extracted as
`BaseItemDto.alphabetSortName` (`sortName ?: name ?: ""`) and carried
onto the `Genre` and `Studio` grid items, so all three grids now derive
the letter from one value.

### Related issues
https://github.com/damontecres/Wholphin/issues/1787

### Testing
Two unit tests added:
- `CollectionFolderViewModelTest` pins that the artist pager actually
requests `SORT_NAME` — the field whose absence caused the bug.
- `AlphabetSortNameTest` covers both branches of the `alphabetSortName`
fallback.

Manually: select a music library → Artists tab → put the cursor on an
artist starting with "The " and confirm the highlighted letter matches
the grid position. Same check on the genre and studio grids.

## AI or LLM usage
I used Cursor for debugging and implementing the fix, and Claude Code
for a follow-up cleanup pass over the diff.
2026-08-10 21:52:43 -04:00
Damontecres
454c0212b6
Fix issues with external subtitles & transcoding (#1790)
## Description
Fixes a bug where the external subtitle track would not be activated if
playback is transcoded

Also fixes a bug where external subtitles would cause MPV to incorrectly
switch to transcoding

### Related issues
Fixes #1778

### Testing
Emulator with files that have a variety of external SRT, embedded SRT &
ASS, & embedded PGS

## Screenshots
N/A

## AI or LLM usage
None
2026-08-10 20:17:42 -04:00
Damontecres
7581b189aa
Fix preference text overflow/clipping (#1785)
## Description
Removes the max height constraints for slider preferences and navigation
drawer preferences so that longer text (possibly in other languages or a
long library name) won't cut off any content

### Related issues
Fixes #1784 

### Testing
Emulator with manual long text

## Screenshots
N/A, no significant change except sliders can be slightly taller if
needed

## AI or LLM usage
None
2026-08-04 11:01:19 -04:00
Damontecres
fceb5f81a7
Release v1.0.5
Some checks failed
Create release / publish (push) Has been cancelled
2026-08-03 11:10:07 -04:00
Stefan Bullones
d7d63f52c3
Fix the alphabet buttons scroll bar on the Artists tab (#1775)
## Description
positionOfLetter counted through /Items no matter which endpoint the
grid was actually paging. A music library's Artists tab pages /Artists,
so the index it returned had counted every album and song before the
letter - an order of magnitude more rows than the artist grid holds -
and every letter jumped to the last row.

Count through the same endpoint createPager builds its pager from:
/Artists for the ARTIST override, /Items for NONE. The artist request is
now built once in createGetArtistsRequest and shared by both, so the two
cannot drift apart.

GetPersonsRequest has neither nameLessThan nor startIndex, so /Persons
cannot be counted up to a letter at all; the PERSON override returns
null, which the call sites already map to the "no jump" sentinel. That
path is unreachable today - the only PERSON tab sorts by
ItemSortBy.DEFAULT, so showLetterButtons is never true there and
AlphabetButtons is never composed.

### Related issues
https://github.com/damontecres/Wholphin/issues/1774

### Testing
Select a music library -> Go to Artist tab -> Select a letter on the
right side alphabet scroll bar

## AI or LLM usage
I used Cursor and Claude Code for planning, implementation and code
review.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-08-03 09:54:03 -04:00
Damontecres
6ad96865e7
Fix live tv & recorded tv playback (#1770)
## Description
#1654 broke playback for both live TV and recorded programs in `.ts`
format.

This PR fixes both issues! Jellyfin returns `NoCompatibleStream` for
live tv if the source ID is passed in, so that is now skipped for live
tv. `.ts` files may have IDs formatted with a slash instead of colon, so
the logic is updated to handle that plus fallbacks for potentially
unknown future formats.

### Related issues
Fixes #1767

### Testing
Emulator & nvidia shield, plus unit tests

## Screenshots
N/A

## AI or LLM usage
None
2026-08-02 11:11:32 -04:00
Damontecres
7c6ab39436
Fix translations again (#1776)
## Description
Another follow up to https://github.com/damontecres/Wholphin/pull/1764 &
#1772

Also adds `lint.xml` to mark the extra translations as just a warning
since they don't cause a problem in the final build.

### Related issues
#1764
#1772

### Testing
N/A

## Screenshots
N/A

## AI or LLM usage
None
2026-08-02 10:31:56 -04:00
Damontecres
1874b5babe
Merge remote-tracking branch 'origin/main' into github-main 2026-07-30 20:48:57 -04:00
Damontecres
89933c83f9
Fix incorrect translation merge (#1772)
Fixes the squash merge from #1764
2026-07-30 20:32:50 -04:00
Damontecres
c1b055db3a
Remove outdated dpad_seek_mode_skip_time entries 2026-07-30 20:14:13 -04:00
arcker95
145f716da4
Translated using Weblate (Italian)
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-07-30 20:10:01 -04:00
Outbreak2096
836706fffa
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-30 20:10:01 -04:00
opakholis
2ac7a3d83f
Translated using Weblate (Indonesian)
Currently translated at 97.5% (626 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-07-30 20:10:01 -04:00
n8llcaster
e24ef7783e
Translated using Weblate (Slovak)
Currently translated at 98.9% (634 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-30 20:10:01 -04:00
idezentas
45f3cc7665
Translated using Weblate (Turkish)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-30 20:10:01 -04:00
Outbreak2096
99c5ce2fdf
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-30 20:10:01 -04:00
TheMatrixan
ab400fd7fb
Translated using Weblate (Polish)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-07-30 20:10:01 -04:00
tal_sarid
708ba9dfab
Translated using Weblate (Hebrew)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-07-30 20:10:01 -04:00
ymir
712fbcc9b8
Translated using Weblate (French)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-07-30 20:10:00 -04:00
American_Jesus
e3afc3c583
Translated using Weblate (Portuguese)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-07-30 20:10:00 -04:00
arcker95
d73cf5e9e8
Translated using Weblate (Italian)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-07-30 20:10:00 -04:00
SimonHung
46347abbc7
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 99.8% (640 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-30 20:10:00 -04:00
Priit Jõerüüt
45f05fbc69
Translated using Weblate (Estonian)
Currently translated at 98.7% (633 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-07-30 20:10:00 -04:00
danpergal84
24792279cc
Translated using Weblate (Spanish)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-07-30 20:10:00 -04:00
idezentas
fe39318e04
Translated using Weblate (Turkish)
Currently translated at 100.0% (639 of 639 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-30 20:10:00 -04:00
danpergal84
ea13c5c5ad
Translated using Weblate (Spanish)
Currently translated at 100.0% (639 of 639 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-07-30 20:10:00 -04:00
Prahlis
50e306baa1
Translated using Weblate (Swedish)
Currently translated at 87.8% (559 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-07-30 20:10:00 -04:00
Prahlis
0b94cdb509
Translated using Weblate (Swedish)
Currently translated at 87.7% (558 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-07-30 20:10:00 -04:00
North-DaCoder
3830a9eb01
Translated using Weblate (German)
Currently translated at 100.0% (636 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-07-30 20:10:00 -04:00
Codeberg Translate
a26d0932b5
Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/
2026-07-30 20:10:00 -04:00
idezentas
291ac6c50c
Translated using Weblate (Turkish)
Currently translated at 100.0% (636 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-30 20:10:00 -04:00
Outbreak2096
22b04ff855
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (636 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-30 20:09:59 -04:00
n8llcaster
ec97a52ffc
Translated using Weblate (Slovak)
Currently translated at 100.0% (636 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-30 20:09:59 -04:00
Prahlis
05560697a3
Translated using Weblate (Swedish)
Currently translated at 87.4% (556 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-07-30 20:09:59 -04:00
danpergal84
10a7008ad2
Translated using Weblate (Spanish)
Currently translated at 100.0% (636 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-07-30 20:09:59 -04:00
Prahlis
c4ac8b6a65
Translated using Weblate (Swedish)
Currently translated at 85.7% (543 of 633 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-07-30 20:09:59 -04:00
TheMatrixan
1c3e269718
Translated using Weblate (Polish)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-07-30 20:09:59 -04:00
ymir
9988974001
Translated using Weblate (French)
Currently translated at 99.8% (624 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-07-30 20:09:59 -04:00
opakholis
43e86e4769
Translated using Weblate (Indonesian)
Currently translated at 99.8% (624 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-07-30 20:09:59 -04:00
nofkclue
c393bc9697
Translated using Weblate (German)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-07-30 20:09:59 -04:00
arcker95
a6887c0826
Translated using Weblate (Italian)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-07-30 20:09:59 -04:00
outopik
a335dbcbc5
Translated using Weblate (French)
Currently translated at 98.5% (616 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-07-30 20:09:59 -04:00
SimonHung
4d7ac8f759
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-30 20:09:59 -04:00
unikitty
327a49f1d7
Translated using Weblate (English (United Kingdom))
Currently translated at 6.7% (42 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/en_GB/
2026-07-30 20:09:59 -04:00
damontecres
e7efe473c8
Added translation using Weblate (English (United Kingdom)) 2026-07-30 20:09:59 -04:00
idezentas
aa5c65b4a2
Translated using Weblate (Turkish)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-30 20:09:58 -04:00
Outbreak2096
857d1505ed
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-30 20:09:58 -04:00
n8llcaster
2807c92a35
Translated using Weblate (Slovak)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-30 20:09:58 -04:00
Priit Jõerüüt
00d37eb726
Translated using Weblate (Estonian)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-07-30 20:09:58 -04:00
American_Jesus
4eba026541
Translated using Weblate (Portuguese)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-07-30 20:09:58 -04:00
danpergal84
fbe0458dad
Translated using Weblate (Spanish)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-07-30 20:09:58 -04:00
ymir
a959049c77
Translated using Weblate (French)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-07-30 20:09:58 -04:00
opakholis
f80bfdc886
Translated using Weblate (Indonesian)
Currently translated at 99.8% (611 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-07-30 20:09:58 -04:00
viretius
28cf4c6443
Translated using Weblate (German)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-07-30 20:09:58 -04:00
idezentas
5eddaacf4e
Translated using Weblate (Turkish)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-30 20:09:58 -04:00
streamyfinchris
28b909ed79
Translated using Weblate (Swedish)
Currently translated at 86.6% (530 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-07-30 20:09:58 -04:00
tal_sarid
912551e74b
Translated using Weblate (Hebrew)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-07-30 20:09:58 -04:00
danpergal84
964b3b5068
Translated using Weblate (Spanish)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-07-30 20:09:58 -04:00
North-DaCoder
e539551e70
Translated using Weblate (German)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-07-30 20:09:58 -04:00
Outbreak2096
d61cba4101
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-30 20:09:57 -04:00
TheMatrixan
ad241ca7e8
Translated using Weblate (Polish)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-07-30 20:09:57 -04:00
arcker95
0d2524b359
Translated using Weblate (Italian)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-07-30 20:09:57 -04:00
SimonHung
76b5aa12ab
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-30 20:09:57 -04:00
n8llcaster
ca27e94d33
Translated using Weblate (Slovak)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-30 20:09:57 -04:00
Priit Jõerüüt
8ac955c44d
Translated using Weblate (Estonian)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-07-30 20:09:57 -04:00
Outbreak2096
7e837b4d45
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (610 of 610 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-30 20:09:57 -04:00
idezentas
12640a9fd1
Translated using Weblate (Turkish)
Currently translated at 100.0% (610 of 610 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-30 20:09:57 -04:00
TheMatrixan
48455bc13f
Translated using Weblate (Polish)
Currently translated at 100.0% (610 of 610 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-07-30 20:09:57 -04:00
SimonHung
b86e454db6
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (610 of 610 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-30 20:09:57 -04:00
n8llcaster
c03abe4511
Translated using Weblate (Slovak)
Currently translated at 100.0% (610 of 610 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-30 20:09:57 -04:00
Priit Jõerüüt
8747dd30b7
Translated using Weblate (Estonian)
Currently translated at 100.0% (610 of 610 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-07-30 20:09:57 -04:00
Damontecres
7cc7ecb61e
Release v1.0.4
Some checks failed
Create release / publish (push) Has been cancelled
2026-07-29 16:58:19 -04:00
arcker95
b98dab8945
Translated using Weblate (Italian)
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-07-29 20:57:55 +00:00
Damontecres
ad7b53ec15
Fix ExtraTranslation vital lint errors 2026-07-29 16:57:38 -04:00
Damontecres
a1082bd732
Release v1.0.4 2026-07-29 16:26:38 -04:00
Damontecres
7074123bc1
Translations 2026-07-29 (#1764)
Update translations as of 2026-07-29

Thank you to all of the contributors!

---------

Co-authored-by: Priit Jõerüüt <jrtcdbrg@noreply.codeberg.org>
Co-authored-by: n8llcaster <n8llcaster@noreply.codeberg.org>
Co-authored-by: SimonHung <simonhung@noreply.codeberg.org>
Co-authored-by: TheMatrixan <thematrixan@noreply.codeberg.org>
Co-authored-by: butterflyoffire <butterflyoffire@noreply.codeberg.org>
Co-authored-by: idezentas <idezentas@noreply.codeberg.org>
Co-authored-by: Outbreak2096 <outbreak2096@noreply.codeberg.org>
Co-authored-by: arcker95 <arcker95@noreply.codeberg.org>
Co-authored-by: North-DaCoder <north-dacoder@noreply.codeberg.org>
Co-authored-by: danpergal84 <danpergal84@noreply.codeberg.org>
Co-authored-by: tal_sarid <tal_sarid@noreply.codeberg.org>
Co-authored-by: streamyfinchris <streamyfinchris@noreply.codeberg.org>
Co-authored-by: viretius <viretius@noreply.codeberg.org>
Co-authored-by: opakholis <opakholis@noreply.codeberg.org>
Co-authored-by: ymir <ymir@noreply.codeberg.org>
Co-authored-by: American_Jesus <american_jesus@noreply.codeberg.org>
Co-authored-by: damontecres <damontecres@noreply.codeberg.org>
Co-authored-by: unikitty <unikitty@noreply.codeberg.org>
Co-authored-by: outopik <outopik@noreply.codeberg.org>
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: nofkclue <nofkclue@noreply.codeberg.org>
Co-authored-by: Prahlis <prahlis@noreply.codeberg.org>
2026-07-29 16:25:24 -04:00
Outbreak2096
fbeb192ad7
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (642 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-29 14:21:55 +00:00
opakholis
d0d996fda7
Translated using Weblate (Indonesian)
Currently translated at 97.5% (626 of 642 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-07-29 14:21:55 +00:00
n8llcaster
42155bdab0
Translated using Weblate (Slovak)
Currently translated at 98.9% (634 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-29 14:21:55 +00:00
idezentas
05f8456177
Translated using Weblate (Turkish)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-29 14:21:55 +00:00
Outbreak2096
4f05b6ffab
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-29 14:21:55 +00:00
TheMatrixan
477e4c921d
Translated using Weblate (Polish)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-07-29 14:21:55 +00:00
tal_sarid
4ed689064e
Translated using Weblate (Hebrew)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-07-29 14:21:55 +00:00
ymir
dcb304641d
Translated using Weblate (French)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-07-29 14:21:55 +00:00
American_Jesus
d969e73bd0
Translated using Weblate (Portuguese)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-07-29 14:21:55 +00:00
arcker95
4633209fb3
Translated using Weblate (Italian)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-07-29 14:21:55 +00:00
SimonHung
a1b5182e6a
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 99.8% (640 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-29 14:21:55 +00:00
Priit Jõerüüt
e41d0032f5
Translated using Weblate (Estonian)
Currently translated at 98.7% (633 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-07-29 14:21:55 +00:00
danpergal84
36369afb70
Translated using Weblate (Spanish)
Currently translated at 100.0% (641 of 641 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-07-29 14:21:55 +00:00
idezentas
4dde8b80cc
Translated using Weblate (Turkish)
Currently translated at 100.0% (639 of 639 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-29 14:21:55 +00:00
danpergal84
50f2afc5c4
Translated using Weblate (Spanish)
Currently translated at 100.0% (639 of 639 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-07-29 14:21:55 +00:00
Prahlis
9a6749a0d3
Translated using Weblate (Swedish)
Currently translated at 87.8% (559 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-07-29 14:21:55 +00:00
Prahlis
5820398b4a
Translated using Weblate (Swedish)
Currently translated at 87.7% (558 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-07-29 14:21:55 +00:00
North-DaCoder
6c8be55c29
Translated using Weblate (German)
Currently translated at 100.0% (636 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-07-29 14:21:55 +00:00
Codeberg Translate
25bbc7c418
Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/
2026-07-29 14:21:55 +00:00
idezentas
eedca7331a
Translated using Weblate (Turkish)
Currently translated at 100.0% (636 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-29 14:21:55 +00:00
Outbreak2096
09e835a374
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (636 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-29 14:21:55 +00:00
n8llcaster
e7a54c8012
Translated using Weblate (Slovak)
Currently translated at 100.0% (636 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-29 14:21:55 +00:00
Prahlis
0964322ab9
Translated using Weblate (Swedish)
Currently translated at 87.4% (556 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-07-29 14:21:54 +00:00
danpergal84
a4e62fb532
Translated using Weblate (Spanish)
Currently translated at 100.0% (636 of 636 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-07-29 14:21:54 +00:00
Prahlis
b3cc7c424d
Translated using Weblate (Swedish)
Currently translated at 85.7% (543 of 633 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-07-29 14:21:54 +00:00
TheMatrixan
6827201bdc
Translated using Weblate (Polish)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-07-29 14:21:54 +00:00
n8llcaster
c198f5c2b6
Translated using Weblate (Slovak)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-29 14:21:54 +00:00
idezentas
043dfb61bd
Translated using Weblate (Turkish)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-29 14:21:54 +00:00
nofkclue
c7a5ec61a3
Translated using Weblate (German)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-07-29 14:21:54 +00:00
ymir
b96d50d166
Translated using Weblate (French)
Currently translated at 99.8% (624 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-07-29 14:21:54 +00:00
opakholis
08444c2509
Translated using Weblate (Indonesian)
Currently translated at 99.8% (624 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-07-29 14:21:54 +00:00
nofkclue
aa83ee1382
Translated using Weblate (German)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-07-29 14:21:54 +00:00
Outbreak2096
320c9acc8a
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-29 14:21:54 +00:00
arcker95
63893894d0
Translated using Weblate (Italian)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-07-29 14:21:54 +00:00
SimonHung
8abbbabcd5
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-29 14:21:54 +00:00
Codeberg Translate
f7348a6c7a
Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/
2026-07-29 14:21:54 +00:00
outopik
75f1f06345
Translated using Weblate (French)
Currently translated at 98.5% (616 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-07-29 14:21:54 +00:00
n8llcaster
7ba5c83f33
Translated using Weblate (Slovak)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-29 14:21:54 +00:00
SimonHung
ab993b0d96
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-29 14:21:54 +00:00
unikitty
920af648be
Translated using Weblate (English (United Kingdom))
Currently translated at 6.7% (42 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/en_GB/
2026-07-29 14:21:54 +00:00
damontecres
07592b15b4
Added translation using Weblate (English (United Kingdom)) 2026-07-29 14:21:54 +00:00
idezentas
d301cde016
Translated using Weblate (Turkish)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-29 14:21:54 +00:00
Outbreak2096
527c9e3705
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-29 14:21:54 +00:00
n8llcaster
f0ab44be88
Translated using Weblate (Slovak)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-29 14:21:54 +00:00
Priit Jõerüüt
a71b40feab
Translated using Weblate (Estonian)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-07-29 14:21:54 +00:00
American_Jesus
49c14aaa6c
Translated using Weblate (Portuguese)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-07-29 14:21:53 +00:00
danpergal84
c906431b0d
Translated using Weblate (Spanish)
Currently translated at 100.0% (625 of 625 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-07-29 14:21:53 +00:00
ymir
0cacb4e4ed
Translated using Weblate (French)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-07-29 14:21:53 +00:00
opakholis
92a17f2b84
Translated using Weblate (Indonesian)
Currently translated at 99.8% (611 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-07-29 14:21:53 +00:00
viretius
15592f87aa
Translated using Weblate (German)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-07-29 14:21:53 +00:00
idezentas
ea96feca74
Translated using Weblate (Turkish)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-29 14:21:53 +00:00
SimonHung
7c22ce7ce7
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-29 14:21:53 +00:00
streamyfinchris
171371fb54
Translated using Weblate (Swedish)
Currently translated at 86.6% (530 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-07-29 14:21:53 +00:00
tal_sarid
7a44d2c553
Translated using Weblate (Hebrew)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-07-29 14:21:53 +00:00
danpergal84
96dceefcd8
Translated using Weblate (Spanish)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-07-29 14:21:53 +00:00
North-DaCoder
dc1e74fc84
Translated using Weblate (German)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-07-29 14:21:53 +00:00
Outbreak2096
78291b6dd9
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-29 14:21:53 +00:00
TheMatrixan
b841f5d021
Translated using Weblate (Polish)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-07-29 14:21:53 +00:00
arcker95
4c557f3ced
Translated using Weblate (Italian)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-07-29 14:21:53 +00:00
SimonHung
15f8398610
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-29 14:21:53 +00:00
n8llcaster
4739ab73eb
Translated using Weblate (Slovak)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-29 14:21:53 +00:00
Priit Jõerüüt
7da1b0b510
Translated using Weblate (Estonian)
Currently translated at 100.0% (612 of 612 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-07-29 14:21:53 +00:00
Outbreak2096
1f214ebf87
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (610 of 610 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-29 14:21:53 +00:00
idezentas
1459ac7b77
Translated using Weblate (Turkish)
Currently translated at 100.0% (610 of 610 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-29 14:21:53 +00:00
butterflyoffire
35a0804e73
Translated using Weblate (Kabyle)
Currently translated at 9.6% (59 of 610 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/kab/
2026-07-29 14:21:53 +00:00
TheMatrixan
781efcbb8d
Translated using Weblate (Polish)
Currently translated at 100.0% (610 of 610 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-07-29 14:21:53 +00:00
SimonHung
613032ebfe
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (610 of 610 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-29 14:21:53 +00:00
n8llcaster
2de2746f77
Translated using Weblate (Slovak)
Currently translated at 100.0% (610 of 610 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-29 14:21:53 +00:00
Priit Jõerüüt
124b587285
Translated using Weblate (Estonian)
Currently translated at 100.0% (610 of 610 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-07-29 14:21:53 +00: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
2463841663
Update libass-android to v0.5.0 (#1763)
## Description
Updates `libass-android` to `v0.5.0`. There aren't any changes from
`v0.5.0-beta01`, so this is just a formal release.

### Related issues
Fixes #1554
Fixes #1561
Fixes #1678
Pr for the beta release: #1752

### Testing
N/A, I tested the beta

## Screenshots
N/A

## AI or LLM usage
None
2026-07-29 10:21:29 -04:00
Stefan Bullones
438c9dd704
Selecting a song on the search page navigates to a song's album with the song focused (#1661)
## Description
Selecting a song from search now resolves to its album's detail page,
scrolled to and focusing that song, instead of sending you to an
"unsupported item type" page. Implemented in the shared
BaseItem.destination() rather than in search specifically, so any caller
that navigates to a song gets the same behavior.

A song with no albumId plays directly while remaining in the search
results page: NavigationManager.navigateTo() intercepts a bare AUDIO
destination and hands it to a new MusicService.playSong(). Handling this
in the single navigation funnel means every caller gets it for free with
no loading-screen flash.

Adds BaseItemDestinationTest covering the new destination() logic.

### Related issues
https://github.com/damontecres/Wholphin/issues/1656

### Testing
Device: Sony Bravia 4K VH21

#### Tests on a search results page, song with albumId:
1. Select a song sends you to album page with cursor on song. Another
press plays it
2. Back button brings you back to search results.

Tested with both songs at the start of an album and songs deep into a
100+ songs album.

#### Tests on a search results page, song with no albumId:
1. Select a songs plays the song while staying in the search results
page. No loading screen.

## AI or LLM usage
I used Cursor and Claude Code for planning, implementation and code
review.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-07-28 18:45:02 -04:00
Damontecres
79cb93db08
Fixes to artist detail page (#1762)
## Description
Fixes an incorrect query for an artist's albums (thanks @StefanBS for
reporting & fixing in #1761!)

Additionally, another album row for "Appears on" is added. This is
definitely necessary because Wholphin only has a tab for all artists
which includes artists featured on albums attributed to a different
artist. So those albums will now show in the "Appears on" similar to the
web UI instead of an empty artist page.

Finally, the long-click context menu is added for music videos on an
artist's page.

### Related issues
Fixes #1760
Replaced #1761 

### Testing
Emulator, particularly with artists who are not album artists

Also, verified the query fix against the web UI dev console

## Screenshots
N/A

## AI or LLM usage
None, but the fix included from #1761 did use some AI

---------

Co-authored-by: Stefan Bullones <stefan.bullones@gmail.com>
2026-07-28 15:17:57 -04:00
Steve Smith
bf43a41405
Correct sort order when "Date Played" is selected in TV library. (#1746)
## Description
The TV library page offers the option to sort by date played, however
when selected it reverts to the default (alphabetical). This appears to
be due to the use of `ItemSortBy.DATE_PLAYED` rather than the correct
`ItemSortBy.SERIES_DATE_PLAYED`. See
[BrowseGridFragment](https://github.com/jellyfin/jellyfin-androidtv/blob/master/app/src/main/java/org/jellyfin/androidtv/ui/browsing/BrowseGridFragment.java#L169)
for the usage in the official Android TV client.

### Related issues
N/A

### Testing
Emulator against personal Jellyfin server.

## Screenshots
N/A

## AI or LLM usage
None
2026-07-27 12:43:47 -04:00
Damontecres
f16a7bb6d6
Fix trying to fetch images with zero fillWidth (#1753)
## Description
The bug report was caused by a race condition where the image URL was
built before the grid was fully laid out causing a `fillWidth=0` and the
`remember` would not reset after the `fillWidth` was updated to the
actual value. So this PR fixes the `remember`.

Furthermore, some extra guards are added to only pass the `fillWidth` &
`fillHeight` params if greater than 0.

Finally, the `EpisodeCard` code is deleted because it's height & width
logic was overly complicated and `BannerCardWithTitle` provides better
functionality anyway.

### Related issues
Should fix #1751
Maybe #1156 is also fixed by this

### Testing
Emulator, but I haven't been able to reproduce the race condition, but
the fix makes sense given the bug report

## Screenshots
N/A

## AI or LLM usage
None
2026-07-26 16:36:49 -04:00
Damontecres
ad1e5ec205
Update libass-android to 0.5.0-beta01 (#1752)
## Description
Updates `libass-android` to `0.5.0-beta01`. While this is a beta
release, from my testing it is very stable. It also fixes several
issues, including crashes, as well. So I want to merge into the develop
branch, but it can always been reverted if necessary.

### Related issues
Should fix these issues:
- #1554
- #1561
- #1678

### Testing
On emulator & nvidia shield

## Screenshots
N/A

## AI or LLM usage
None
2026-07-26 16:16:15 -04:00
Damontecres
9954365c6f
Add two new themes: Red & Brown (#1721)
## Description
Adds new two new theme colors: Red & Brown

### Related issues
Related to #91, #950
Closes #1646

### Testing
<!-- Describe how this change was tested and on what device(s) -->

## Screenshots
<img width="2727" height="2171" alt="image"
src="https://github.com/user-attachments/assets/ae19f98d-e782-466a-bafc-6c0502f64e00"
/>

## AI or LLM usage
None
2026-07-25 22:52:09 -04:00
Damontecres
e7b0be2660
Display server error message on server list (#1741)
## Description
On the switch server page, display error message at the bottom for the
focused server. This includes unsupported server version.

### Related issues
Jellyfin 10.11/12.0 support discussion: #1640

### Testing
Emulator

## Screenshots
<img width="1280" height="720" alt="server_error Large"
src="https://github.com/user-attachments/assets/1227e7e7-a364-48d9-9a77-cb3f78d134a1"
/>

## AI or LLM usage
None
2026-07-25 22:52:02 -04:00
Damontecres
4171d0b861
Minor UI changes to playlist details page (#1749)
## Description
Makes some minor changes to the playlist details page
- Center playlist name
- Show duration with seconds for songs
- Adjust some padding

Also fixes an issue where focus could be lost when leaving the page
while focused on a playlist item

Dev note: bulk of the change in thie PR is refactoring the `LazyColumn`
of playlist items into its own composable

### Related issues
N/A

### Testing
Emulator

## Screenshots
N/A, the changes are very minor

## AI or LLM usage
None
2026-07-25 16:13:31 -04:00
Damontecres
26a23d673b
Add option for dpad skipping with no seek bar (#1742)
## Description
Adds a third option for d-pad seeking to not show any seek bar which
only shows the skip indicator and not the seek bar along the bottom

### Related issues
Original work to add other mode in #1650
Closes #1520

### Testing
Emulator testing

## Screenshots
<img width="546" height="347" alt="image"
src="https://github.com/user-attachments/assets/2872566e-4598-4284-92cb-95a45b307835"
/>

## AI or LLM usage
None
2026-07-25 16:10:00 -04:00
calebvetter
cf3c00a1e1
Add option for trickplay preview mode when D-Pad seeking (#1650)
## Description
Adds a setting to allow the user to choose between the current D-Pad
skip functionality, and a new simple trickplay overlay.

The setting is added in Advanced, under the Playback section, called
"D-Pad seek mode". The options are "Skip time" and "Trickplay previews".
I left the default as "Skip time" since that's how it operates now, and
nothing would change for users after this option is added unless they go
and set it.

When "Trickplay previews" is enabled, it shows an simple overlay when
using the D-Pad to seek (without the full overlay open). The new simple
overlay is the same implementation of the components in the full
overlay: seekbar, trickplay images, current playhead time, remaining
time. The components were pulled from the full overlay's inline UI code
into their own reusable components, now used in both the full overlay
and the simple overlay.

#### Notes
I could see there being an adjustment for the overlay hide delay. It's
currently hard coded to 1.5s.

This could be the default method, but it seems like preference is split
as to whether people want this, or the minimalistic skipped seconds only
version.

### Related issues
This is directly in response to this issue, as well as my own desire for
the feature :)
https://github.com/damontecres/Wholphin/discussions/1084

It's also loosely related to this issue, in that it would solve it since
it shows the playhead and remaining times.
https://github.com/damontecres/Wholphin/discussions/1122

### Testing
Built and tested on an Amazon Fire TV Stick 4K Max. I wouldn't
anticipate and compatibility or performance changes since it's a simple
settings/UI change involving already-used UI components.

## Screenshots
<img width="1920" height="1080" alt="wholphin-20260630-155708"
src="https://github.com/user-attachments/assets/52046cc2-7413-4f66-89dd-355464fa0f91"
/>
<img width="1920" height="1080" alt="wholphin-20260630-155723"
src="https://github.com/user-attachments/assets/57a4a271-9e44-4d2a-bea3-ce5c9cdc731a"
/>
<img width="1920" height="1080" alt="wholphin-20260630-153825"
src="https://github.com/user-attachments/assets/2cf664ff-3db5-4a2e-8626-10219946669c"
/>


## AI or LLM usage
AI was used to generate the change, automate some testing, and provide
information surrounding the change. I manually did some refactoring and
cleanup to keep it clean and consistent with the rest of the code
conventions.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-07-23 15:32:40 -04:00
Damontecres
d2bf5cd1e2
Fix library freeze if first item's title starts with a character not in the alphabet string (#1740)
## Description
A library's grid page tries to highlight the first letter of the focused
item. But if the first letter was a not in the alphabet string (which
varies by language), a exception was thrown on the main thread causing a
freeze.

This PR ensures only a non-negative index is highlighted on the letter
buttons.

### Related issues
Fixes #1361
Need to create an issue to improve alphabet letter button handling for
non-English alphabets

### Testing
Emulator, I manually passed a non-`[0-9A-Z]` character to replicate the
freeze

## Screenshots
N/A

## AI or LLM usage
None
2026-07-23 15:32:31 -04:00
Damontecres
5bc874a9ff
Fix music artist listing & show music in collections (#1739)
## Description
Switches to use the Artists API instead of Items API when fetching the
list of artists in a library.

Enables fetching artists, albums, and music videos in a collection.

Also adds titles for the remaining `BaseItemKind` types for future
proofing even if they aren't used anywhere in the app.

### Related issues
Fixes #1733
Fixes #1734
Fixes #1735


### Testing
Emulator, created a new collection with the above. Also verified the
artist query against the web UI

## Screenshots
N/A

## AI or LLM usage
None
2026-07-23 14:23:48 -04:00
Damontecres
00490b14ec
Improvements to subtitle search & download (#1728)
## Description
Changes to searching & downloading subtitles:
- Activating newly downloaded subtitles more reliably
- Better audio index adjustment checks after downloading subtitles
- Sort subtitle downloadable options by whether the file hash matches
first
- Update the UI for subtitle downloadable options

Also, some improvements to the subtitle choice dialog:
- It now loops from top to bottom (and vice versa)
- It now focuses initially on current choice

### Dev notes
This PR now the selected tracks from `state.currentItemPlayback` to
`state.currentPlayback`. `currentItemPlayback` now only reflects the
user's choices saved in the database instead of a combination of DB and
"actual" state. `currentPlayback` stores the actual, resolved track
indexes regardless of how they were derived.

This means the indexes values in `state.currentItemPlayback` and
`state.currentPlayback` may be different. For example, if the user has
chosen "Only forced", `state.currentItemPlayback==-3` but
`state.currentPlayback` might be an actual forced track index.

### Related issues
Fixes #1549
Fixes #1718
Closes #1723
Fixes bug reported in
https://github.com/damontecres/Wholphin/issues/1498#issuecomment-5019965271

### Testing
Emulator

## Screenshots

Now shows SDH & hash matching

<img width="779" height="280" alt="image"
src="https://github.com/user-attachments/assets/4c33e5af-c9a1-4c9c-8217-6bc2ab3fcd27"
/>

## AI or LLM usage
None
2026-07-23 13:30:43 -04:00
Damontecres
48005ce67a
Updates to chapter card UI (#1729)
## Description
Changes the chapter cards to use the chapter's image's aspect ratio
instead of forcing the video's aspect ratio. These ratios can be
different sometimes often for "dummy" chapters.

Also, the font size for the chapter name & position is reduced a bit.

### Related issues
N/A

### Testing
Emulator

## Screenshots
<img width="873" height="193" alt="image"
src="https://github.com/user-attachments/assets/e1c0e8a8-2e37-4644-bc99-899e20c4af09"
/>

## AI or LLM usage
None
2026-07-21 17:21:07 -04:00
Damontecres
0a4ab7a547
Several UI fixes (#1730)
## Description
- Fixes not scrolling to the last season on series overview if there is
a Specials season
- Apply the "bouncing" fix from #1543 to all applicable card types
- Fix card images not automatically reloading when changing image type
on grid pages
- Get rid of the sticky headers which overlap items on view options
dialogs and use a fixed header as elsewhere in the app

### Related issues
Related to #1708 which didn't introduce the bug, but made it much more
likely to occur

### Testing
Emulator & unit tests

## Screenshots
N/A, the sticky header UI changes are very minor

## AI or LLM usage
None
2026-07-21 17:20:59 -04:00
Damontecres
2aae7eed38
Add search intent-filter for #1688 2026-07-20 10:39:42 -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
4f5cd3e7b1
Fix errors when a plural translation is missing (#1716)
## Description
Previously not every plural translation had full coverage and some did
not have the fallback "other" quantity translation. So for some
languages this would cause an error when trying to display the string
depending on the quantity. This PR ensures every plural translation has
the "other" fallback.

Note: the "other" fallback is copied from an existing translation so I'm
sure some will not be grammatically correct!

Dev note: I used a python script to parse and fix the entries, so it
also rewrote some of the unrelated `CDATA` entries.

### Related issues
Fixes #1676

### Testing
On the emulator, I reproduced the error in the issue with the Danish
translation, verified it was fixed after the patch was applied

## Screenshots
N/A

## AI or LLM usage
None
2026-07-18 17:52:16 -04:00
Damontecres
35c4d13f8f
Various minor bug fixes (#1707)
## Description
Gathering several small bug fixes:
- Clear the backdrop when manually reloading the home page
- Convert the "# years old" string resource to plural
- Fix a spelling error in the source English strings
- Ensure #1680 is respected when searching for subtitles to download
- Slightly adjust padding for the header in collection details to match
other headers

### Related issues
Fixes string issues reported in
https://github.com/damontecres/Wholphin/issues/1705#issuecomment-4982909931

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-07-18 17:50:35 -04:00
Damontecres
3cc2bf2dd1
Fix resume query for Jellyfin 12 (#1715)
## Description
Fixes querying for resume item (continue watching) for Jellyfin 12 so
that whole series are not included. The patch is also compatible with
Jellyfin 10.11.

### Related issues
Fixes #1689

I couldn't find an issue in Jellyfin that mentions this behavior change,
so I'm guessing it was a side effect. But since the old query did ask to
include Series, I don't think it's a bug in the server.

### Testing
Manual API queries & emulator on both Jellyfin 11 & 12-rc2 servers

## Screenshots
N/A

## AI or LLM usage
None
2026-07-17 11:23:31 -04:00
Damontecres
ed97fc3009
Add context menu to search page (#1701)
## Description
Adds long clicking search results to show the context menu. It's the
same context menu as elsewhere in the app.

### Related issues
Closes #1695

### Testing
Emulator to toggle favorite and watched status on various types

## Screenshots
N/A

## AI or LLM usage
None
2026-07-16 11:36:07 -04:00
Damontecres
a350fa2017
Improvements to "Add to playlist" dialog (#1706)
## Description
Fixes & improves the "Add to playlist" dialog:
- Added searching by name
- Playlists are sorted by recently updated
- No longer filters by media type
- Filters based on user's permission for the playlists

The behavior is now consistent with the web UI's list except for the
sort

### Related issues
Should fix #1124

### Testing
Emulator testing

## Screenshots
N/A

## AI or LLM usage
None
2026-07-16 11:35:48 -04:00
Damontecres
39c030c1d5
Fix loading a canceled discover tv show (#1709)
## Description
#1614 added filtering by a TV production status. I pulled the enum
values from a comment in Seerr's code which spelled it as `Cancelled`,
but the actual API uses `Canceled`. This PR corrects the spelling.

Also, since this value isn't actually used yet, I've removed it from
`TvDetails` for now.

### Related issues
Fixes #1703

### Testing
Emulator, filter by production status as canceled and open any result

## Screenshots
N/A

## AI or LLM usage
None
2026-07-16 11:35:32 -04:00
Damontecres
c2aab06c0b
Save sort & filter within a genre or studio (#1711)
## Description
Saves the sort and filter of items within a genre or studio
(Movies->Genres tab->Click genre) across genres or studios. So if you
open "Action" movies and change the sort, the sort will be used for
"Animation" or "Horror" movies.

This is saved separate from the sort/filter on the Library tab.

### Related issues
Closes https://github.com/damontecres/Wholphin/issues/1253

### Testing
Emulator by switching sorts

## Screenshots
N/A

## AI or LLM usage
None
2026-07-15 20:25:43 -04:00
Damontecres
84f6035103
Fix bug finding the episode to scroll to on Series Overview (#1708)
## Description
Fixes an off-by-one error when searching for the right episode (or
season) to scroll to on the series overview page.

While the code works most of the time, on cases where a special appears
_after_ a season is included, the final episode in the season not be
scrolled to. For example, if Special S00E04 comes after Season 2 (which
has 10 episodes), clicking on S02E10 on the home page won't scroll to
that episode on the series overview, instead S02E01 will be focused. And
S00E04 will appear at the end of the list.

This should also be a very, very tiny performance boost since the
"search the whole list" fallback won't be triggered as often. I also
added some comments to explain the function better.

### Related issues
N/A, just noticed it yesterday

### Testing
Emulator in the situation described above

## Screenshots
N/A

## AI or LLM usage
None
2026-07-15 18:45:32 -04:00
Damontecres
46b2f2839f
Fix compile error from merging #947 & #1680 (#1700)
## Description
Fixes [compile
error](https://github.com/damontecres/Wholphin/actions/runs/29357259594/job/87167912698)

### Related issues
Resulted from merging #947 & #1680 together into main

### Testing
Compiled & unit tests

## Screenshots
N/A

## AI or LLM usage
None
2026-07-14 15:35:49 -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
077b7e4570
Show duration & ends at time on next up card (#1690)
## Description
Adds the duration & ends at time to the next up card

### Related issues
Closes #1664

### Testing
<!-- Describe how this change was tested and on what device(s) -->

## Screenshots
<img width="660" height="241" alt="image"
src="https://github.com/user-attachments/assets/7551843e-4f53-43fc-af2a-3e503ba6d03c"
/>

## AI or LLM usage
None
2026-07-13 01:30:54 -04:00
Damontecres
f74306a12c
Fix subtitle outline width not applying in release builds (#1687)
## Description
Fix applying the subtitle outline width in ExoPlayer for release builds

The outline width field isn't public, so reflection is used to bypass
and modify it. Since proguard/r8 renames all of the classes and fields,
this meant the field was no longer found. The fix is to have proguard
preserve the necessary field names.

### Related issues
Fixes #1681

### Testing
Emulator w/ release build

## Screenshots
N/a

## AI or LLM usage
None
2026-07-11 13:21:41 -04:00
Damontecres
26124a5348
Improve track selection logic (#1654)
## Description
Updates the logic for selecting tracks which should fix several edge
cases

The new logic groups track types together and then applying the user's
selection to the actual tracks in the file. For example, if the user
choosing the audio track which the server reports as absolute `index=2`
(eg the _third_ track of any type in the file) and audio `index=1` (eg
the _second audio_ track in the file), then the track selection logic
will use the _second audio_ track in the direct played file (after
accounting for id sorting).

Some other changes:
- If direct playing and an unsupported track is selected, the app will
now fall back to transcoding
- If the correct audio/subtitle tracks are not found, the app will now
fall back to transcoding instead of blocking any track change attempts

### TODO
Not necessarily in this PR, but some things need further investigation:
- Handling external audio tracks
- Why does ExoPlayer order the tracks differently sometimes, see
[example
here](https://github.com/damontecres/Wholphin/issues/791#issuecomment-4836470813)
where `0:3` is _after_ `0:4`, but the `MediaSourceInfo` lists them `0:3`
then `0:4`

### Related issues
Maybe fixes #791
Should fix
https://github.com/damontecres/Wholphin/issues/1005#issuecomment-4085440175
Related to the parent issue, #1475
Replaces #845

### Testing
Unit tests mostly, some basic tests on emulator

Needs more "real world" testing

#### Try it out?

You can try out this build by following the [instructions
here](https://github.com/damontecres/Wholphin/releases/tag/develop-track-selection).
If you do, please report your results in this PR!

## Screenshots
N/A

## AI or LLM usage
None
2026-07-10 17:49:47 -04:00
Damontecres
d481aea85a
Experimental option to transcode surround sound audio to AC3 (#947)
## Description
Adds an experimental setting to prefer transcoding audio with more than
2 channels to AC3. Stereo audio does not transcode.

This is basically a workaround for HDMI ARC (not eARC) or optical audio
devices because instead of 2 uncompressed PCM channels, 6 AC3 channels
(ie 5.1) is possible.

An downside of enabling this is that embedded subtitles be burned in
causing a video transcode as well.

### Related issues
Fixes #255
Incorporates some code from #1410

### Testing
Just on the emulator so far to observe if the settings trigger the
transcode or not

## Screenshots
N/A

## AI or LLM usage
None

---------

Co-authored-by: darkflame91 <darkflame91@gmail.com>
2026-07-09 13:15:42 -04:00
Damontecres
f2287a85e8
Release v1.0.3
Some checks failed
Create release / publish (push) Has been cancelled
2026-07-08 11:22:42 -04:00
Damontecres
35e88e5f2a
Update translations as of 2026-07-07 (#1673)
Updates translations as of 2026-07-07

Thank you to all of the contributors!
2026-07-08 11:14:31 -04:00
idezentas
e0143c0e26
Translated using Weblate (Turkish)
Currently translated at 100.0% (608 of 608 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-07 17:56:28 +00:00
Outbreak2096
ab47868b6c
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (608 of 608 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-07 17:56:28 +00:00
TheMatrixan
b93e12681e
Translated using Weblate (Polish)
Currently translated at 100.0% (608 of 608 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-07-07 17:56:28 +00:00
SimonHung
b57a6d7b00
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (608 of 608 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-07 17:56:28 +00:00
North-DaCoder
b4c6d40edc
Translated using Weblate (German)
Currently translated at 100.0% (608 of 608 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-07-07 17:56:28 +00:00
Waspoedertje
182c258c03
Translated using Weblate (Dutch)
Currently translated at 100.0% (601 of 601 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-07-07 17:56:28 +00:00
North-DaCoder
30ec17e50f
Translated using Weblate (German)
Currently translated at 99.8% (600 of 601 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-07-07 17:56:27 +00:00
TheMatrixan
422ac89575
Translated using Weblate (Polish)
Currently translated at 100.0% (601 of 601 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-07-07 17:56:27 +00:00
arcker95
517b5b0507
Translated using Weblate (Italian)
Currently translated at 100.0% (601 of 601 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-07-07 17:56:27 +00:00
SimonHung
db4b4b8489
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (601 of 601 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-07 17:56:27 +00:00
opakholis
63d4e8e80d
Translated using Weblate (Indonesian)
Currently translated at 100.0% (601 of 601 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-07-07 17:56:27 +00:00
n8llcaster
eea9a61064
Translated using Weblate (Slovak)
Currently translated at 100.0% (601 of 601 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-07 17:56:27 +00:00
idezentas
f1a205a465
Translated using Weblate (Turkish)
Currently translated at 100.0% (601 of 601 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-07 17:56:27 +00:00
Outbreak2096
52e5aa41d2
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (601 of 601 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-07 17:56:27 +00:00
SimonHung
662ef66319
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (601 of 601 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-07 17:56:27 +00:00
ymir
a3de7c330a
Translated using Weblate (French)
Currently translated at 100.0% (601 of 601 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-07-07 17:56:27 +00:00
TheMatrixan
dfafed5801
Translated using Weblate (Polish)
Currently translated at 100.0% (601 of 601 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-07-07 17:56:27 +00:00
Priit Jõerüüt
d7d283c188
Translated using Weblate (Estonian)
Currently translated at 100.0% (601 of 601 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-07-07 17:56:27 +00:00
TheMatrixan
b887507301
Translated using Weblate (Polish)
Currently translated at 100.0% (597 of 597 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-07-07 17:56:27 +00:00
idezentas
c8cf918116
Translated using Weblate (Turkish)
Currently translated at 100.0% (597 of 597 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-07 17:56:27 +00:00
Outbreak2096
b2f1cf1455
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (597 of 597 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-07 17:56:27 +00:00
SimonHung
818692a405
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (597 of 597 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-07 17:56:27 +00:00
opakholis
4f9abd0edc
Translated using Weblate (Indonesian)
Currently translated at 98.1% (586 of 597 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-07-07 17:56:27 +00:00
Priit Jõerüüt
44d7880468
Translated using Weblate (Estonian)
Currently translated at 100.0% (597 of 597 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-07-07 17:56:26 +00:00
North-DaCoder
e69afe1848
Translated using Weblate (German)
Currently translated at 100.0% (597 of 597 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-07-07 17:56:26 +00:00
Codeberg Translate
94a568ae95
Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/
2026-07-07 17:56:26 +00:00
JorgeLNJunior
56455344ed
Translated using Weblate (Portuguese (Brazil))
Currently translated at 93.7% (559 of 596 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt_BR/
2026-07-07 17:56:26 +00:00
idezentas
ac45a7b64a
Translated using Weblate (Turkish)
Currently translated at 100.0% (596 of 596 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-07 17:56:26 +00:00
arcker95
c6bbd0a2de
Translated using Weblate (Italian)
Currently translated at 100.0% (596 of 596 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-07-07 17:56:26 +00:00
Priit Jõerüüt
d0f44513f6
Translated using Weblate (Estonian)
Currently translated at 100.0% (596 of 596 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-07-07 17:56:26 +00:00
idezentas
a33670ef11
Translated using Weblate (Turkish)
Currently translated at 100.0% (594 of 594 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-07-07 17:56:26 +00:00
Outbreak2096
587c289775
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (594 of 594 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-07-07 17:56:26 +00:00
rgabbo
5390332e0f
Translated using Weblate (Italian)
Currently translated at 100.0% (594 of 594 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-07-07 17:56:26 +00:00
arcker95
2c62d36cbe
Translated using Weblate (Italian)
Currently translated at 100.0% (594 of 594 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-07-07 17:56:26 +00:00
SimonHung
54163bc37f
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (594 of 594 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-07-07 17:56:26 +00:00
n8llcaster
c5a0f266d9
Translated using Weblate (Slovak)
Currently translated at 100.0% (594 of 594 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-07-07 17:56:26 +00:00
Priit Jõerüüt
22c00fea1b
Translated using Weblate (Estonian)
Currently translated at 100.0% (594 of 594 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-07-07 17:56:26 +00:00
TheMatrixan
5cef952797
Translated using Weblate (Polish)
Currently translated at 100.0% (589 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-07-07 17:56:26 +00:00
Damontecres
013755208d
Update Seerr integration version & start blocklist handling logic (#1671)
## Description
Start testing against Seerr `3.3.0` which has been out for over a month.
The only significant change is that the new "blocklisted" status is
added, but it uses the same code as the previous "deleted" status. So
generally, Seerr `3.2.0` will still work.

This starts work on supporting Seerr's blocklist which, unfortunately,
is expected to be enforced on the client side. For now, blocklisted
items still show, but the request button is disabled and shows
"Unavailable" instead.

Finally, some Seerr parent permissions I missed in #1631 are added.

### Related issues
Related to #1626

### Testing
Emulator testing

## Screenshots
N/A

## AI or LLM usage
None
2026-07-07 13:56:22 -04:00
Tal Sarid
f6e00c7820
Add localized duration formatting and related string resources (#1571)
## Description
Instead of using English-only duration format (e.g. `HHh MMm SSs`) in
various places, I've added a format function that is based on several
new localized resources keys.

That includes:
1. Media runtime in Item view
2. Time remaining in Item view
3. End time in Player view
4. Time passed and time remained in Player view

### Related issues
None

### Testing
I installed the app on my Google TV Streamer, switched to my language,
browsed all item views that I could think of to see that the duration
labels are all localized in my language. I didn't commit those strings,
I will do it through the Weblate after it will get merged. I also played
a few items and saw that the labels there are localized as well.

## Screenshots
It looks the same in English as before, currently no other language has
custom resources for it yet.

## AI or LLM usage
Used Claude Sonnet to detect places that I missed that are related to
this localization change. Also it helped me to get access to my format
function from various places in the code.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-07-06 08:43:06 -04:00
Damontecres
1d7ddd713b
Bump & clean up some dependencies (#1660)
## Description
Updates several dependencies and cleans up some naming and duplicates.

There are no user facing changes in this PR

### Dev notes

There is a workaround needed to upgrade to Kotlin 2.4.0 to avoid
https://github.com/google/ksp/issues/2964

There are a few more dependencies, but they require increasing the
target SDK to 37, but I don't want to do that yet. API 37 requires
requesting a [new permission for local network
access](https://developer.android.com/privacy-and-security/local-network-permission).
Since that is a critical requirement for Wholphin/Jellyfin, I need to
spend more time reviewing the dev guide to make sure it's handled
correctly.

### Related issues
Replaces #1593
Replaces #1512
Replaces #1465
Replaces part of #1446

### Testing
Unit tests and quick emulator run

## Screenshots
N/A

## AI or LLM usage
None
2026-07-03 11:36:54 -04:00
renovate[bot]
06d7c85027
Update Github Actions (#1620) 2026-07-01 19:39:05 -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
ad25a1eb7c
Fix issue templates 2026-06-28 14:18:21 -04:00
calebvetter
1c9081951a
Fix AFTKRT + HSL/TS + EAC3 7.1 playback issues (#1580)
## Description
This is a targeting fix for a very specific bug: on Fire TV 4k Max
(AFTKRT), and possibly others but untested, ExoPlayer can't play an
HLS/TS stream that has 8-channel (7.1) EAC3 audio. Direct play is not
affected (7.1 EAC3 plays fine), and fewer channels work fine (i.e. 5.1
EAC3) even in an HLS/TS wrapper.

This bug + device is tracked in KnownDefects. If more models are tested
and found to have the same issue, they can be added to the list. Top
candidates are probably AFTKA and AFTKM since they are also in the
models with HEVC DoVi/HDR10+ playback issues and so probably have
similar underlying limitations, but I don't have those devices to test.

The fix is to first allow the app to make a playback request to the
server and receive the stream info Jellyfin has decided to send. Then it
evaluates the settings and stream for the following:
- Playback is using ExoPlayer
- Device is in the modelsWithEac3HlsPassthroughBug list (currently only
AFTKRT)
- Stream is not direct play
- Stream is HLS with MPEG-TS container
- Audio is 8-channel EAC3

We have to use a two-request fix because we don't know what Jellyfin is
going to decide for the stream until we receive it. That way normal
playable streams aren't getting overwritten by the fix when they
shouldn't be. So the only streams that get the two request path applied
is the exact fix for this bug.

To apply post-playback-info fixes (including this one) a helper file was
created PostPlaybackInfoFixes. In the future, if there are more bugs
that can only be determined after the decided stream info is received,
they can be added here. This keeps things logical and maintainable.


### Related issues
The bug was able to be reproduced on multiple items with this exact
scenario by simply playing back the item (making sure ExoPlayer is set
as the default player in settings).

### Testing
Tested on the target device: Amazon Fire TV 4k Max (2nd Gen), model
AFTKRT.

The following media was tested:
- ExoPlayer, multiple titles with HEVC, EAC3 7.1 copied into HLS/TS (all
failed, only processing 1-2fps)
- Direct play of the same titles (works)
- EAC3 5.1, HLS, MPEG-TS (works)
- MPV with failing titles (works)

The following device and settings were tested and had no effect on the
above media results, ruling out external connections and settings:
- HDMI to an LG TV, both connected to a soundbar via ARC, and
disconnected using internal speakers
- HDMI to a Dell computer monitor
- Fire TV Settings > Display & Sound > Audio > Surround Sound > all 4
settings tested (Best Available, PCM, Dolby Digital Plus, Dolby Digital)

## AI or LLM usage
Codex was used to debug the issue, create the initial change, and do
iterative testing on the device to narrow the scope. The code was then
carefully reviewed and I made structural decisions so everything fits
well within the project's established conventions, removing bloat, added
comments and kept it targeted, clear and maintainable.
2026-06-27 17:42:01 -04:00
Damontecres
00338c5c11
Fix MPV minify crash (#1636)
## Description
Fixes a crash when trying to play content via MPV on release builds.

After #1365, the package name for `MPVLib` changed, so the existing
proguard rule no longer applied. This meant the native code could no
longer find the right class.

### Related issues
Fixes #1633
https://github.com/damontecres/wholphin-extensions/pull/9 fixes this
upstream

### Testing
Tested release builds on emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-06-27 14:29:46 -04:00
Damontecres
083d4e4fa8
Add experimental settings & video tunneling (#1624)
## Description
This PR introduces the concept of "experimental" settings or features.
These may cause instabilities or may need further testing. They may be
changed or removed any time. Also, the developers won't provide lots of
support for them.

The goal is expose some specialized settings that might only apply to a
small subset of users or test out tweaks to playback.

The first experimental feature is a toggle to enable [video
tunneling](https://developer.android.com/media/media3/exoplayer/track-selection#tunneling).

### Related issues
Related to #1283

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-06-27 14:29:37 -04:00
Damontecres
21e720c420
Also check Seerr parent permissions (#1631)
## Description
When saving Seerr permissions on the WebUI, if a "parent" permission is
checked, only its bit is flipped and its children's bits are not. So
clients must check if a user has the permission _or_ its parent.

For example, if just "Request 4K" is checked, the flags for "Request 4K
Movies" and "Request 4K TV" will not set explicitly.

### Related issues
Fixes #1630

### Testing
Emulator & unit tests, also manually reviewed API requests/responses on
Seerr's web UI

## Screenshots
N/A

## AI or LLM usage
None
2026-06-27 14:28:57 -04:00
Damontecres
a8ade9171e
Update wholphin-extensions with newer MpvPlayer (#1365)
## Description
Uses the newer version of `wholphin-extensions` which now has the Kotlin
code, so this PR removes the MPV Kotlin code from this repo. Also
updates gradle to use providers.

There are no user facing changes

### Dev notes

`wholphin-mpv` can be provided by:
1. [Fetching pre-built
binaries](https://github.com/damontecres/wholphin-extensions#pre-built-binaries)
(Preferred method for devs)
2. Building `wholphin-extensions` from
[source](https://github.com/damontecres/wholphin-extensions#build-from-source)
3. Using `wholphin-mpv-stub` (the new module added in this PR) which
allows for compiling Wholphin, but will throw exceptions if MPV is used
during runtime

### Related issues
Related to #1172 

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-06-26 12:50:45 -04:00
Damontecres
f5ce9745ba
Several UI/UX minor changes (#1623)
## Description
Gathering some smaller UI/UX changes:
- Default to the user's preferred subtitle language when
searching/downloading subtitles and if unset, fall back to the UI's
language
- Only show >0s runtime on quick details & don't show negative ends at
- On the OS channel row for recently added, only include movies &
episodes
- Only show movies & episodes to "Recently Added" Android OS channel
- Fix context menu for adding songs & albums to the music queue

### Related issues
Related to #1339 & #1484
Fixes #1617
Fixes
https://github.com/damontecres/Wholphin/issues/1329#issuecomment-4440684328

### Testing
Just on the emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-06-26 12:43:56 -04:00
Damontecres
e5887dfa15
Better UX for separate SDR vs HDR subtitle styles (#1622)
## Description
This PR moves the separated HDR subtitle style behind a toggle. This
means users must now opt-in to using separated SDR vs HDR subtitle
styles. This is a better UX for users who want to customize the style
but use the same styles for both.

Users who have changed the HDR subtitles previously will have the toggle
enabled automatically. If the user hasn't changed HDR style or their SDR
& HDR styles are the the same, the toggle will be disabled.

Also, fixes a bug where when playback switched between SDR or HDR, the
subtitle styles might not be updated.

### Related issues
Closes #1603

### Testing
Emulator & unit tests for migration

## Screenshots
N/A

## AI or LLM usage
None
2026-06-24 19:26:05 -04:00
Christian Tismer-Sperling
8a684545ac
Respect system 24-hour setting in clock time format (#1619)
## Description
<!-- Describe the changes in detail -->
The clock ignored the device's system 24-hour preference:
getTimeFormatter derived 12/24h purely from the locale. This reads
DateFormat.is24HourFormat and builds the time pattern from it, so the
clock follows the system setting.

### Related issues
<!-- If this is a new feature or a change, there must be a discussion in
an issue first, reference the issue here -->
<!-- If fixing a bug, reference the bug issue here, or describe the bug,
including steps to reproduce -->

### Testing
<!-- Describe how this change was tested and on what device(s) -->
Tested on the emulator toggling the system 24-hour setting on/off.
assembleDefaultDebug, testDefaultDebugUnitTest and pre-commit (ktlint)
pass.

## Screenshots
<!-- Please include screenshots if the PR alters any UI elements -->

## AI or LLM usage
<!-- If you used any AI or LLM assistance, please list where in the code
and how you tested it -->
The analysis and the fix were done by me with AI assistance (Claude). I
wrote the description myself; the English wording was polished with AI.
I understand the code and can maintain it.
2026-06-24 17:02:53 -04:00
Damontecres
d705a3ea01
Add filtering & sorting for discover grid pages (#1614)
## Description
This PR adds sort & filter options for the discover grid pages.

Sort options:
- Popularity
- Release date
- "Community rating" (TMDB user vote)
- Name

Filter options:
- Genre
- Studio/Network
- "Community rating" (TMDB user vote)
- Parental rating
- Production status (TV only)

Also, two more tabs are added to the discover page for browsing movies
and TV. These are essentially the same as clicking "View more" on the
respective rows on the Discover tab.

### Limitations

This PR hardcodes the region used for studio listing and filtering to
"United States". I'll work on a follow up PR to enable users to specify
this.

The sort & filters are not persisted. I think they should be on the two
new tabs, but not elsewhere, but this will be a follow up PR.

### Related issues
Related to #702 
Implements some of #1511
Rewrites the filter logic in #1610 

### Testing
Emulator, but needs further testing

## Screenshots
<img width="1280" height="720" alt="discover_filters Large"
src="https://github.com/user-attachments/assets/cdefb95c-c4b7-4e06-9bac-b535d372ba03"
/>


## AI or LLM usage
None
2026-06-23 17:24:37 -04:00
Damontecres
552c3b5500
Fix inconsistent playback end behavior issues (#1616)
## Description
The exact behavior of what happens when playback ends based on the
user's settings would change between versions. This PR defines the
behaviors better and includes unit tests to verify future changes won't
break it again.

### Related issues
Fixes #1548
Related to https://github.com/damontecres/Wholphin/pull/1257

### Testing
Emulator & unit tests

## Screenshots
N/A

## AI or LLM usage
None
2026-06-23 17:23:47 -04:00
Damontecres
899e3decc2
Prefer non-forced when subtitle preference is always on (#1618)
## Description
If subtitles preferences are "Always", this PR will prefer non-forced
tracks even if any forced ones are default tracks.

This is in-line with the web UI from my testing and another review of
the code.

### Related issues
Fixes
https://github.com/damontecres/Wholphin/issues/790#issuecomment-4708705576

### Testing
Unit tests

## Screenshots
N/A

## AI or LLM usage
None
2026-06-23 17:23:37 -04:00
Damontecres
7422f15bff
Fix possible crash if remembered tab no longer exists (#1613)
## Description
If remember tabs is enabled and the current remembered index is somehow
removed, the app would crash.

I think the only way a regular user can encounter this is if the server
removes a live TV recording folder that the user has
selected/remembered. All other tabbed pages use a fixed list.

### Related issues
None

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-06-23 11:11:39 -04:00
Damontecres
f21b29fe1b
Discover movies & tv shows by genre (#1610)
## Description
Adds genre rows for movies & TV shows to the discover page. These work
the same as on the Seerr web UI, clicking one will show media in the
genre.

### Related issues
Related to https://github.com/damontecres/Wholphin/issues/702

### Testing
Just on the emulator so far

## Screenshots
<img width="1280" height="720" alt="discover_genres Large"
src="https://github.com/user-attachments/assets/93e5ef25-e253-49bc-9009-89f814889c98"
/>


## AI or LLM usage
None
2026-06-22 19:35:44 -04:00
Damontecres
c945e1a472
Several small fixes (#1612)
## Description
- Fixes showing "null" in the subtitle for some tv episodes (usually
daily shows like Jeopardy)
- Partially fix holding back to return to the previous grid position
(still has focus issues)
- Show/hide tabs on the discover page similar to other pages in the app
- Prevent a warning server-side if a negative playback timestamp is sent

### Related issues
N/A

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-06-22 18:38:19 -04:00
Damontecres
77d7571276
Choose Seerr quality profile & root folder (#1604)
## Description
Adds option to select the quality profile and/or root folder when
requesting from Seerr.

The user needs "Advanced Requests" permission to use this feature. If
not, the options won't show.

### Related issues
Closes #863
Related to #702

### Testing
Emulator w/ different user permissions and different request
configurations

## Screenshots
<img width="1280" height="720" alt="request_movie Large"
src="https://github.com/user-attachments/assets/2665dcbb-e6c3-4d26-857c-b465edff6a02"
/>


## AI or LLM usage
None
2026-06-22 18:37:16 -04:00
Damontecres
4431757705
Release v1.0.2
Some checks failed
Create release / publish (push) Has been cancelled
2026-06-22 10:36:27 -04:00
Damontecres
30edde5bd2
Translations 2026-06-22 (#1609)
Updates translations as of 2026-06-22

Thank you to all of the contributors!
2026-06-22 10:36:01 -04:00
Damontecres
d85f188236
Show runtime on different versions dialog (#1607)
## Description
Small PR to add the runtime for different versions of media to the
choose version dialog.

This can be helpful to further distinguish versions such as theatrical
vs extended editions.

### Related issues
Suggested
https://github.com/damontecres/Wholphin/pull/1522#issuecomment-4766811832

### Testing
Emulator

## Screenshots
<img width="560" height="337" alt="image"
src="https://github.com/user-attachments/assets/47b0c1be-d1d7-4845-b097-a67358f584f9"
/>


## AI or LLM usage
None
2026-06-22 09:44:19 -04:00
tal_sarid
ac18bdf285
Translated using Weblate (Hebrew)
Currently translated at 100.0% (589 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-06-21 21:06:25 +00:00
danpergal84
501ecf89b0
Translated using Weblate (Spanish)
Currently translated at 100.0% (589 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-06-21 21:06:25 +00:00
wizart
db401962d0
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (589 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/uk/
2026-06-21 21:06:25 +00:00
SimonHung
c2f2f13146
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (589 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-06-21 21:06:25 +00:00
Priit Jõerüüt
0cb0d899a1
Translated using Weblate (Estonian)
Currently translated at 100.0% (589 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-06-21 21:06:25 +00:00
Pwnisher
200b2cd06c
Translated using Weblate (Romanian)
Currently translated at 55.8% (329 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ro/
2026-06-21 21:06:25 +00:00
gatisr
52f11cc405
Translated using Weblate (Latvian)
Currently translated at 38.7% (228 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/lv/
2026-06-21 21:06:25 +00:00
alexkahler
dc5792e5ce
Translated using Weblate (Danish)
Currently translated at 66.3% (391 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-06-21 21:06:25 +00:00
idezentas
5568ded067
Translated using Weblate (Turkish)
Currently translated at 100.0% (589 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-06-21 21:06:24 +00:00
Sathen
8245b49cbc
Translated using Weblate (Ukrainian)
Currently translated at 68.2% (402 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/uk/
2026-06-21 21:06:24 +00:00
veisen
78195740a3
Translated using Weblate (Czech)
Currently translated at 77.4% (456 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-06-21 21:06:24 +00:00
oyvhov
77f944dd99
Translated using Weblate (Norwegian Nynorsk)
Currently translated at 54.6% (322 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nn/
2026-06-21 21:06:24 +00:00
Outbreak2096
cb2724e881
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (589 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-06-21 21:06:24 +00:00
adambibor
eac606f1dd
Translated using Weblate (Hungarian)
Currently translated at 53.4% (315 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2026-06-21 21:06:24 +00:00
TheMatrixan
bcc84a99a4
Translated using Weblate (Polish)
Currently translated at 97.9% (577 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-06-21 21:06:24 +00:00
arcker95
2cdf5a1738
Translated using Weblate (Italian)
Currently translated at 95.4% (562 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-06-21 21:06:24 +00:00
ymir
ef1a8184da
Translated using Weblate (French)
Currently translated at 100.0% (589 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-06-21 21:06:24 +00:00
RabSsS
d06ae90f1d
Translated using Weblate (French)
Currently translated at 100.0% (589 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-06-21 21:06:24 +00:00
oyvhov
8adfa3f312
Translated using Weblate (Norwegian Bokmål)
Currently translated at 52.1% (307 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nb_NO/
2026-06-21 21:06:24 +00:00
n8llcaster
818e14c53a
Translated using Weblate (Slovak)
Currently translated at 85.9% (506 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-06-21 21:06:24 +00:00
Priit Jõerüüt
127f3c74c2
Translated using Weblate (Estonian)
Currently translated at 95.4% (562 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-06-21 21:06:24 +00:00
streamyfinchris
115799c6c6
Translated using Weblate (Swedish)
Currently translated at 82.3% (485 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-06-21 21:06:24 +00:00
American_Jesus
f88f78116b
Translated using Weblate (Portuguese)
Currently translated at 100.0% (589 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-06-21 21:06:24 +00:00
damontecres
b522320e97
Translated using Weblate (Spanish)
Currently translated at 99.6% (587 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-06-21 21:06:24 +00:00
nofkclue
cb779c82ff
Translated using Weblate (German)
Currently translated at 100.0% (589 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-06-21 21:06:23 +00:00
North-DaCoder
cd263fadd3
Translated using Weblate (German)
Currently translated at 100.0% (589 of 589 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-06-21 21:06:23 +00:00
Pwnisher
060641ece3
Translated using Weblate (Romanian)
Currently translated at 56.1% (330 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ro/
2026-06-21 21:06:23 +00:00
gatisr
afe26f3db4
Translated using Weblate (Latvian)
Currently translated at 38.9% (229 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/lv/
2026-06-21 21:06:23 +00:00
EmadAljohani
222c6c4924
Translated using Weblate (Arabic)
Currently translated at 25.8% (152 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ar/
2026-06-21 21:06:23 +00:00
alexkahler
66459bb2fc
Translated using Weblate (Danish)
Currently translated at 66.6% (392 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-06-21 21:06:23 +00:00
RafaelSoaresP
e80d689603
Translated using Weblate (Portuguese (Brazil))
Currently translated at 91.3% (537 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt_BR/
2026-06-21 21:06:23 +00:00
Sathen
8168a07dc6
Translated using Weblate (Ukrainian)
Currently translated at 68.5% (403 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/uk/
2026-06-21 21:06:23 +00:00
veisen
1ce07862af
Translated using Weblate (Czech)
Currently translated at 77.7% (457 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-06-21 21:06:23 +00:00
oyvhov
1a90b480eb
Translated using Weblate (Norwegian Nynorsk)
Currently translated at 54.9% (323 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nn/
2026-06-21 21:06:23 +00:00
Rand0mB0y
7f43642467
Translated using Weblate (Greek)
Currently translated at 29.0% (171 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/el/
2026-06-21 21:06:23 +00:00
efko
5c5c5cd202
Translated using Weblate (Greek)
Currently translated at 29.0% (171 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/el/
2026-06-21 21:06:23 +00:00
adambibor
376ae48aeb
Translated using Weblate (Hungarian)
Currently translated at 53.7% (316 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2026-06-21 21:06:23 +00:00
Chosnuk
4a29672569
Translated using Weblate (Polish)
Currently translated at 95.7% (563 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-06-21 21:06:23 +00:00
Pawsi
8ba6b1c613
Translated using Weblate (Polish)
Currently translated at 95.7% (563 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-06-21 21:06:23 +00:00
Tellos
26b64bd636
Translated using Weblate (Russian)
Currently translated at 55.2% (325 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ru/
2026-06-21 21:06:23 +00:00
Vistaus
e177f36212
Translated using Weblate (Dutch)
Currently translated at 82.3% (484 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-06-21 21:06:23 +00:00
arcker95
d5d0f99474
Translated using Weblate (Italian)
Currently translated at 95.7% (563 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-06-21 21:06:23 +00:00
RabSsS
27ebf9d621
Translated using Weblate (French)
Currently translated at 95.7% (563 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-06-21 21:06:22 +00:00
oyvhov
61d07a9d29
Translated using Weblate (Norwegian Bokmål)
Currently translated at 52.3% (308 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nb_NO/
2026-06-21 21:06:22 +00:00
n8llcaster
bedbde3ebb
Translated using Weblate (Slovak)
Currently translated at 86.2% (507 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-06-21 21:06:22 +00:00
streamyfinchris
06c99b2aa1
Translated using Weblate (Swedish)
Currently translated at 82.6% (486 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-06-21 21:06:22 +00:00
danpergal84
891cbc8ae3
Translated using Weblate (Spanish)
Currently translated at 100.0% (588 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-06-21 21:06:22 +00:00
rcv11x
21619c5e10
Translated using Weblate (Spanish)
Currently translated at 100.0% (588 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-06-21 21:06:22 +00:00
damontecres
379da64442
Translated using Weblate (Spanish)
Currently translated at 100.0% (588 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-06-21 21:06:22 +00:00
texxlan
37c02e2491
Translated using Weblate (German)
Currently translated at 95.5% (562 of 588 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-06-21 21:06:22 +00:00
renovate[bot]
6a18e30314
Update Github Actions to v7 (#1585) 2026-06-21 17:06:18 -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
8e0798ee16
Unit tests for PlaybackViewModel & several services (#1592)
## Description
This PR contains only tests, there are no user facing changes.

Adds tests for playback covering:
- Playing next, previous, or within the queue
- Media segments (intro/credit)
- Playing intros before media

Adds basic unit tests for track selection. This uses a new "test track"
builder which can model various files and build them as represented
(differently) by the server, ExoPlayer, and MPV. In future, this will
allow for testing of more complex files.

Also add unit tests for services covering things like:
- Fetching extras
- Deleting media
- Server/user management & auth

### Related issues
Will help with testing/verifying some issues like #1475, #1548 

### Testing
Ran the unit tests!

## Screenshots
N/A

## AI or LLM usage
None
2026-06-19 11:06:35 -04:00
renovate[bot]
2afac3a1a5
Update openapi.generator to v7.23.0 (#1542) 2026-06-18 18:59:54 -04:00
renovate[bot]
3fd2cbbbd6
Pin dependencies (#1589) 2026-06-18 18:58:34 -04:00
Damontecres
0a1f2a5fef
Update renovate settings (#1588)
Just updating the renovate config to separate out android TV
dependencies & use digests for GHA.
2026-06-18 12:00:39 -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
ddbec0da73
Fix issues from #1530 2026-06-14 19:55:01 -04:00
jankg
8534c58426
Translated using Weblate (German)
Currently translated at 100.0% (575 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-06-14 17:36:51 -04:00
Damontecres
fa5cc99c3b
De-duplicate string resource keys (#1560)
## Description
Ensures that all string & plural resource keys are unique. This should
help make sure all of the strings are shown in weblate's translation.

### Related issues
Related to #1551

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-06-12 01:24:24 -04:00
Damontecres
38b4a8c4de
Play or shuffle series, season, collections, & playlists from context menu (#1544)
## Description
Adds options to the context menu to play or shuffle items in a series,
season, collection, or playlist. So you can long click a TV show and
play it directly.

Playing a TV show will play the next up episode (same as the play button
on the series detail page).

Playing a season, collection, or playlist will start from the beginning.

Also, removes the green play arrows and uses the same neutral color as
other icons.

### Related issues
Closes #1451
Closes #1507

### Testing
Emulator from library pages & series detail pages

## Screenshots
N/A

## AI or LLM usage
None
2026-06-10 19:30:07 -04:00
Damontecres
f7f0ea9cca
Fixes missing parental ratings & focus loss when changing filters (#1559)
## Description
- Ensures all parent ratings are shown in the filter drop down
- Fixes focus loss after filtering library and there are no results

### Related issues
Fixes #1535 (along w/ #1529)
Fixes #1534

### Testing
Emulator w/ a UK movie library

## Screenshots
N/A

## AI or LLM usage
None
2026-06-10 19:29:49 -04:00
Damontecres
01bd3249fa
Fixes two row bouncing UI issues (#1543)
## Description

Fixes row bounce using `SeasonCard` when are two exactly two items. This
was done by calculating the y-offset in the `layout` modifier instead of
`offset`. This has the side effect of probably being very slightly more
efficient since only 1 animation state is needed now.

Fixes row bounce on the second row of the home page if the first row is
empty and not shown.

### Related issues
Fixes #1528
Fixes #1271

### Testing
Emulator in different situations. Also checked the layout inspector to
make sure the optimizations from #1184 weren't affected

## Screenshots
N/A

## AI or LLM usage
None
2026-06-08 17:23:09 -04:00
Damontecres
93d601e7bf
Improvements to debug page (#1538)
## Description
Makes some improvements to the debug page. Also adds some audio device
listing for debugging.

### Related issues
N/A

### Testing
Emulator & shield

## Screenshots
N/A

## AI or LLM usage
None
2026-06-08 13:45:48 -04:00
Damontecres
b49926f80b
Store remembered tabs in app database instead of preferences (#1541)
## Description
Moves storing the remembered tabs from app preferences to the app
database. This means deleting the user will remove the remembered tabs
and the app preferences file is smaller and faster to load.

The remembered tabs are migrated automatically when the app is updated.

There are no significant user facing changes in this PR.

### Related issues
N/A

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-06-08 13:45:38 -04:00
Damontecres
a4ee1e5175
Only show relevant parental ratings when filtering (#1529)
## Description
Updates the parental rating filter so it only shows the relevant options
instead of all of them. This is now the same behavior as the web UI.

Additionally, the filter options are cached for a short time so the UI
doesn't need to load them every time the menu is opened.

Dev note: Refactors `FilterUtils` into a service which allows for the
caching

### Related issues
Closes #410

### Testing
Emulator with movie & tv show libraries

## Screenshots
N/A

## AI or LLM usage
None
2026-06-07 07:15:40 -04:00
Damontecres
d1b8c09615
Add a shortcut to chosing the version of an item (#1522)
## Description
Based on feedback from users, many felt it was difficult to 1) tell if
multiple version of an item existed and 2) choose the version they want

So this PR adds a new expandable button next to Play/Resume/Restart to
choose the version. It is a shortcut to the More->Choose Version dialog,
but also serves as a more prominent indicator that there are multiple
versions. The button is only shown if there are 2+ versions to choose
from.

Also adds displaying the bitrate in the choose version dialog just in
case there are identically named versions with the same resolution.

Dev note: I also added some docs for classes & functions related to
choosing a version

### Related issues
Related to #652

### Testing
Emulator

## Screenshots
<img width="1280" height="720" alt="choose_version_button_unfocused
Large"
src="https://github.com/user-attachments/assets/4f6051ef-b6ae-4fad-9d54-3840523350ed"
/>

<img width="1280" height="720" alt="choose_version_button Large"
src="https://github.com/user-attachments/assets/0e8412bc-2d18-4c6c-987a-2f067363ad2c"
/>

## AI or LLM usage
None
2026-06-07 07:15:30 -04:00
Damontecres
99fab9a383
Release v1.0.1
Some checks failed
Create release / publish (push) Has been cancelled
2026-06-05 12:30:39 -04:00
Damontecres
2a002f3d11
Translations 2026-06-05 (#1526)
Updates translations as of 2026-06-05

Thanks to all of the contributors!

---------

Co-authored-by: streamyfinchris <streamyfinchris@noreply.codeberg.org>
Co-authored-by: SimonHung <simonhung@noreply.codeberg.org>
Co-authored-by: arcker95 <arcker95@noreply.codeberg.org>
Co-authored-by: TheMatrixan <thematrixan@noreply.codeberg.org>
Co-authored-by: tal_sarid <tal_sarid@noreply.codeberg.org>
Co-authored-by: pavel-miniutka <pavel-miniutka@noreply.codeberg.org>
Co-authored-by: nofkclue <nofkclue@noreply.codeberg.org>
Co-authored-by: ymir <ymir@noreply.codeberg.org>
Co-authored-by: American_Jesus <american_jesus@noreply.codeberg.org>
Co-authored-by: Wit Krittawit <krittawit.st@gmail.com>
Co-authored-by: North-DaCoder <north-dacoder@noreply.codeberg.org>
2026-06-05 11:43:23 -04:00
North-DaCoder
01081b1985
Translated using Weblate (German)
Currently translated at 100.0% (575 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-06-05 14:01:15 +00:00
Wit Krittawit
be1b0c0a35
Translated using Weblate (Thai)
Currently translated at 96.8% (557 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/th/
2026-06-05 14:01:15 +00:00
American_Jesus
33dd630a59
Translated using Weblate (Portuguese)
Currently translated at 100.0% (575 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-06-05 14:01:15 +00:00
ymir
8a5988b95e
Translated using Weblate (French)
Currently translated at 100.0% (575 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-06-05 14:01:14 +00:00
nofkclue
9e15db4143
Translated using Weblate (German)
Currently translated at 99.8% (574 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-06-05 14:01:14 +00:00
pavel-miniutka
ac2e1e8f64
Translated using Weblate (Belarusian)
Currently translated at 1.5% (9 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/be/
2026-06-05 14:01:14 +00:00
tal_sarid
5cc9d3697d
Translated using Weblate (Hebrew)
Currently translated at 100.0% (575 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-06-05 14:01:14 +00:00
TheMatrixan
5fb207ce08
Translated using Weblate (Polish)
Currently translated at 100.0% (575 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-06-05 14:01:14 +00:00
arcker95
5375c279ab
Translated using Weblate (Italian)
Currently translated at 100.0% (575 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-06-05 14:01:14 +00:00
SimonHung
0035367c83
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (575 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-06-05 14:01:14 +00:00
streamyfinchris
7bd555aaa7
Translated using Weblate (Swedish)
Currently translated at 86.6% (498 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-06-05 14:01:14 +00:00
Damontecres
2cc25d068d
Release v1.0.0 2026-06-05 14:01:14 +00: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
Justin Caveda
0842c35e34
Fix combined search results losing keyboard focus when typing (#1524)
## Description
Minor tweak to prevent the keyboard from losing focus when typing search
queries when "combine search results" is enabled

### Related issues
Fixes #1523

### Testing
Emulator only

## Screenshots
N/A

## AI or LLM usage
None

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-06-05 09:54:06 -04:00
Damontecres
a0869ca6e4
Several minor fixes (#1505)
## Description
Collecting some minor fixes together:

- Adds error handling when submitting a discover/seerr request,
previously there was no visual feedback if the request failed
- Fix MpvPlayer reporting that it was buffering forever
- Fix discover results row on regular search page not being clickable
- Fix focus loss when returning to movie details after playback advanced
enough to switch the button from Play to Resume
- Fix some focus issues when returning to the TV guide page

### Related issues
Related to #1503 for MPV
Fixes #1500
Fixes #1508
Fixes #1501

### Testing
Emulator & nvidia shield

## Screenshots
N/A

## AI or LLM usage
None
2026-06-03 20:47:07 -04:00
Damontecres
a1b372667b
Fix highlighting the wrong 'more' nav drawer item (#1515)
## Description
Fixes an issue where the wrong nav drawer item would be highlighted in
the "More" section

Previously, displaying the discover nav item was partially handled in
the view model state and partially within the UI code which caused this
bug. This PR moves it all into the view model state.

### Related issues
N/A

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-06-03 20:46:57 -04:00
Damontecres
471dbd62db
Fixes a crash during playback (#1514)
## Description
Fixes a crash that could occur during the beginning of playback

Also adds better handling for clicking next extremely fast

### Technical details

The playlist objects would be created on the IO thread and it had a
`mutableIntStateOf` to track the current index. This meant that there is
a race conditions for the IO thread to commit the snapshot for the main
thread to see them. If the main thread is faster, it will crash.

To fix that, this PR moves the mutable state in `Playlist` into the
`PlaybackState` flow in the view model. This enures both the background
threads and re-compositions have the right state.

### Related issues
Fixes #1509 

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-06-03 12:41:13 -04:00
Damontecres
5ae452155e
Fix showing live tv programs on home page (#1504)
## Description
Fixes the inconsistent live tv programs shown on the home page by using
a different API endpoint & filtering.

Also some UI fixes:
- Use the "Watch live" string consistently for the row instead of it
sometimes switching to "Live TV"
- Adjust the "Ends at" in the home page header to show the actual end
time
- Show the channel name in "quick details"
- Show season & episode number, if applicable, in "quick details"

### Related issues
Fixes #1080

### Testing
Nvidia shield w/ fake EPG data

## Screenshots
N/A, just UI fixes, nothing new

## AI or LLM usage
None
2026-06-03 12:40:53 -04:00
Damontecres
6c64470c28
Only show view more card if row has more items (#1493)
## Description
Only show the view more card at the end of row if there are actually
more items (or at the limit).

Also reduces the size of the arrow a bit

### Related issues
Related to #1392 

### Testing
Emulator with different max items setting

## Screenshots
N/A

## AI or LLM usage
None
2026-05-30 17:41:44 -04:00
Damontecres
68836bb834
Fix click/long click issues on some view more grid pages (#1486)
## Description
On a library recommended tab, if you clicked View More, clicking a card
on the grid would directly play the item instead of the regular
behavior.

This PR fixes that and simplifies the code required to support simple
context menus.

### Related issues
Fixes #1480

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-30 17:41:27 -04:00
Damontecres
7660c53e3a
Don't show home row w/ 404 Not Found (#1487)
## Description
If a home page row returns 404 Not Found, treat it as empty and don't
show it.

It will still show as an error on home row settings, since this page
doesn't hide empty rows.

Also fixes a bug where you can't scroll up if there are 3+ empty rows
before the current row.

### Related issues
Closes #1469

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-30 16:56:52 -04:00
Damontecres
273a6138c6
Add more sort options to different types (#1488)
## Description
Adds more sorting options, notably premier date & ratings, to different
media types.

### Related issues
Closes #1417
Closes #1429
Replaces #1420

### Testing
Emulator on movie genre's items and music video libraries

## Screenshots
N/A

## AI or LLM usage
None
2026-05-30 16:56:33 -04:00
Damontecres
81e5f31401
Workaround for resource shrinking w/ split ABIs
Some checks failed
Create release / publish (push) Has been cancelled
2026-05-27 13:56:18 -04:00
Damontecres
30837d0a9a
Release v1.0.0 2026-05-27 13:05:32 -04:00
Damontecres
1da72c5df2
Translations as of 2026-05-27 (#1471)
Updates translations as of 2026-05-27

Thank you to all of the contributors!
2026-05-27 13:05:18 -04:00
Damontecres
967295fd5e
Fix linting 2026-05-27 12:56:46 -04:00
Outbreak2096
428f58b1e3
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (575 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-05-27 12:53:18 -04:00
idezentas
64d6579cae
Translated using Weblate (Turkish)
Currently translated at 100.0% (575 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-05-27 12:53:18 -04:00
Priit Jõerüüt
40b080ce32
Translated using Weblate (Estonian)
Currently translated at 100.0% (575 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-05-27 12:53:18 -04:00
danpergal84
696af7b39d
Translated using Weblate (Spanish)
Currently translated at 100.0% (575 of 575 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-05-27 12:53:18 -04:00
thotranhuu99
0dbf172742
Added translation using Weblate (Vietnamese) 2026-05-27 12:53:18 -04:00
TheMatrixan
c5fc26bbff
Translated using Weblate (Polish)
Currently translated at 100.0% (573 of 573 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-05-27 12:53:17 -04:00
opakholis
445ed91275
Translated using Weblate (Indonesian)
Currently translated at 100.0% (573 of 573 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-05-27 12:53:17 -04:00
danpergal84
fb8b516239
Translated using Weblate (Spanish)
Currently translated at 100.0% (573 of 573 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-05-27 12:53:17 -04:00
JoschiGrey
caa0e769da
Translated using Weblate (German)
Currently translated at 99.8% (572 of 573 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-05-27 12:53:17 -04:00
idezentas
a29dcd2ea0
Translated using Weblate (Turkish)
Currently translated at 100.0% (573 of 573 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-05-27 12:53:17 -04:00
Outbreak2096
8271d7a486
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (573 of 573 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-05-27 12:53:17 -04:00
TheMatrixan
4ac2024318
Translated using Weblate (Polish)
Currently translated at 100.0% (573 of 573 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-05-27 12:53:17 -04:00
SimonHung
08a0f3f247
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (573 of 573 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-05-27 12:53:17 -04:00
ymir
e6e5502cdc
Translated using Weblate (French)
Currently translated at 100.0% (573 of 573 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-05-27 12:53:17 -04:00
Priit Jõerüüt
f2b87c9e36
Translated using Weblate (Estonian)
Currently translated at 100.0% (573 of 573 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-05-27 12:53:16 -04:00
nofkclue
8e124846a3
Translated using Weblate (German)
Currently translated at 99.6% (571 of 573 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-05-27 12:53:16 -04:00
Priit Jõerüüt
99560439ad
Translated using Weblate (Estonian)
Currently translated at 100.0% (570 of 570 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-05-27 12:53:16 -04:00
danpergal84
8618fbc6e6
Translated using Weblate (Spanish)
Currently translated at 99.8% (569 of 570 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-05-27 12:53:16 -04:00
idezentas
9d6197671e
Translated using Weblate (Turkish)
Currently translated at 100.0% (570 of 570 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-05-27 12:53:16 -04:00
SimonHung
dcb71a48fd
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (569 of 569 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-05-27 12:53:16 -04:00
TheMatrixan
3f5ffdb865
Translated using Weblate (Polish)
Currently translated at 100.0% (569 of 569 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-05-27 12:53:16 -04:00
idezentas
96e670cc17
Translated using Weblate (Turkish)
Currently translated at 100.0% (569 of 569 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-05-27 12:53:16 -04:00
Outbreak2096
5543a78421
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (569 of 569 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-05-27 12:53:16 -04:00
Priit Jõerüüt
4580b2598a
Translated using Weblate (Estonian)
Currently translated at 100.0% (569 of 569 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-05-27 12:53:16 -04:00
danpergal84
8f6ce83cd3
Translated using Weblate (Spanish)
Currently translated at 99.8% (568 of 569 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-05-27 12:53:15 -04:00
Timer
25bf0ee574
Translated using Weblate (German)
Currently translated at 98.2% (559 of 569 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-05-27 12:53:15 -04:00
mashu
da685142a8
Translated using Weblate (Serbian)
Currently translated at 1.2% (7 of 566 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sr/
2026-05-27 12:53:15 -04:00
idezentas
e64ab11a2e
Translated using Weblate (Turkish)
Currently translated at 100.0% (566 of 566 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-05-27 12:53:15 -04:00
Outbreak2096
8c20c57595
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (566 of 566 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-05-27 12:53:15 -04:00
Geekice
debc810cd3
Translated using Weblate (Italian)
Currently translated at 99.4% (563 of 566 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-05-27 12:53:15 -04:00
Priit Jõerüüt
19912b9e0c
Translated using Weblate (Estonian)
Currently translated at 100.0% (566 of 566 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-05-27 12:53:15 -04:00
danpergal84
07bddc5efa
Translated using Weblate (Spanish)
Currently translated at 100.0% (566 of 566 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-05-27 12:53:15 -04:00
Priit Jõerüüt
e352cce894
Translated using Weblate (Estonian)
Currently translated at 100.0% (560 of 560 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-05-27 12:53:15 -04:00
pavel-miniutka
88b569c156
Added translation using Weblate (Belarusian) 2026-05-27 12:53:14 -04:00
Outbreak2096
5380bc18bc
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (560 of 560 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-05-27 12:53:14 -04:00
danpergal84
a438733af9
Translated using Weblate (Spanish)
Currently translated at 100.0% (560 of 560 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-05-27 12:53:14 -04:00
ymir
a09f0ffabd
Translated using Weblate (French)
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-05-27 12:53:14 -04:00
opakholis
0bdbd2e84d
Translated using Weblate (Indonesian)
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-05-27 12:53:14 -04:00
American_Jesus
c6729df112
Translated using Weblate (Portuguese)
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-05-27 12:53:14 -04:00
Wit Krittawit
a4789fcaca
Translated using Weblate (Thai)
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/th/
2026-05-27 12:53:14 -04:00
nofkclue
0cf1ac4bb4
Translated using Weblate (German)
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-05-27 12:53:14 -04:00
mashu
a4f393bcd9
Added translation using Weblate (Serbian) 2026-05-27 12:53:14 -04:00
Wit Krittawit
d36bb96b69
Translated using Weblate (Thai)
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/th/
2026-05-27 12:53:14 -04:00
TheMatrixan
b57ec267e4
Translated using Weblate (Polish)
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-05-27 12:53:13 -04:00
idezentas
b5d9679a10
Translated using Weblate (Turkish)
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-05-27 12:53:13 -04:00
Outbreak2096
0d2321a2f1
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-05-27 12:53:13 -04:00
TheMatrixan
8f0da522d0
Translated using Weblate (Polish)
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-05-27 12:53:13 -04:00
tal_sarid
d9738a0cee
Translated using Weblate (Hebrew)
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-05-27 12:53:13 -04:00
lednurb
9de7102896
Translated using Weblate (Dutch)
Currently translated at 89.0% (496 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-05-27 12:53:13 -04:00
SimonHung
bd8dae1dae
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-05-27 12:53:13 -04:00
Priit Jõerüüt
b211dc37d9
Translated using Weblate (Estonian)
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-05-27 12:53:13 -04:00
danpergal84
c4e855b180
Translated using Weblate (Spanish)
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-05-27 12:53:13 -04:00
North-DaCoder
371809096a
Translated using Weblate (German)
Currently translated at 100.0% (557 of 557 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-05-27 12:53:13 -04:00
ppskj178
e804e4a923
Added translation using Weblate (Korean) 2026-05-27 12:53:12 -04:00
idezentas
a02e8a77f1
Translated using Weblate (Turkish)
Currently translated at 100.0% (553 of 553 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-05-27 12:53:12 -04:00
Outbreak2096
d20aa08937
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (553 of 553 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-05-27 12:53:12 -04:00
SimonHung
5e0ecc6101
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (553 of 553 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-05-27 12:53:12 -04:00
danpergal84
916f6afe07
Translated using Weblate (Spanish)
Currently translated at 100.0% (553 of 553 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-05-27 12:53:12 -04:00
arcker95
3bb6bdd825
Translated using Weblate (Italian)
Currently translated at 100.0% (553 of 553 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-05-27 12:53:12 -04:00
Priit Jõerüüt
555d2741ca
Translated using Weblate (Estonian)
Currently translated at 100.0% (553 of 553 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-05-27 12:53:12 -04:00
Damontecres
7bbbed877d
Update readme 2026-05-27 12:38:33 -04:00
Damontecres
33161d4d6d
Several bug fixes & minor UI changes (#1466)
## Description
### Bug fixes
- Fixes a race condition crash when configuring subtitle styling before
libmpv is initialized
- Save the clicked item position on grid pages (#1428 & #1462)
- Fix querying the server every 3 seconds for transcode reason if the
video is direct playing


### UI changes
- Makes the title on grid pages slightly smaller
- Makes adjustments to the song list item on now playing so that no
space is used if there's no artist
- Use settings icon instead of triple dots on now playing page

### Related issues
See above

### Testing
Emulator, nvidia shield & google streamer

## Screenshots
### Song list item
Before & after
<img width="199" height="119" alt="image"
src="https://github.com/user-attachments/assets/7fd3636a-d1e4-4b1f-8379-8599068d6309"
/>


## AI or LLM usage
None
2026-05-26 18:36:56 -04:00
Damontecres
8ea6bbbfca
Improvements to home page customization (#1458)
## Description
Adds a few enhancements to home page customization:
- Add option to combined continue watching & next up to split it
- Add option to continue watching and next up rows to combine them
- Add option for sorting playlist or collections
- Show suggestions (recently updated) when searching for a playlist or
collection to add
- Better handling for loading vs unsupported types for suggestions

Also, the old toggle for combining continue watching & next up in
advanced settings is removed. Now by default the rows will be combined
unless changed in the home page customization settings.

### Related issues
Closes #340
Closes #1383
Closes #1350

### Testing
Just on the emulator so far

## Screenshots
N/A

## AI or LLM usage
None
2026-05-26 13:30:00 -04:00
Damontecres
23856ffcf8
Fixes favorites page not loading (#1462)
## Description
- Fixes a bug introduced in #1453 which broke the favorite page
- Follow up to #1428 to refactor the collection folder code to hoist
state up another level
- Fixes resuming playback from series details play button

### Related issues
Fixes #1313
Fixes #1461
Follow up to #1453 & #1428 

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-25 20:05:44 -04:00
Damontecres
510160aceb
Fix issue with local trailers, cinema mode, prerolls, & next up (#1460)
## Description
Fixes an issue where local trailers wouldn't play (or show in the queue)
if Cinema Mode is enabled and the server has a preroll/intro.

Also fix building the playback queue when "Show next up" is "Never". The
queue is shown, but not automatically played and next item dialog popup
is never shown. But to indicate playback is over, the controller show.

### Related issues
Fixes #1348
Fixes #1303

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-25 16:54:58 -04:00
Damontecres
9eff22e939
Fix several issues with right-to-left locales (#1404)
## Description
Fix several issues when the current locale is right-to-left

- Fixes the backdrop dynamic colors
- Fixes mirroring of playback controls, including seeking

### Related issues
Fixes #1397
Fixes #1398

### Testing
Emulator w/ forced RTL in developer options

## Screenshots
<img width="1280" height="720" alt="rtl_backdrop Large"
src="https://github.com/user-attachments/assets/3e3b14ec-32cc-42c0-a224-d1d859a55acd"
/>
<img width="1280" height="720" alt="rtl_controls Large"
src="https://github.com/user-attachments/assets/79983bb9-e1d0-4712-a5c6-da3d0721db96"
/>


## AI or LLM usage

None
2026-05-25 13:27:34 -04:00
Damontecres
656393fa1d
Prefer thumb art for movies for Watch Next (#1456)
## Description
Prefer using thumb image types for movies when adding to Watch Next
android OS channel. The primary/poster image will be used if there is no
thumb.

### Related issues
Fixes #1425 
Fixes #1403

### Testing
Emulator, verified a movie was showing its primary image, then ran this
PR

## Screenshots
N/A

## AI or LLM usage
None
2026-05-25 13:27:24 -04:00
Damontecres
ebcc2f324a
Several small fixes (#1455)
## Description
- Fix crash on license info page for release builds (#1437)
- Fix configuring which ratings to show by default (#1407)
- When re-ordering nav drawer items, ensure the one that was just moved
is always visible (#886 )
- Fix app losing focus when switching between list layout types (#1428)
- If the version doesn't have any additional commits, don't show the git
hash info (ie `v0.6.4-0-gabc1234` shows as `v0.6.4`)
- Adds tests for Version comparisons & toString

### Related issues
See above

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-25 13:27:04 -04:00
renovate[bot]
cb853b2db8
Update dependency com.mikepenz.aboutlibraries.plugin.android to v14.2.1 (#1447) 2026-05-25 08:28:25 -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
982d9e6fe8
Add list view for libraries (#1428)
## Description
Adds an option to display libraries in a list layout

The list can be regular showing 6-7 items or compact showing ~11 items.
Can also toggle showing the details on the left side. Movies, tv shows,
& albums have type-specific layouts and other types like videos, music
videos, etc use a generic one similar to movies.

Another change is that showing the backdrop is now a separate toggle
from showing details for both grid & list layouts.

Sorting, filtering, & jumping to a letter all work as expected!

### Related issues
Closes #1101

### Testing
Emulator mostly

## Screenshots
<details><summary>Click for screenshots</summary>
<p>

### TV shows list
<img width="1280" height="720" alt="series_list Large"
src="https://github.com/user-attachments/assets/f8f9b801-e537-4cdf-b992-f2f1259457fa"
/>

### TV shows compact list
<img width="1280" height="720" alt="series_list_compact Large"
src="https://github.com/user-attachments/assets/747e5929-90d7-4d59-a179-bc3210ac01b9"
/>

### Movie list
<img width="1280" height="720" alt="movie_list Large"
src="https://github.com/user-attachments/assets/ee3e1c6c-fb25-438a-a11e-522d30fbaabf"
/>

### Album list
<img width="1280" height="720" alt="music_list Large"
src="https://github.com/user-attachments/assets/9b1f59ec-c237-4ab2-9b51-6cb519f5327c"
/>


</p>
</details> 

## AI or LLM usage
None
2026-05-24 18:07:56 -04:00
Damontecres
8177700e23
Show loading indicator when video is buffering (#1441)
## Description
Shows a loading indicator and slightly dim the screen when playback is
buffering.

Showing the indicator is delayed in case the buffering doesn't take very
long.

Dev note: the changes to `MpvPlayer` need to be ported to
wholphin-extensions as well.

### Related issues
Closes #1411

### Testing
Emulator

## Screenshots

[buffering.webm](https://github.com/user-attachments/assets/dd848696-d6de-4f68-94e4-901bb5db1185)

## AI or LLM usage
None
2026-05-24 10:11:59 -04:00
Damontecres
903c45dfb8
Support configuring interface language per user (#1406)
## Description
Adds a setting to configure the app's language/locale. It is configured
per user. This settings has no impact on audio or subtitles language
choices.

Only languages Wholphin has translations for can be used. If you would
like to contribute new translations, check out
https://translate.codeberg.org/projects/wholphin/!

Dev notes: This doesn't exactly follow Android's recommendations because
the [guidelines are for
per-app](https://developer.android.com/guide/topics/resources/app-languages)
and not users within the app.

### Related issues
Closes #513
Closes #669

### Testing
- Emulator API 34
- Nvidia shield API 30
- Fire TV OS 7 API 28

## Screenshots
<img width="1280" height="720" alt="language Large"
src="https://github.com/user-attachments/assets/bf1c0962-8a36-4012-aed9-eab564fc1114"
/>

## AI or LLM usage
None
2026-05-24 09:55:08 -04:00
Damontecres
625cdcb4a3
Fix proguard for guava cache (#1445)
## Description
r8 optimizations seemingly cause guava's LocalCache to throw an
AssertationError when evicting an entry. This PR adds a broad keep rule
for the cache package.

The crash is reproducible on a release build with a TV Season that has
25+ episodes with people in them and scrolling through the episodes. It
will crash when focusing on the ~25th episode.

### Related issues
Related to #1437 

### Testing
Emulator & nvidia shield

## Screenshots
N/A

## AI or LLM usage
None
2026-05-23 21:21:51 -04:00
Damontecres
bfb49b80be
Enable minify optimizations for release builds (#1437)
## Description
Enables minify & resource shrinking for release builds. This should
improve overall performance a bit. It also decreases the apk size by
~15%.

Also updates AGP

### Related issues
N/A

### Testing
Emulator, but needs more testing before a final release

## Screenshots
N/A

## AI or LLM usage
None
2026-05-23 14:08:38 -04:00
Damontecres
086badf2e2
Fix theme song playing over video (#1442)
## Description
There was an issue if when opening a page with theme music and quickly
changing pages, usually playback, the theme music might play on the new
page. This is because the call to stop the theme music could potentially
happen _before_ the song was loaded or had started playing.

This PR fixes this race condition by tracking the job that's fetching
the theme song and by adding a mutex to ensure the play function
completes before the stop function can begin.

### Related issues
Fixes #938

### Testing
Emulator w/ fake delay to increase the odds of the issue occurring

## Screenshots
N/A

## AI or LLM usage
None
2026-05-23 13:38:59 -04:00
Damontecres
cb704991f0
Update dependencies (#1443)
## Description
Manually update dependencies because #1408 includes the `tv-material`
update, see #1394

### Related issues
Replaces #1408
2026-05-23 13:36:40 -04:00
Damontecres
c68ab33f38
Filter out future content for recently released rows (#1436)
## Description
Don't show future content (episodes, etc) on recently released rows in
recommended tabs or home page rows.

### Related issues
Closes #262
Replaces #1262

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
The original PR #1262 author used claude. Some of that code that was
retained in this PR was reviewed & tested.

---------

Co-authored-by: ADT95 <turner7@outlook.com>
2026-05-22 15:48:11 -04:00
Justin Caveda
df2dd3e7ca
Fix: suggestions get stuck on loading for hours/days (#1364)
## Description

This should fix the issue where suggestions would sometimes get stuck
loading. My best guess is that they were getting stuck because the app
thought it was still loading, even though the background job was just
waiting.

This should fix that by making each library kick off its own suggestions
job when needed.

  ### Related issues

  Fixes #1065

  ### Testing

Emulator and nvidia shield

  ## Screenshots

N/A no ui changes

  ## AI or LLM usage

Used codex to help diagnosis the bug. Also added unit tests and more
debug logging for the suggestions system using ai. Wrote the fix myself.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-05-22 12:19:26 -04:00
Damontecres
c3ba14557c
Search fixes & improvements (#1435)
## Description
Fixes showing the discover search result row

Also adds a Search tab to the discover page. You can already search
discover results from the regular search page, so the new tab is just
for convenience.

### Related issues
Related to #709 

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-22 11:54:38 -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
223d2d1f71
Show extras for music albums (#1426)
## Description
Adds the standard extras row to the album detail page if there are any
extras

I also tried to get Jellyfin to recognize artist extras, but it did not
work, so this just adds extras for albums

### Related issues
Closes #1409 

### Testing
Emulator

## Screenshots
N/A, same extras row

## AI or LLM usage
None
2026-05-21 15:31:43 -04:00
JustinZeus
fa914845f4
Add option to hide the voice search button (#1423)
## Description

Adds a "Show voice search button" toggle to the search page's view
options dialog, alongside the existing "Combined search results" toggle.
When disabled, the voice search button is hidden from the search row,
taking it out of the D-pad focus path.

The new field `show_voice_search_button` is added to `SearchPreferences`
(default `true`). The serializer applies the default for new installs,
and a new entry in `AppUpgradeHandler` sets it to `true` for users
upgrading from earlier versions so existing behaviour is preserved.

### Related issues

Discussion: https://github.com/damontecres/Wholphin/discussions/1419

### Testing

Tested on an Android TV (1080p) AVD running API 34:

- Toggle on (default): voice search button visible at the top of the
search row.
- Toggle off: button disappears; only the search text field remains.
- Toggle back on: button reappears.
- Upgrade handler verified by spoofing `version.current.name` to an
older value in SharedPreferences and force-restarting the app; after
`needUpgrade()` fired, the toggle was on as expected.

The upgrade-handler entry currently uses `0.6.4-8-g0` (based on current
`main` HEAD); happy to bump this if you'd prefer a different target
version on merge.

## Screenshots

<img width="1920" height="1080" alt="pr1-01-home"
src="https://github.com/user-attachments/assets/0d5cdad0-5547-44f3-b4a5-5d15f71dddc0"
/>
<img width="1920" height="1080" alt="pr1-02-toggled-off"
src="https://github.com/user-attachments/assets/1e574229-0838-487d-b943-83d394a8193d"
/>

## AI or LLM usage

I used Claude to scaffold the proto/serializer/upgrade-handler/dialog
changes, mirroring the existing `combined_search_results` pattern. I
reviewed each diff before committing and tested the toggle manually on
the emulator, including the upgrade-handler simulation.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-05-21 15:31:17 -04:00
renovate[bot]
3880e562f6
Update dependency com.google.devtools.ksp to v2.3.8 (#1385) 2026-05-21 13:35:59 -04:00
renovate[bot]
f6ec54fa8c
Update Gradle to v9.5.1 (#1326) 2026-05-21 13:35:26 -04:00
JustinZeus
c82d36794f
Fix bottom padding in playback controls (#1422)
Fixes #1396.

The else branch of `when (nextState)` in PlaybackController added a 32dp
Spacer that left empty space below the controls when there were no
chapters or queue.

Possible follow-up: Pentaphon's idea of a "similar items" row in that
empty slot. Happy to put up a separate PR if useful.

## Testing

Verified on an Android TV (1080p) AVD running API 34 by playing a movie
with no chapters and no next-up item. With the fix, the bottom of the
control overlay sits the expected 8dp from the screen edge (the existing
parent padding). Also rebuilt without the fix and compared back-to-back
to confirm the Spacer was the cause.

The CHAPTERS and QUEUE branches were not modified.

## Screenshot
<img width="1920" height="1080" alt="case1-controls"
src="https://github.com/user-attachments/assets/18f50a87-a373-4f48-a9d4-f3c2a5cb2c00"
/>

## AI or LLM usage

I used Claude to help identify the file containing the bug and to
suggest the fix. I reviewed the diff before committing.
2026-05-21 12:43:49 -04:00
Damontecres
366483599c
Customize which ratings to display (#1407)
## Description
Adds a setting, Settings->Advanced->"Customize which ratings to
display", which allows for toggling parental, community, or critic
ratings to be displayed in the quick details.

This doesn't apply to discover yet.

### Related issues
Related to #1020

### Testing
Emulator

## Screenshots

### Just community rating
<img width="1280" height="720" alt="ratings Large"
src="https://github.com/user-attachments/assets/d6f9ad8a-6cc5-4680-9d2f-66d16a44b41e"
/>


## AI or LLM usage
None
2026-05-20 12:40:58 -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
1463480e26
Update media3 to 1.10.1 (#1414)
Updates media3 to v1.10.1 & wholphin-extensions to v0.1.3

Fixes #1189
Fixes #1399
Replaces #1380
2026-05-19 19:02:53 -04:00
Damontecres
da7f7048e2
Enhancements to live TV guide/EPG (#1393)
## Description
This PR has some UI/UX updates for live tv epg/guide.

- Show program images, if provided by the EPG source
- Show channel name under logo
- Show & set favorite channels
- Adjusts the header to match the rest of the app (font, padding, etc)

Additionally, navigation around the guide has been rewritten. It is more
efficient and feels a bit more smooth to scroll around.

### Related issues
Closes #529

### Testing
Emulator mostly

## Screenshots
<img width="1280" height="720" alt="epg_new Large"
src="https://github.com/user-attachments/assets/c53c01be-9209-4c59-82cb-47f268811d3e"
/>


## AI or LLM usage
None
2026-05-19 14:11:55 -04:00
Damontecres
87a109c6b6
Add option for click to play continue watching/next up rows (#1405)
## Description
Adds an option under Settings->Customize home page->Settings, "Continue
watching/Next up click behavior":
- When disabled (the default), clicking goes to the item's details page
- When enabled, clicking starts playback for the item from its resume
position

This only applies to the continue watching, next up, or combined rows.
All other rows go to the details page no matter what.

### Related issues
Closes #1007

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-19 14:11:40 -04:00
Damontecres
59ccce9d54
Fix small UI issue with long stream labels (#1388)
## Description
Fixes a small UI glitch when the subtitle label is too long, it would
slightly increase the high of the labels. This is especially noticeable
on series details if some episodes have the long label and some don't
because the episode row will bounce up and down a few pixels.

Also moves the labels closer together and changes "Disabled" to "None"
when subtitles are turned off.

### Related issues
None

### Testing
Emulator & compose previews

## Screenshots
### Before
The subtitle label is slightly taller than the others
<img width="528" height="36" alt="image"
src="https://github.com/user-attachments/assets/32d322bc-11cc-4238-840c-03ae41b069d3"
/>


### After
The ellipse doesn't look great, but there's limited space and looks
better than being cut off.
<img width="510" height="37" alt="image"
src="https://github.com/user-attachments/assets/40af707c-3121-40c0-a978-60781d9704d6"
/>


## AI or LLM usage
None
2026-05-16 11:35:21 -04:00
Damontecres
f4bdb8446e
View more from home page & recommended rows (#1392)
## Description
Adds a card at the end of the rows on the home page and recommended tabs
to "View More" which goes to a grid of the items in the row.

The grid will endlessly scroll all of the available items except for a
few cases where the first 1000 are shown because the server does not
(yet) support pagination: genres, studios, people (added in v12), and
latest grouped tv shows.

Dev notes: This PR also rewrites the `RecommendedViewModel` and
consolidates the previous multiple view models into one.

### Related issues
Closes #1371
Closes #1035
Similar to #1198

### Testing
Emulator

## Screenshots
N/A, see #1198

## AI or LLM usage
None
2026-05-16 11:35:11 -04:00
Justin Caveda
98ffaf51ac
Search page improvements (#709)
## Description

Improves how search results are displayed:

- Reorders search categories to Movies → TV Shows → Episodes →
Collections, surfacing the most commonly searched content types at the
top.
- Adds a new "Combined search results" preference in Advanced settings →
Search. When enabled, shows all results in a single list instead of
separate categories.
- When Seerr is active, adds Library/Discover tabs to switch between
local library results and Seerr discovery results.
- Hides empty result categories instead of showing "No results"
placeholders.

### Screenshots

<img width="1935" height="1100" alt="Screenshot_20260116_113656"
src="https://github.com/user-attachments/assets/1e29aa3a-3ba2-4f86-ab61-fed683490474"
/>

<img width="1934" height="1095" alt="image"
src="https://github.com/user-attachments/assets/8b93fa38-d4c9-4e72-a997-caa82b4949f0"
/>

<img width="1921" height="1094" alt="image"
src="https://github.com/user-attachments/assets/82aed1a5-810c-413f-a266-6d78490a1534"
/>

### AI/LLM usage
Code auditing and PR description drafting with Claude Code. Also used
Claude Code for the regex.
2026-05-13 10:02:33 -04:00
Damontecres
f364ef9708
Release v0.6.4
Some checks failed
Create release / publish (push) Has been cancelled
2026-05-12 13:13:19 -04:00
Damontecres
1614ef7158
Translations 2026-05-11 (#1374)
Update translations as of 2025-05-11

Thank you to all of the contributors!
2026-05-12 13:11:33 -04:00
dudecuda
96f94feac2
libass-android lifecycle issue: Fixes #1049 (much of it) (#1362)
Fixed a bug with libass integration where the background thread
responsible for drawing the ASS subtitles (AssRenderThread) has been
shut down, but ExoPlayer is still actively running and trying to push
video time updates to it. Added fallback for software rendering on older
android versions that can have broken OpenGl implementation.This should
address much of issue #1049.

## Description
Essentially, debug logs showed repeated:
3145 3920 W MessageQueue: Handler (android.os.Handler) {4ba3c72} sending
message to a Handler on a dead thread
3145 3920 W MessageQueue: java.lang.IllegalStateException: Handler
(android.os.Handler) {4ba3c72} sending message to a Handler on a dead
thread

Essentially this was a lifecycle bug where the libass-android thread was
created, but was detaching from the window, shutting down when it does
so. It was still registered to ExoPlayer, but since there was no thread
to send to, it just repeatedly kept failing, never spinning back up to
display any subtitles. It seems just about any ui change can cause this
detachment, but in this case it seems it was right from the moment the
loading spinner shows up (making libass-android fail before video starts
playing).

Fixed by ensuring TextureView's visibility wasn't changed to invisible,
which ends up destroying the canvas that libass-android attaches itself
to. Hid the canvas using graphicsLayer (so the user can't see it, but
it's still alive and active). Also changed resizing logic to prevent
Android from seeing a 0x0 TextureView as "destroyed".

Since I discovered in testing how terribly my TV's OpenGl was handling
this, I added an additional fallback for Android versions < 9. Older
TV's can fallback to software rendering when using libass-android.

### Related issues
#### Issue #1049 : 
**Fixes:**
- the issue where libass-android (when enabled) fails to load any
subtitles in ExoPlayer DirectPlay.

**Not Fixed from issue #1049 :**
- Issues around accented characters.
- Any issues surrounding MPV (I have no way of testing, MPV never worked
on any player for me)
- Fallback mode (libass-android disabled) where subtitles aren't
rendering properly

**Added:**
- While testing, discovered older Android versions have trouble with
reliable OpenGl rendering. Thus I added a fallback for older Android
versions to use libass-android in software rendering mode
(AssRenderType.OVERLAY_CANVAS)

### Testing
- Tested and confirmed flawlessly to work on Android 9 TCL Smart TV
(AssRenderType.OVERLAY_CANVAS)
- Tested on Android 14 Chromecast (AssRenderType.OVERLAY_OPEN_GL).
Slightly less perfect (font not always a match, less fancy shadows) but
most ASS/SSA effects work well. May be worth testing other AssRenderType
options to compare features and performance on other machines.

## AI or LLM usage
Google Gemini Pro in tracing down the cause and fine-tuning the fixes.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-05-11 17:49:08 -04:00
Damontecres
8b9b0ddec8
Fix two home row focus bugs (#1376)
## Description
1. Fixes when moving from nav drawer to home page content, focus would
sometimes move up a row
2. Fixes when moving up/down between rows, focus would sometimes move to
the later card (usually the last visible)

The second above is fixed by downgrading `tv-material` to
`1.0.1-alpha01`. Switching to
[`1.0.1-beta01`](https://developer.android.com/jetpack/androidx/releases/tv#tv-material-1.1.0-beta01)
or newer causes the bug. Not clear if its a bug in the library or
Wholphin though.

### Related issues
Fixes #1359

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-11 17:48:38 -04:00
Damontecres
4928c1156d
Fix cleaning up APKs on debug builds (#1373)
## Description
Directly use "Wholphin" when searching for older APKs to clean up.

On debug builds, the `app_name` string is "Wholphin (Debug)" but the APK
file names is always "Wholphin.apk", so I think the debug build won't
clean them up.

### Related issues
Fixes #1368

### Testing
Testing downloading a few times without this change, applied the change,
and clean up worked

## Screenshots
N/A

## AI or LLM usage
None
2026-05-11 11:12:05 -04:00
tal_sarid
9c41a7200e
Translated using Weblate (Hebrew)
Currently translated at 100.0% (552 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-05-11 14:27:12 +00:00
tal_sarid
278dbe3774
Translated using Weblate (Hebrew)
Currently translated at 100.0% (552 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-05-11 14:27:12 +00:00
tal_sarid
af39b9f596
Translated using Weblate (Hebrew)
Currently translated at 99.2% (548 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-05-11 14:27:12 +00:00
Codeberg Translate
21fd8e0a14
Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/
2026-05-11 14:27:12 +00:00
tal_sarid
aab0b62e83
Translated using Weblate (Hebrew)
Currently translated at 46.5% (257 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-05-11 14:27:12 +00:00
opakholis
c13d2f4129
Translated using Weblate (Indonesian)
Currently translated at 100.0% (552 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-05-11 14:27:12 +00:00
idezentas
1bf72656c8
Translated using Weblate (Turkish)
Currently translated at 100.0% (552 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-05-11 14:27:12 +00:00
tal_sarid
47f728f8b1
Translated using Weblate (Hebrew)
Currently translated at 44.0% (243 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-05-11 14:27:11 +00:00
tal_sarid
73cb8c60fe
Translated using Weblate (Hebrew)
Currently translated at 30.6% (169 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-05-11 14:27:11 +00:00
RafaelSoaresP
74698d2d4e
Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (552 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt_BR/
2026-05-11 14:27:11 +00:00
arcker95
fb3f738f50
Translated using Weblate (Italian)
Currently translated at 100.0% (552 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-05-11 14:27:11 +00:00
TheMatrixan
3627d29d33
Translated using Weblate (Polish)
Currently translated at 100.0% (552 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-05-11 14:27:11 +00:00
American_Jesus
7d79bf3d3e
Translated using Weblate (Portuguese)
Currently translated at 100.0% (552 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-05-11 14:27:11 +00:00
Priit Jõerüüt
e8b3203817
Translated using Weblate (Estonian)
Currently translated at 100.0% (552 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-05-11 14:27:11 +00:00
North-DaCoder
95303ac2db
Translated using Weblate (German)
Currently translated at 100.0% (552 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-05-11 14:27:11 +00:00
Outbreak2096
80c4b9e7b7
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (552 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-05-11 14:27:11 +00:00
lednurb
75212e7148
Translated using Weblate (Dutch)
Currently translated at 89.6% (495 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-05-11 14:27:11 +00:00
SimonHung
cd058b2f37
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (552 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-05-11 14:27:11 +00:00
danpergal84
251b659ad3
Translated using Weblate (Spanish)
Currently translated at 100.0% (552 of 552 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-05-11 14:27:11 +00:00
TheMatrixan
c66b41d90d
Translated using Weblate (Polish)
Currently translated at 100.0% (550 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-05-11 14:27:11 +00:00
MrDog
04f7f1ac79
Translated using Weblate (Slovenian)
Currently translated at 86.5% (476 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sl/
2026-05-11 14:27:11 +00:00
randomsoftware
9767e3297e
Translated using Weblate (Arabic)
Currently translated at 29.6% (163 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ar/
2026-05-11 14:27:11 +00:00
SimonHung
b0db3ce483
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (550 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-05-11 14:27:11 +00:00
MrDog
dbe788e7e9
Translated using Weblate (Slovenian)
Currently translated at 42.3% (233 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sl/
2026-05-11 14:27:11 +00:00
RafaelSoaresP
aaca10fbea
Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (550 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt_BR/
2026-05-11 14:27:11 +00:00
Outbreak2096
9afc07e77c
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (550 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-05-11 14:27:11 +00:00
TheMatrixan
8b92fceeae
Translated using Weblate (Polish)
Currently translated at 100.0% (550 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-05-11 14:27:11 +00:00
n8llcaster
d7cbc1c926
Translated using Weblate (Slovak)
Currently translated at 94.5% (520 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-05-11 14:27:11 +00:00
American_Jesus
2cb44090f6
Translated using Weblate (Portuguese)
Currently translated at 100.0% (550 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-05-11 14:27:11 +00:00
North-DaCoder
235a782621
Translated using Weblate (German)
Currently translated at 100.0% (550 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-05-11 14:27:11 +00:00
renovate[bot]
9ddce95e57
Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-test to v1.11.0 (#1299) 2026-05-11 10:27:07 -04:00
renovate[bot]
59af13d29e
Update Dependencies to v14.2.0 (#1335) 2026-05-11 10:26:43 -04:00
Brian Gunter
08db2b0efc
Accept digits in the PIN field in addition to l/r/u/d (#1330)
## Description
This enhances the PIN entry system by allowing users to input digits
using the numbers 0-9. This is useful for those using a keyboard or
universal remote with a number pad.

### Testing
Tested on Nvidia Shield Pro and Onn 4k Pro

## Screenshots
<img width="3840" height="2160" alt="Screenshot_20260429_083041"
src="https://github.com/user-attachments/assets/9e603bda-f1d3-4268-a04c-b077297fdfac"
/>

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-05-11 10:23:02 -04:00
Damontecres
cb46aa4159
Rearrange playback controller buttons (#1366)
## Description
- Moves the audio track selection out of the menu as a button
- Combines the settings & more buttons into one dialog

All functionality works the same, this is just UI changes

### Related issues
Related to #528

### Testing
Emulator, it's just UI changes

## Screenshots

### Before
<img width="1280" height="720" alt="controller_before Large"
src="https://github.com/user-attachments/assets/40430408-2108-4eda-9f05-d5f8c22a8b01"
/>

### After
<img width="1280" height="720" alt="controller_update Large"
src="https://github.com/user-attachments/assets/d2623a7c-9c05-4b0f-ae57-fed1c37e6cbc"
/>


## AI or LLM usage
None
2026-05-11 10:22:27 -04:00
Damontecres
df71baccea
Fix remove series from next up (#1369)
## Description
Fixes the "Remove series from next up" context menu item not working at
all.

Looks like after the refactoring in #1302, I missed [adding back the
function
call](https://github.com/damontecres/Wholphin/pull/1302/changes#diff-440f67bfe7be2011418c2f90e3a8ae2af05795a5161edd1a8bda14bc3b962cdcL175).

### Related issues
Fixes #1363

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-10 18:00:09 -04:00
Damontecres
dbd17597b2
Show public users when switching users (#1358)
## Description
Show the public users (ie "Hide this user from login screens" is
unchecked) if the server has any. Previously, all users had to be
explicitly added by username or quick connect.

This PR also refactors the server & user pages to use modern state flows
and deletes some unused code.

### Related issues
Closes #600

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-10 08:50:08 -04:00
Damontecres
b23d30f94b
Slightly optimize subtitles view & workaround possible crash (#1334)
## Description
Instead of adding/removing the `SubtitleView` from composition
sometimes, instead toggle its visibility. This is technically slightly
more efficient. There's also a couple other minor tweaks that make the
subtitles style a little more efficient.

This should also help prevent crash caused by possible a race condition
in libass-android
(https://github.com/peerless2012/libass-android/issues/75).

### Related issues
I think this fixes #1322

### Testing
Emulator mostly

## Screenshots
N/A

## AI or LLM usage
None
2026-05-06 18:29:26 -04:00
Damontecres
45ab0713a9
Update AGP, Gradle, & other third party dependencies (#1356)
## Description
Just a lot of dependency bumps. There are no user facing changes.

Switches to the new Android gradle DSL now that the protobuf plugin is
compatible.

### Related issues
None

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-06 18:08:29 -04:00
Damontecres
8107a82a6b
Fixes/improves showing delete button (#1353)
## Description
Fixes the delete button not showing on the movie detail page. Also fixes
some edge cases where button may not appear when toggling on the media
management setting.

Adds the delete button to album & artist pages. Previously, it was only
in the context menu.

### Related issues
Fixes #1343

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-06 11:49:27 -04:00
Damontecres
461bad1ddd
Fix bug where changing subtitles during playback doesn't work (#1352)
## Description
Fixes an issue when changing subtitles during playback if you had
previously explicitly chosen subtitles for that item but not chosen an
audio track.

### Related issues
I don't think there is a specific issue for this

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-05-06 11:49:13 -04:00
Damontecres
6afcfb469b
Enable large heap (#1337)
## Description
This PR simply enables the `largeHeap` flag to request more memory from
the OS.

I am a bit embarrassed that I haven't added this before now, but I had
read that using `largeHeap` was a bandaid, not a solution. So I assumed
Wholphin was doing something incorrect memory-wise. But after crawling
through many, many heap dumps to analyze Wholphin's memory usage, and
after further research, enabling this does make sense for handling high
bitrate, high resolution videos. Furthermore, many other Jellyfin
clients and other video apps like Plex and Prime Video have this
enabled.

So since I am pretty confident that Wholphin doesn't have any memory
leaks, I feel good about enabling this.

### Related issues
I believe this will fix many of the bug reports about crashes during 4K
playback, but it's not a cure-all since it is up the device & OS to
allocate the max memory.
Fixes #333 
Fixes #826
Fixes #1189

### Testing
Shield 2019, Fire 4k Max

## Screenshots
N/A

## AI or LLM usage
None
2026-05-01 19:12:23 -04:00
Damontecres
d538b8d4f9
Preload grid card images & other minor optimizations (#1328)
## Description
Preload ~6 rows worth of images on library grid pages. So depending on
device CPU & memory, most cards do not show a placeholder image when
scrolling unless you press and hold up/down to scroll as fast as
possible.

There's a couple other minor optimizations on the grid code as well, but
nothing huge.

### Dev notes

This PR makes the `cacheWindow` for pre-rendering cards applicable to
their images. Previously, images would not be fetched until the card is
laid out within the window. This was to get the width/height to pass to
the server request. This PR instead calculates the card widths when the
grid itself is laid out and then passes this width into the cards'
composables.

### Related issues
Related to #1246

### Testing
Tested with emulator, shield, fire stick 4k max

## Screenshots
N/A

## AI or LLM usage
None
2026-04-29 19:45:57 -04:00
Damontecres
b94faceb2f
Choose backend to play media in context menu (#1325)
## Description
Adds a context menu option to choose the player backend or transcoding
to start playback. This allows for temporarily swapping to another
backend without having to updating the settings.

### Related issues
A couple comments have suggested this, but I don't think there's a
specific feature request

### Testing
Emulator

## Screenshots
<img width="1280" height="720" alt="play_with Large"
src="https://github.com/user-attachments/assets/2a75756f-5d7f-45f6-8ea6-a406f3d7f3e1"
/>


## AI or LLM usage
None
2026-04-29 19:45:37 -04:00
Damontecres
c718d903a4
Refactor context menu code (#1302)
## Description
This PR refactors most of the context menu dialog code moving more of
the display into a Composable. This then allows for embedding the
confirm delete or choose version sub-dialogs. This will allow for easier
future expansion of the context menu.

Now instead of the page using a combination of `DiaglogPopup` &
`buildMoreFor` functions, pages create a `ContextMenu` object and pass
it to a composable.

There's no significant user facing changes.

### Related issues
None

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-04-27 18:58:54 -04:00
Damontecres
a159526296
Release v0.6.3
Some checks failed
Create release / publish (push) Has been cancelled
2026-04-27 18:14:00 -04:00
Damontecres
cce5b9a762
Translation 2026-04-27 (#1321)
Update translations as of 2026-04-27

Thank you to all of the contributors!!
2026-04-27 18:13:40 -04:00
idezentas
a46cb36735
Translated using Weblate (Turkish)
Currently translated at 100.0% (550 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-04-26 21:53:03 +00:00
arcker95
4d81d31e8d
Translated using Weblate (Italian)
Currently translated at 100.0% (550 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-04-26 21:53:03 +00:00
Priit Jõerüüt
c616d58ce5
Translated using Weblate (Estonian)
Currently translated at 100.0% (550 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-04-26 21:53:03 +00:00
danpergal84
b2ecc2886f
Translated using Weblate (Spanish)
Currently translated at 100.0% (550 of 550 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-04-26 21:53:02 +00:00
MrDog
eeb2373507
Translated using Weblate (Slovenian)
Currently translated at 32.6% (179 of 549 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sl/
2026-04-26 21:53:02 +00:00
Outbreak2096
6c2769f84a
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (549 of 549 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-04-26 21:53:02 +00:00
SimonHung
dc56296082
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (549 of 549 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-04-26 21:53:02 +00:00
North-DaCoder
e82a8632fc
Translated using Weblate (German)
Currently translated at 100.0% (549 of 549 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-04-26 21:53:02 +00:00
idezentas
8ae7d7bd1a
Translated using Weblate (Turkish)
Currently translated at 100.0% (549 of 549 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-04-26 21:53:02 +00:00
SimonHung
a65f9ecd6c
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 99.8% (548 of 549 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-04-26 21:53:02 +00:00
danpergal84
ac85008a7b
Translated using Weblate (Spanish)
Currently translated at 100.0% (549 of 549 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-04-26 21:53:02 +00:00
zyplex
e71cb4aaae
Translated using Weblate (German)
Currently translated at 99.6% (547 of 549 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-04-26 21:53:02 +00:00
arcker95
d68a5dfb85
Translated using Weblate (Italian)
Currently translated at 100.0% (546 of 546 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-04-26 21:53:02 +00:00
MrDog
28047cceee
Translated using Weblate (Slovenian)
Currently translated at 22.8% (125 of 546 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sl/
2026-04-26 21:53:02 +00:00
arcker95
632cf6bae4
Translated using Weblate (Italian)
Currently translated at 99.6% (544 of 546 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-04-26 21:53:02 +00:00
MrDog
9bd10a9880
Translated using Weblate (Slovenian)
Currently translated at 20.8% (114 of 546 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sl/
2026-04-26 21:53:02 +00:00
Alan700
116b4516a2
Translated using Weblate (French)
Currently translated at 99.8% (545 of 546 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-04-26 21:53:02 +00:00
idezentas
4b5b18247d
Translated using Weblate (Turkish)
Currently translated at 100.0% (546 of 546 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-04-26 21:53:02 +00:00
Outbreak2096
ed5a6b3aaf
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (546 of 546 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-04-26 21:53:02 +00:00
TheMatrixan
23a45016eb
Translated using Weblate (Polish)
Currently translated at 100.0% (546 of 546 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-04-26 21:53:02 +00:00
SimonHung
dd0fda5573
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (546 of 546 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-04-26 21:53:02 +00:00
ymir
6e08f06527
Translated using Weblate (French)
Currently translated at 100.0% (546 of 546 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-04-26 21:53:02 +00:00
Priit Jõerüüt
591214fe94
Translated using Weblate (Estonian)
Currently translated at 100.0% (546 of 546 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-04-26 21:53:02 +00:00
danpergal84
90e932a804
Translated using Weblate (Spanish)
Currently translated at 100.0% (546 of 546 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-04-26 21:53:02 +00:00
North-DaCoder
346349910a
Translated using Weblate (German)
Currently translated at 100.0% (546 of 546 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-04-26 21:53:02 +00:00
Alan700
e6bc4e3c19
Translated using Weblate (French)
Currently translated at 100.0% (544 of 544 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-04-26 21:53:02 +00:00
Wit Krittawit
3210ad110e
Translated using Weblate (Thai)
Currently translated at 93.7% (510 of 544 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/th/
2026-04-26 21:53:01 +00:00
idezentas
72b956a1c4
Translated using Weblate (Turkish)
Currently translated at 100.0% (544 of 544 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-04-26 21:53:01 +00:00
danpergal84
e0b4cf17bb
Translated using Weblate (Spanish)
Currently translated at 100.0% (544 of 544 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-04-26 21:53:01 +00:00
North-DaCoder
73edbbc01d
Translated using Weblate (German)
Currently translated at 100.0% (544 of 544 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-04-26 21:53:01 +00:00
MrDog
504c74e0c6
Translated using Weblate (Slovenian)
Currently translated at 13.6% (74 of 544 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sl/
2026-04-26 21:53:01 +00:00
Priit Jõerüüt
2c0bf94f42
Translated using Weblate (Estonian)
Currently translated at 100.0% (544 of 544 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-04-26 21:53:01 +00:00
MrDog
6e6df53d0c
Added translation using Weblate (Slovenian) 2026-04-26 21:53:01 +00:00
Outbreak2096
371925aa72
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (544 of 544 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-04-26 21:53:01 +00:00
SimonHung
91a75606df
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (544 of 544 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-04-26 21:53:01 +00:00
Wit Krittawit
6fd7d4e1cb
Translated using Weblate (Thai)
Currently translated at 93.9% (510 of 543 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/th/
2026-04-26 21:53:01 +00:00
idezentas
ac9c49a7b3
Translated using Weblate (Turkish)
Currently translated at 99.8% (542 of 543 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-04-26 21:53:01 +00:00
Priit Jõerüüt
e06170919c
Translated using Weblate (Estonian)
Currently translated at 100.0% (543 of 543 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-04-26 21:53:01 +00:00
danpergal84
6d895042d8
Translated using Weblate (Spanish)
Currently translated at 100.0% (543 of 543 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-04-26 21:53:01 +00:00
jbrek
f42ed8bb09
Translated using Weblate (Thai)
Currently translated at 95.3% (517 of 542 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/th/
2026-04-26 21:53:01 +00:00
RafaelSoaresP
2d4ff6600e
Translated using Weblate (Portuguese (Brazil))
Currently translated at 91.5% (496 of 542 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt_BR/
2026-04-26 21:53:01 +00:00
oyvhov
6546d1d3fb
Translated using Weblate (Norwegian Nynorsk)
Currently translated at 61.6% (334 of 542 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nn/
2026-04-26 21:53:01 +00:00
Outbreak2096
1a8309767f
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (542 of 542 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-04-26 21:53:01 +00:00
arcker95
42515f3b79
Translated using Weblate (Italian)
Currently translated at 100.0% (542 of 542 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-04-26 21:53:01 +00:00
Alan700
eb4f42d215
Translated using Weblate (French)
Currently translated at 98.5% (534 of 542 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-04-26 21:53:01 +00:00
ymir
1939b95632
Translated using Weblate (French)
Currently translated at 98.5% (534 of 542 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-04-26 21:53:01 +00:00
danpergal84
55c717e201
Translated using Weblate (Spanish)
Currently translated at 100.0% (542 of 542 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-04-26 21:53:01 +00:00
American_Jesus
93ead0861d
Translated using Weblate (Portuguese)
Currently translated at 100.0% (538 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-04-26 21:53:01 +00:00
ℂ𝕠𝕠𝕠𝕝 (𝕘𝕚𝕥𝕙𝕦𝕓.𝕔𝕠𝕞/ℂ𝕠𝕠𝕠𝕝)
3d1b62ed40
Translated using Weblate (Latvian)
Currently translated at 45.3% (244 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/lv/
2026-04-26 21:53:00 +00:00
shift19
94712b8170
Translated using Weblate (Hungarian)
Currently translated at 61.1% (329 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2026-04-26 21:53:00 +00:00
lednurb
fd10ed9660
Translated using Weblate (Dutch)
Currently translated at 91.4% (492 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-04-26 21:53:00 +00:00
arcker95
3ba1fbc3b0
Translated using Weblate (Italian)
Currently translated at 100.0% (538 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-04-26 21:53:00 +00:00
opakholis
986589bae5
Translated using Weblate (Indonesian)
Currently translated at 100.0% (538 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-04-26 21:53:00 +00:00
TheMatrixan
2e40ac62af
Translated using Weblate (Polish)
Currently translated at 100.0% (538 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-04-26 21:53:00 +00:00
Chosnuk
3947f9ff26
Translated using Weblate (Polish)
Currently translated at 100.0% (538 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-04-26 21:53:00 +00:00
TheMatrixan
10a2519285
Translated using Weblate (Polish)
Currently translated at 100.0% (538 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-04-26 21:53:00 +00:00
true-seeker
ac98dd4a2b
Translated using Weblate (Russian)
Currently translated at 62.8% (338 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ru/
2026-04-26 21:53:00 +00:00
ymir
1d55073ccd
Translated using Weblate (French)
Currently translated at 100.0% (538 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-04-26 21:53:00 +00:00
North-DaCoder
f57954cf4f
Translated using Weblate (German)
Currently translated at 100.0% (538 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-04-26 21:53:00 +00:00
Damontecres
5571654a22
Show overlay for extra items (#1320)
## Description
Shows the overlay (watched, progress bar) for extras appearing as single
items. Grouped extras individually show the overlay when clicking into.
If all of the grouped are played, the group is show with the watched
indicator.

Also very slightly optimizations the UI by offloading the
title/subtitle/imageUrl to object creation time instead of when they're
shown on screen.

### Related issues
Closes #1311

### Testing
Shield testing with different extras

## Screenshots
N/A

## AI or LLM usage
None
2026-04-26 17:52:57 -04:00
Damontecres
8cf99900e5
Fix index error on home page refresh with empty rows (#1318)
## Description
Fixes an bug caused when the home page has an empty row before the last
row and there are non-empty rows later, an `IndexOutOfBoundsException`
would be thrown.

Also removes the loading indicator if an unknown error occurs during a
refresh.

### Related issues
Fixes #1314

### Testing
Emulator & shield

## Screenshots
N/A

## AI or LLM usage
None
2026-04-26 16:09:19 -04:00
Damontecres
edb37f5d69
Add support for cinema mode (#1305)
## Description
Adds support for cinema mode, which is enabled by default.

When enabled and if starting playback from the beginning, the server is
queried for any "intros", ie prerolls or trailers, to play first. These
are shown in the queue and can be skipped if desired.

The server has to provide the intros, so a plugin like
https://github.com/jellyfin/jellyfin-plugin-intros or
https://github.com/CherryFloors/jellyfin-plugin-cinemamode is required
server-side.

### Related issues
Closes #418
Closes #1180

### Testing
Emulator and server w/
https://github.com/CherryFloors/jellyfin-plugin-cinemamode

## Screenshots
N/A

## AI or LLM usage
None
2026-04-26 10:16:26 -04:00
Damontecres
ea6a0227b0
Update issue template 2026-04-25 14:44:01 -04:00
Damontecres
3f0613f5f4
Fix play/shuffle buttons on music library tabs (#1307)
## Description
Fixes the play & shuffle buttons on music library tabs.

They previously attempted to play the audio via the video playback
method. This PR adds the ability to override the buttons and does so for
music libraries.

### Related issues
Fixes #1304

### Testing
Emulator, compared results against the web UI

## Screenshots
N/A

## AI or LLM usage
None
2026-04-25 10:35:12 -04:00
Damontecres
50174db22e
Updates to the playback debug overlay (#1290)
## Description
Lots of minor changes to the debug overlay

- Adjusts the size and layout 
- Smaller font size
- Only shows selected tracks by default
- Expanding tracks are scrollable and don't cover controls
- Show (estimated) network bitrate
- Show heap memory usage
- Better controls/chapters/queue animations
- Minor optimizations

Dev note: There's lots of code reorganization too

### Related issues
Related to #1216
Related to https://github.com/damontecres/Wholphin/issues/528

### Testing
Emulator & nvidia shield

## Screenshots
### Selected tracks
<img width="1280" height="720" alt="overlay1 Large"
src="https://github.com/user-attachments/assets/e6ef71f5-73ef-4947-8b2a-0f41f64ef019"
/>

### Expanded tracks, scrolled down
<img width="1280" height="720" alt="overlay2 Large"
src="https://github.com/user-attachments/assets/2e655f7e-7331-42dd-a572-0173b06563a8"
/>

### Transcode info
<img width="1280" height="720" alt="overlay3 Large"
src="https://github.com/user-attachments/assets/3081e2ec-bb9e-422c-b833-c22785c649bf"
/>

## AI or LLM usage
None
2026-04-24 15:51:43 -04:00
renovate[bot]
0f9d458bc7
Update Dependencies (#1293) 2026-04-24 14:51:09 -04:00
renovate[bot]
cc295cff38
Update dependency com.google.devtools.ksp to v2.3.7 (#1296) 2026-04-24 14:50:56 -04:00
Damontecres
8dd396b777
Move skip segments settings to their own page (#1306)
## Description
Moves the five skip segment settings to their own page. So there's now a
single "Skip behavior" that you click to bring up the settings for the
five. I doubt many people change these frequently, so moving them to a
new page de-clutters the settings page.

### Related issues
N/A

### Testing
Emulator

## Screenshots

### Advanced settings
<img width="1280" height="720" alt="single_setting Large"
src="https://github.com/user-attachments/assets/673555d5-109c-4c43-9073-f51572bb949d"
/>

### Skip segment specific settings
<img width="1280" height="720" alt="skip_settings Large"
src="https://github.com/user-attachments/assets/dae9d0bd-7340-4731-b561-b7b4e74d9d4f"
/>

## AI or LLM usage
None
2026-04-24 14:50:23 -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
Damontecres
0e37084a16
Fix always listing next up/continue watching rows first (#1288)
## Description
Previously the home page always tried to load the "watching rows" (Next
up, Continue watching, or combined) first. This meant that even if the
user's customized home page did not put these rows at the top, they
would always be listed first anyway.

This PR fixes this so rows on the home page are always listed in the
right order.

### Related issues
Fixes #1275

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-04-22 13:34:43 -04:00
Damontecres
c9cab7a5c2
Show season extras row on series overview (#1286)
## Description
Adds a row for season level extras on the series overview page below the
people rows. The extras will update when switching between season tabs.

Like elsewhere in the app, an single extra of a type is show inline and
multiple extras of the same type open a grid when clicked.

### Related issues
Closes #504

### Testing
Emulator

## Screenshots
<img width="1280" height="720" alt="season_extras Large"
src="https://github.com/user-attachments/assets/bc62bef4-66c8-4412-9eca-ec0105d0d0fa"
/>


## AI or LLM usage
None
2026-04-21 17:34:38 -04:00
Ray
8f98526558
CI & Docs updates (#1284)
Increases the heap size for gradle builds in CI

Also adds some docs about where/how app settings are stored.

No user facing changes
2026-04-20 17:41:31 -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
36b7390c4e
Fix crash when screensaver networks errors out (#1279)
## Description
Fixes a crash if there is a network error when starting the screensaver.

Instead, a black screen with an error message shows. The error message
fades in/out to different parts of the screen. This applies to both
in-app and OS screensavers.

### Related issues
Fixes #1247

### Testing
Emulator w/ simulated errors

## Screenshots
N/A

## AI or LLM usage
None
2026-04-20 17:30:56 -04:00
Ray
61afe6ba86
Port two changes to ExoPlayer device profile from the official app (#1263)
## Description
Ports two changes in the official jellyfin android TV app here:
- https://github.com/jellyfin/jellyfin-androidtv/pull/5240
- https://github.com/jellyfin/jellyfin-androidtv/pull/5186

The code changes are copied under GPL-2.0 from the above pull requests.

### Related issues
Closes #1260

### Testing
Emulator mostly

## Screenshots
N/A

## AI or LLM usage
None

---------

Co-authored-by: Niels van Velzen <git@ndat.nl>
Co-authored-by: clams4shoes <prestidgeandrew@gmail.com>
2026-04-19 15:20:46 -04:00
renovate[bot]
27310d71b4
Update Kotlin serialization to v1.11.0 (#1224) 2026-04-19 15:11:08 -04:00
renovate[bot]
29a0ddaeb2
Update Dependencies (#1221) 2026-04-19 15:10:28 -04:00
Ray
08836e3c5d
Update ExoPlayer with better audio configuration (#1258)
## Description
Makes some changes when building ExoPlayer to help improve pass through
audio.

The major changes are enabled constant bitrate seeking and audio
offloading.

Documentation is thin on these features, but [this blog
post](https://www.qualcomm.com/developer/blog/2023/08/audio-offload-support-exoplayer)
while focused on ExoPlayer2 and power usage, does explain a bit about
audio offloading.

Small section on constant bitrate seeking:
https://developer.android.com/media/media3/exoplayer/customization#enabling-constant-bitrate-seeking

### Related issues
N/A

### Testing
Emulator, nvidia shield w/ AVR to check pass through

## Screenshots
N/A

## AI or LLM usage
None
2026-04-19 15:10:15 -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
dudecuda
5050b087e1
Fix trickplay image grid Y-coordinate calculation (#943) (#1259)
## Description
Issue 943 where trickplay images were misaligned with seek preview
mages. I made a post in issues but figured i'd go ahead and make the
pull request.

per gemini:
In SeekPreviewImage.kt, the code calculates which tile in the grid to
display based on the current timestamp:

val x = (tileIndex % trickPlayInfo.tileWidth) // x position within tile
grid
val y = (tileIndex / trickPlayInfo.tileHeight) // y position <-- BUG IS
HERE
To find the correct row (y) in a grid, you are supposed to divide the
current index by the width of the row (the number of columns), not the
height.

Why this breaks: Let's assume a standard trickplay grid that is 10
images wide (tileWidth = 10) and 5 images tall (tileHeight = 5).

If you are looking at the 12th image (tileIndex = 11), it should be on
Row 1 (the second row).

The code calculates y = 11 / 5 = 2. It skips to Row 2!

Because it divides by the smaller height value, the y coordinate
increases much faster than it should. This causes the preview to
visually "jump" down to future rows prematurely, and then snap back as
the x coordinate resets, causing the exact "flashing back and forth" you
saw with the credits and intros.

### Related issue
Fixes #943

### Testing
Tested in Android TV (Android 9) and it does indeed display proper seek
previews using trickplay images.


## AI or LLM usage
Gemini Pro was used to find and fix this bug.
2026-04-18 17:07:18 -04:00
Damontecres
6e58a089f4
Fix linting 2026-04-17 13:45:34 -04:00
Ray
a6eb6bcec9
Clarify seerr integration
Clarify that Seerr integration is only for github & play store releases
2026-04-17 13:20:26 -04:00
Ray
b3d630f272
Add option to never show next up episodes (#1257)
## Description
Adds an option to never show next up. This means when an episode
completes, the app always returns to the previous page instead of
suggesting the next episode.

### Related issues
Closes #1100

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-04-16 17:28:02 -04:00
Ray
b5422fe82b
Show last played date on overview dialog (#1248)
## Description
Adds a line to the overview/media info dialog with the date when the
item was last played.

### Related issues
N/A

### Testing
Emulator

## Screenshots
<img width="1280" height="720" alt="last_played Large"
src="https://github.com/user-attachments/assets/1c38141d-1000-43d0-bf28-a0845edfacc2"
/>

## AI or LLM usage
None
2026-04-16 00:26:57 -04:00
Ray
b18416c954
Integrate with libass-android to support SSA/ASS subtitles in ExoPlayer (#1052)
## Description
Add better ASS/SSA support to ExoPlayer by using
[`libass-android`](https://github.com/peerless2012/libass-android).

This is enabled with the "Use libass for ASS subtitles" advanced
settings for ExoPlayer. It is enabled by default.

### Related issues
Related to #22
Replaces reverted #569
Fixes #1107
2026-04-15 17:50:34 -04:00
Ray
95859be3c6
Add a small glow effect to the segment skip button (#1228)
## Description
Adds a small glow to the border of the segment skip buttons, ie "Skip
Intro", etc. Since this button appears as focused immediately, there's
no contrast and it can be missed sometimes. So this glowing border draws
the eye a bit better.

Also a small change to reduce the minimum skip forward amount to 5
seconds .

### Related issues
Skip forward: #1203

### Testing
Emulator. But this needs some "real world" testing on a TV, too, so this
may need tweaking

## Screenshots


https://github.com/user-attachments/assets/5488d468-aee4-4448-a823-6326ccb003da

## AI or LLM usage
None
2026-04-15 17:49:54 -04:00
Damontecres
923a3713af
Release v0.6.2
Some checks failed
Create release / publish (push) Has been cancelled
2026-04-13 19:20:13 -04:00
Ray
c923afde8d
Use wholphin-extensions v0.1.1 (#1241)
## Description
Updates to use the latest wholphin-extensions which includes
https://github.com/damontecres/wholphin-extensions/pull/2

The crash in #1239 occurs if "AV1 software decoding" is enabled and
seemingly only if the app was installed via APK, including app stores.

### Related issues
Fixes #1239

### Testing
Emulator, nvidia shield

## Screenshots
N/A

## AI or LLM usage
None
2026-04-13 13:50:24 -04:00
Damontecres
57e954f502
Fix build script
Some checks failed
Create release / publish (push) Has been cancelled
2026-04-12 09:01:24 -04:00
Damontecres
24ed1bb182
Release v0.6.1 2026-04-12 08:44:17 -04:00
Ray
ae5dd1f3a9
Translations 2026-04-11 (#1232)
Update translations as of 2025-04-11. Adds a new language translation:
Thai

Thanks to all of the contributors!
2026-04-12 08:42:31 -04:00
SimonHung
9037527d2a
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (538 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-04-11 22:25:36 -04:00
Ray
287a7b0c69
Create FireTV specific app variant (#1230)
## Description
Adds a variant of Wholphin targeted for the Amazon Fire TV App store.

### Related issues
See #1192 for more details

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-04-11 19:30:23 -04:00
Damontecres
2a9f8a27af
Fix formatting 2026-04-11 13:40:20 -04:00
idezentas
acf4eab1d9
Translated using Weblate (Turkish)
Currently translated at 100.0% (538 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-04-10 18:56:49 +00:00
Outbreak2096
89dc42871e
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (538 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-04-10 18:56:49 +00:00
Chosnuk
1f7b7c8b24
Translated using Weblate (Polish)
Currently translated at 55.0% (296 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-04-10 18:56:49 +00:00
Priit Jõerüüt
9a4bf6eaa8
Translated using Weblate (Estonian)
Currently translated at 100.0% (538 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-04-10 18:56:49 +00:00
danpergal84
e18a4ebfe2
Translated using Weblate (Spanish)
Currently translated at 100.0% (538 of 538 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-04-10 18:56:49 +00:00
idezentas
7718eca0c4
Translated using Weblate (Turkish)
Currently translated at 100.0% (534 of 534 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-04-10 18:56:49 +00:00
Alan700
a8a906b81f
Translated using Weblate (French)
Currently translated at 100.0% (534 of 534 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-04-10 18:56:49 +00:00
Outbreak2096
a5a0d3f349
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (534 of 534 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-04-10 18:56:49 +00:00
Alan700
cadbf04b4f
Translated using Weblate (French)
Currently translated at 100.0% (534 of 534 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-04-10 18:56:49 +00:00
danpergal84
c539ba1bf6
Translated using Weblate (Spanish)
Currently translated at 100.0% (534 of 534 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-04-10 18:56:49 +00:00
Codeberg Translate
c2b620f15a
Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/
2026-04-10 18:56:49 +00:00
SimonHung
a700b37007
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.2% (530 of 534 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-04-10 18:56:49 +00:00
SimonHung
4e63f528fa
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (534 of 534 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-04-10 18:56:49 +00:00
lednurb
9674f8e8b5
Translated using Weblate (Dutch)
Currently translated at 90.9% (483 of 531 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-04-10 18:56:49 +00:00
Priit Jõerüüt
1e1af85196
Translated using Weblate (Estonian)
Currently translated at 100.0% (531 of 531 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-04-10 18:56:49 +00:00
North-DaCoder
90ef9fda2e
Translated using Weblate (German)
Currently translated at 100.0% (531 of 531 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-04-10 18:56:49 +00:00
idezentas
664e7edb12
Translated using Weblate (Turkish)
Currently translated at 100.0% (531 of 531 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-04-10 18:56:49 +00:00
lednurb
d4f1de461d
Translated using Weblate (Dutch)
Currently translated at 90.9% (483 of 531 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-04-10 18:56:49 +00:00
arcker95
a442fd21b8
Translated using Weblate (Italian)
Currently translated at 100.0% (531 of 531 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-04-10 18:56:49 +00:00
danpergal84
cae9f8d25d
Translated using Weblate (Spanish)
Currently translated at 100.0% (531 of 531 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-04-10 18:56:49 +00:00
Codeberg Translate
f53b0dd507
Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/
2026-04-10 18:56:49 +00:00
jbrek
7f1ccc6146
Translated using Weblate (Thai)
Currently translated at 98.8% (524 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/th/
2026-04-10 18:56:48 +00:00
jbrek
278d2cc3e6
Translated using Weblate (Thai)
Currently translated at 29.2% (155 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/th/
2026-04-10 18:56:48 +00:00
opakholis
ac0e42320f
Translated using Weblate (Indonesian)
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-04-10 18:56:48 +00:00
jbrek
9e2df5660a
Added translation using Weblate (Thai) 2026-04-10 18:56:48 +00:00
idezentas
81a0ae39f0
Translated using Weblate (Turkish)
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-04-10 18:56:48 +00:00
Chosnuk
d055ba4ab4
Translated using Weblate (Polish)
Currently translated at 49.8% (264 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-04-10 18:56:48 +00:00
lednurb
ffb7293368
Translated using Weblate (Dutch)
Currently translated at 91.1% (483 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-04-10 18:56:48 +00:00
Pwnisher
36d2926ff7
Translated using Weblate (Romanian)
Currently translated at 63.9% (339 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ro/
2026-04-10 18:56:48 +00:00
Outbreak2096
97690bdb7b
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-04-10 18:56:48 +00:00
Chosnuk
bfbcfe9678
Translated using Weblate (Polish)
Currently translated at 48.8% (259 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-04-10 18:56:48 +00:00
lednurb
a29cdae0a4
Translated using Weblate (Dutch)
Currently translated at 74.3% (394 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-04-10 18:56:48 +00:00
arcker95
32258407df
Translated using Weblate (Italian)
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-04-10 18:56:48 +00:00
opakholis
0f165d1dbf
Translated using Weblate (Indonesian)
Currently translated at 95.8% (508 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-04-10 18:56:48 +00:00
Ray
97e10f57ca
Few UI improvments & fixes (#1229)
## Description
- Add indicator to mark the chosen version on the choose version dialog
- Add an option for album's context menu to add it to the now playing
queue
- Fix how season cards refresh on the series detail page so the images
don't glitch

### Related issues
Related to #1220

### Testing
Emulator mostly

## Screenshots

The indicator shows which version will play, whether explicitly chosen
by the user or implicitly picked by Wholphin.
![choose_version_indicator
Large](https://github.com/user-attachments/assets/7440ee1a-b9eb-4b34-ac39-86206c2eca09)

## AI or LLM usage
None
2026-04-10 14:56:43 -04:00
Ray
ff37eff27d
Update rennovate settings (#1217)
## Description
Ignore dependencies from `wholphin-extensions` & the Jellyfin SDK. These
dependencies should be updated manually.

### Related issues
N/A

### Testing
N/A

## Screenshots
N/A

## AI or LLM usage
None
2026-04-09 14:42:30 -04:00
renovate[bot]
6aa28e22da
fix(deps): update dependencies to v14 (major) (#1208) 2026-04-07 16:12:39 -04:00
renovate[bot]
c968c07d0b
fix(deps): update dependencies to v0.40.2 (#1202) 2026-04-07 16:12:10 -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
ade564d907
Add studio tab & filtering to TV show libraries (#1211)
## Description
Adds a tab for Studios (TV networks) to TV libraries. This displays a
grid of all of the studios. Clicking one displays the series produced by
the studio.

Additionally, adds a filter to the library tab to filter by studio. You
can select multiple studios, but its not the best UX since there will
likely be many studios (I have almost 200).

Finally, you can add a home row for studios in a library.

### Related issues
Closes #1002

### Testing
Mostly manual testing via emulator

## Screenshots
### Studios tab for a library
![studios_grid
Large](https://github.com/user-attachments/assets/d51e6d20-3768-4bd5-8a09-45cd491064b2)
### Filter drop down for studios
![filter_by_studios
Large](https://github.com/user-attachments/assets/7dbd4c13-4194-4674-b272-d42257368ce2)
### Updates series details
Shows the studio above the genres
![series_details_studio
Large](https://github.com/user-attachments/assets/745caa9b-838c-4e8b-b0f8-69d5f316287a)


## AI or LLM usage
None
2026-04-07 16:03:58 -04:00
Ray
edaf06afd0
Show music video watch status on artist & album pages (#1209)
## Description
Show the watched indicator and favorite icon on music videos on both the
artist or album detail pages.

### Related issues
Fixes #1206 

### Testing
Quick emulator testing

## Screenshots
N/A, same indicators as the rest of the app

## AI or LLM usage
None
2026-04-06 18:17:04 -04:00
Damontecres
ac13ce5713
Fix globstar quoting 2026-04-06 18:02:42 -04:00
Damontecres
bc5bd2f852
Enable globstar 2026-04-06 17:06:02 -04:00
Ray
31465051ff
Dev: use build product flavors for app store release (#1205)
## Description
Instead of applying a code patch, this PR uses build time [product
flavors](https://developer.android.com/build/build-variants#product-flavors)
to define the existing default and an app store variant. This is more
robust than maintaining a patch file and making sure it is applied
before building.

The app store variant applies the same code changes (removing install
permission, requiring leanback, & setting `UpdateChecker.ACTIVE`), but
via manifest placeholders/merging and `BuildConfig` entries.

There are no user facing changes.

### Related issues
None

### Testing
Built the variants and verified manifest changes via aapt

## Screenshots
N/A

## AI or LLM usage
None
2026-04-06 15:24:20 -04:00
Ray
9a3af225a4
View more discover results (#1198)
## Description
Adds a card at the end of the rows on the discover page/tab to view more
results. When clicking this card, it goes to a grid view of more results
starting focused on the next one.

### Dev notes

This PR modifies `ApiRequestPager` to move most of its functionality
into an abstract class, `RequestPager`. And then add a new concrete
class, `DiscoverRequestPager`, for Seerr API paging. `ApiRequestPager`
remains for Jellyfin paging.

### Related issues
Related to #1035

### Testing
Emulator

## Screenshots

![discover_view_more
Large](https://github.com/user-attachments/assets/180aa7a5-45c3-4544-b3f8-e3d394859178)
![discover_grid
Large](https://github.com/user-attachments/assets/ae5e8fbf-0fa4-4a2e-96ac-629ae688c05d)

## AI or LLM usage
None
2026-04-06 13:18:55 -04:00
Leon Omelan
1366ee744d
perfomance: Reduce recompositions in SeriesOverviewContent when navigating (#1185)
<!-- By submitting this pull request, you acknowledge that you have read
the [contributing
guide](https://github.com/damontecres/Wholphin/blob/main/CONTRIBUTING.md,
including the AI/LLM policy, and [developer's
guide](https://github.com/damontecres/Wholphin/blob/main/DEVELOPMENT.md)
-->

## Description
<!-- Describe the changes in detail -->
Make alpha channel and background change in Banner Card more
recomposition friendly.

Split from #1152

### Related issues
<!-- If this is a new feature or a change, there must be a discussion in
an issue first, reference the issue here -->
<!-- If fixing a bug, reference the bug issue here, or describe the bug,
including steps to reproduce -->

### Testing
<!-- Describe how this change was tested and on what device(s) -->
Tested on a emulator
It's observed when moving from TV series episode card to EpisodeFooter
and back
## Screenshots
<!-- Please include screenshots if the PR alters any UI elements -->
| Pre | Post |
|--------|--------|
| <img width="395" height="152" alt="Screenshot 2026-04-02 at 08 41 40"
src="https://github.com/user-attachments/assets/b6554425-8af5-4e29-a866-3e4f6a7be473"
/> | <img width="406" height="296" alt="Screenshot 2026-04-02 at 08 44
56"
src="https://github.com/user-attachments/assets/511fc6df-c860-47eb-aec8-57c400dba83c"
/> |


## AI or LLM usage
<!-- If you used any AI or LLM assistance, please list where in the code
and how you tested it -->
Gemini in AS was used for repetitive code completion
2026-04-06 11:23:54 -04:00
Leon Omelan
5ab4bb712b
performance: reduce recompositons in PersonCard and SeasonCard (#1184)
<!-- By submitting this pull request, you acknowledge that you have read
the [contributing
guide](https://github.com/damontecres/Wholphin/blob/main/CONTRIBUTING.md,
including the AI/LLM policy, and [developer's
guide](https://github.com/damontecres/Wholphin/blob/main/DEVELOPMENT.md)
-->

## Description
<!-- Describe the changes in detail -->
related docs:
https://developer.android.com/develop/ui/compose/performance/bestpractices#defer-reads
Removed setting padding with animated value, instead moved to lambda
offset and layout modifiers to increase performance and reduce
recompositions when focusing those cards

Split from #1152

### Related issues
<!-- If this is a new feature or a change, there must be a discussion in
an issue first, reference the issue here -->
<!-- If fixing a bug, reference the bug issue here, or describe the bug,
including steps to reproduce -->

### Testing
<!-- Describe how this change was tested and on what device(s) -->
Tested on a emulato
## Screenshots
<!-- Please include screenshots if the PR alters any UI elements -->
|Component | Pre | Post |
|---|--------|--------|
|Person Card|<img width="404" height="366" alt="Screenshot 2026-04-02 at
08 19 18"
src="https://github.com/user-attachments/assets/73b38c75-040b-401e-88d6-4bfc9ffc0cc9"
/> | <img width="407" height="346" alt="Screenshot 2026-04-02 at 08 22
06"
src="https://github.com/user-attachments/assets/add473f7-dea6-4e33-97b8-6954149a81e2"
/> |
|Season Card| <img width="404" height="341" alt="Screenshot 2026-04-02
at 08 18 31"
src="https://github.com/user-attachments/assets/eec594ee-6701-4f99-8fb3-339ed8559ec4"
/> |<img width="408" height="299" alt="Screenshot 2026-04-02 at 08 24
06"
src="https://github.com/user-attachments/assets/8de445a6-dcb8-4c7e-a5f6-a25bf822c822"
/> |



## AI or LLM usage
<!-- If you used any AI or LLM assistance, please list where in the code
and how you tested it -->
Gemini in AS was used for repetitive code completion
2026-04-06 10:11:01 -04:00
Ray
44d1ac484f
Refactor movie page to use use state flow (#1183)
## Description
Refactors the movie page & view model to use a `StateFlow` instead of
many `LiveData` objects. This is in keeping with best practices.

There are no user facing changes.

### Related issues
None

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-04-03 12:44:59 -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
46beee00bb
Fix supported audio types (#1173)
## Description
Fixes the list of supported audio types when playing music.

This fixes allows for playback of audio formats like ALAC, PCM/WAV,
Vorbis, etc.

### Related issues
Fixes #1168 

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-04-01 01:05:24 -04:00
Ray
7ccef6f802
Fix card overlays showing, fix home page refresh indicator (#1178)
## Description
- Always show card overlays (ie Movie vs TV Show) for discover/Seerr
cards
- Show card overlays (ie favorite and watched status) for search result
cards
- Restore showing home page refresh indicator

### Related issues
Closes #1141
Closes
https://github.com/damontecres/Wholphin/issues/702#issuecomment-4114775539

### Testing
Emulator

## Screenshots
N/A, no new UIs, just adding existing ones

## AI or LLM usage
None
2026-03-31 23:45:00 -04:00
Leon Omelan
762a5e6dcc
Optimize recompositions on focus change (#1138)
<!-- By submitting this pull request, you acknowledge that you have read
the [contributing
guide](https://github.com/damontecres/Wholphin/blob/main/CONTRIBUTING.md,
including the AI/LLM policy, and [developer's
guide](https://github.com/damontecres/Wholphin/blob/main/DEVELOPMENT.md)
-->

## Description
<!-- Describe the changes in detail -->
I've look at the app using Layout inspector to see if there are any
recompositions that shouldn't be happening while navigating normally in
the app. I've noticed two issues
1. Every BannerCard was being recomposed on every cursor move, which
added a lot of overhead to home screen navigation
2. Tab row in the Series View was being recomposed while navigating
across episodes in given season.

### Related issues
<!-- If this is a new feature or a change, there must be a discussion in
an issue first, reference the issue here -->
<!-- If fixing a bug, reference the bug issue here, or describe the bug,
including steps to reproduce -->
Performance on the home screen is lacking, especially on low end
devices, while this isn't a full fix, it will improve the navigation, as
the app will be making significantly less work.

Probably will fix https://github.com/damontecres/Wholphin/issues/1043

### Testing
<!-- Describe how this change was tested and on what device(s) -->
Using Layout Inspector built-in recomposition counter and highlights
it's easy to see components which recompose while they shouldn't. I've
tested it on a 4K TV emulator on my M1 Macbook Pro

## Screenshots
<!-- Please include screenshots if the PR alters any UI elements -->
main: 
<img width="402" height="508" alt="image"
src="https://github.com/user-attachments/assets/6aaac6af-50d6-4f21-b465-e78bccf7e985"
/>

my branch:
<img width="401" height="563" alt="image"
src="https://github.com/user-attachments/assets/1034c0e2-9a40-4a05-8a07-f30b2e10301d"
/>

same navigation, on the same content. (down, right x4
## AI or LLM usage
<!-- If you used any AI or LLM assistance, please list where in the code
and how you tested it -->
Gemini built in to Android Studio helped me identify non-stable parts of
the code. It also helped me understand and implement fixes in the
HomePage code.
I've took the time to check manually that it didn't alter any behavior,
and verified the fixes using Layout Inspector and recomposition counter
2026-03-31 18:39:02 -04:00
Ray
4ea21be430
Move native builds to wholphin-extensions (#1172)
## Description
This PR moves the native components (ffmpeg decoder, av1 decoder, &
`libmpv`) to https://github.com/damontecres/wholphin-extensions. They
can be retrieved as gradle dependencies now.

This allows for contributors to more easily include these extensions
when building Wholphin locally.

There are no user facing changes in this PR.

Note: the kotlin code for interfacing with `libmpv`, ie `MPVLib` &
`MpvPlayer`, is still in this repo for now.

### Related issues
Related to #828

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-03-31 18:10:40 -04:00
Damontecres
ca43baa531
Release v0.6.0
Some checks failed
Create release / publish (push) Has been cancelled
2026-03-30 17:08:30 -04:00
Ray
39a9b7007f
Translations 2026-03-30 (#1161)
Updates translation as of 2026-03-30

Thank you to everyone who contributed!

Adds two new languages, Latvian & Romanian!
2026-03-30 17:07:58 -04:00
Pwnisher
6f855fe4a3
Translated using Weblate (Romanian)
Currently translated at 51.3% (272 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ro/
2026-03-30 18:11:36 +00:00
Pwnisher
5158675d2b
Added translation using Weblate (Romanian) 2026-03-30 18:11:36 +00:00
gatisr
2cabc0cdba
Translated using Weblate (Latvian)
Currently translated at 37.1% (197 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/lv/
2026-03-30 18:11:36 +00:00
SimonHung
6a3b45371b
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-03-30 18:11:36 +00:00
n8llcaster
84435f713b
Translated using Weblate (Slovak)
Currently translated at 95.2% (505 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-03-30 18:11:36 +00:00
American_Jesus
e0309fb38b
Translated using Weblate (Portuguese)
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-03-30 18:11:36 +00:00
danpergal84
98e4ec0868
Translated using Weblate (Spanish)
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-03-30 18:11:36 +00:00
Sk4hnt42
51a8777b30
Translated using Weblate (German)
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-30 18:11:36 +00:00
flipip
9495cb4d01
Translated using Weblate (German)
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-30 18:11:36 +00:00
zyplex
64ad27aabf
Translated using Weblate (German)
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-30 18:11:36 +00:00
Lorixx
058aed1f53
Translated using Weblate (German)
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-30 18:11:36 +00:00
North-DaCoder
4d510e772f
Translated using Weblate (German)
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-30 18:11:36 +00:00
Sk4hnt42
72fd1a4cb4
Translated using Weblate (German)
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-30 18:11:36 +00:00
alexq83
ce4408f305
Translated using Weblate (German)
Currently translated at 100.0% (530 of 530 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-30 18:11:36 +00:00
Outbreak2096
e70beebaa0
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (529 of 529 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-03-30 18:11:35 +00:00
North-DaCoder
4f519958ca
Translated using Weblate (German)
Currently translated at 100.0% (529 of 529 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-30 18:11:35 +00:00
danpergal84
8e2d4778cb
Translated using Weblate (Spanish)
Currently translated at 100.0% (529 of 529 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-03-30 18:11:35 +00:00
gatisr
65a9f4fdc1
Translated using Weblate (Latvian)
Currently translated at 37.3% (197 of 528 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/lv/
2026-03-30 18:11:35 +00:00
gatisr
91f864ae53
Added translation using Weblate (Latvian) 2026-03-30 18:11:35 +00:00
lednurb
fd6d52c7ba
Translated using Weblate (Dutch)
Currently translated at 74.6% (394 of 528 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-03-30 18:11:35 +00:00
SimonHung
8daf914660
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (528 of 528 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-03-30 18:11:35 +00:00
North-DaCoder
ba71f3df20
Translated using Weblate (German)
Currently translated at 100.0% (528 of 528 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-30 18:11:35 +00:00
lednurb
52fbebb41f
Translated using Weblate (Dutch)
Currently translated at 74.4% (393 of 528 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-03-30 18:11:35 +00:00
lednurb
cc8bf112c5
Translated using Weblate (Dutch)
Currently translated at 74.4% (393 of 528 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-03-30 18:11:35 +00:00
idezentas
9a18750a05
Translated using Weblate (Turkish)
Currently translated at 100.0% (528 of 528 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-03-30 18:11:35 +00:00
Outbreak2096
a9f09888b9
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (528 of 528 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-03-30 18:11:35 +00:00
lednurb
259bb2bbf1
Translated using Weblate (Dutch)
Currently translated at 68.5% (362 of 528 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2026-03-30 18:11:35 +00:00
danpergal84
0b0b93abf7
Translated using Weblate (Spanish)
Currently translated at 100.0% (528 of 528 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-03-30 18:11:35 +00:00
Priit Jõerüüt
a05980a4a8
Translated using Weblate (Estonian)
Currently translated at 100.0% (504 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-03-30 18:11:35 +00:00
SimonHung
6b72746ee3
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (504 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-03-30 18:11:35 +00:00
EmadAljohani
6aba1c1561
Translated using Weblate (Arabic)
Currently translated at 28.1% (142 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ar/
2026-03-30 18:11:35 +00:00
American_Jesus
841b1bc713
Translated using Weblate (Portuguese)
Currently translated at 100.0% (504 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-03-30 18:11:35 +00:00
oyvhov
86b0228b96
Translated using Weblate (Norwegian Nynorsk)
Currently translated at 66.2% (334 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nn/
2026-03-30 18:11:35 +00:00
oyvhov
c2ee214991
Translated using Weblate (Norwegian Nynorsk)
Currently translated at 66.2% (334 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nn/
2026-03-30 18:11:35 +00:00
ymir
666aa3432f
Translated using Weblate (French)
Currently translated at 100.0% (504 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-03-30 18:11:35 +00:00
danyrd92
b4f2258c5e
Translated using Weblate (Spanish)
Currently translated at 100.0% (504 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-03-30 18:11:35 +00:00
streamyfinchris
cff89b418a
Translated using Weblate (Swedish)
Currently translated at 75.1% (379 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-03-30 18:11:35 +00:00
disguisedpsycho
94ff03236b
Translated using Weblate (Hebrew)
Currently translated at 21.2% (107 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2026-03-30 18:11:34 +00:00
unclenet
3c40a98f4c
Translated using Weblate (Italian)
Currently translated at 100.0% (504 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-03-30 18:11:34 +00:00
Priit Jõerüüt
a6cc4b5146
Translated using Weblate (Estonian)
Currently translated at 98.0% (494 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-03-30 18:11:34 +00:00
opakholis
cbf8f5226f
Translated using Weblate (Indonesian)
Currently translated at 100.0% (504 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-03-30 18:11:34 +00:00
jatag41526
8549265203
Translated using Weblate (Russian)
Currently translated at 64.4% (325 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ru/
2026-03-30 18:11:34 +00:00
idezentas
6420345e78
Translated using Weblate (Turkish)
Currently translated at 100.0% (504 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-03-30 18:11:34 +00:00
Outbreak2096
e91a29bce6
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (504 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-03-30 18:11:34 +00:00
n8llcaster
3821ec7046
Translated using Weblate (Slovak)
Currently translated at 100.0% (504 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-03-30 18:11:34 +00:00
danpergal84
c7b7f67e0c
Translated using Weblate (Spanish)
Currently translated at 100.0% (504 of 504 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-03-30 18:11:34 +00:00
North-DaCoder
5eb3ba86c8
Translated using Weblate (German)
Currently translated at 100.0% (503 of 503 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-30 18:11:34 +00:00
RafaelSoaresP
2fe3799785
Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt_BR/
2026-03-30 18:11:34 +00:00
RabSsS
9ae23d6725
Translated using Weblate (French)
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-03-30 18:11:34 +00:00
ymir
768af6958d
Translated using Weblate (French)
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-03-30 18:11:34 +00:00
n8llcaster
79959a4421
Translated using Weblate (Slovak)
Currently translated at 80.0% (402 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-03-30 18:11:34 +00:00
American_Jesus
dfdfc76b71
Translated using Weblate (Portuguese)
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-03-30 18:11:34 +00:00
North-DaCoder
5575a071e3
Translated using Weblate (German)
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-30 18:11:34 +00:00
idezentas
02b4d90a1e
Translated using Weblate (Turkish)
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-03-30 18:11:34 +00:00
Outbreak2096
5abc5ee0d7
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-03-30 18:11:34 +00:00
North-DaCoder
70a4f35b2f
Translated using Weblate (German)
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-30 18:11:34 +00:00
Ray
1334bb3f03
Don't recursively query collections on home page (#1160)
## Description
Don't recursively query collections for the home page rows. This
prevents episodes within tv shows from being shown.

### Related issues
Fixes https://github.com/damontecres/Wholphin/issues/1148
Fixes #1147

### Testing
Emulator & API

## Screenshots
N/A

## AI or LLM usage
None
2026-03-30 14:11:25 -04:00
Ray
66f060dccb
Add a lot of code documentation (#1145)
## Description
A brain dump documenting many classes and functions throughout the app.
Definitely does not document everything, but covers most of the major
components. This should be useful for new contributors.

There is a small amount of code clean up too.

There are no user facing changes.

### Related issues
N/A

### Testing
N/A

## Screenshots
N/A

## AI or LLM usage
None
2026-03-29 10:52:25 -04:00
Ray
2485d2c644
Add prefer logos instead of text titles setting (#1151)
## Description
Adds an advanced settings to prefer logos for titles instead of text. It
is enabled by default.

### Related issues
Closes #383

### Testing
Emulator & onn

## Screenshots
<details><summary>Click to show</summary>
<p>

### Home page
![logo_1
Large](https://github.com/user-attachments/assets/629a65ab-cf43-4050-af4c-5925763a5f4f)

### Series overview
![logo_2
Large](https://github.com/user-attachments/assets/13ca2d28-cc61-4f4e-abaa-61a310b5c707)

### Movie Page
![logo_movie
Large](https://github.com/user-attachments/assets/a590e6cf-452d-4313-b6a3-35c6f59dc1ff)

### Square-ish logo
![logo_square
Large](https://github.com/user-attachments/assets/7d1340c2-4a3e-42c3-b7bd-ae1114cb6645)

</p>
</details> 


## AI or LLM usage
None
2026-03-29 10:38:23 -04:00
renovate[bot]
253b203c02
Update Androidx Media3 to v1.10.0 (#1149) 2026-03-28 18:44:41 -04:00
Ray
bd21c2c46e
Fix pause indicator color on some devices (#1150)
## Description
Ensure the new pause indicator from #1144 is the right color for all
devices.

### Related issues
Related to #1144

### Testing
Emulator & shield

## Screenshots
N/A

## AI or LLM usage
None
2026-03-28 13:13:28 -04:00
Ray
c35b5cdd0a
Better collection pages including more view options (#1137)
## Description
This is a large update to the collection page!

- Show the collection's details including overview & backdrop
- Separate collection by type (Movies, TV, etc) into rows
- View options are now shared across all collections
- Filter & sort are still saved per collection

By default, the collection will be separated by type, but there is a
view option to restore the combined grid.

### Related issues
Closes #527 
Closes #1088
Fixes
https://github.com/damontecres/Wholphin/issues/877#issuecomment-3889013124
Related to #737

### Testing
Emulator mostly

## Screenshots
![collection_page
Large](https://github.com/user-attachments/assets/2b9a8740-5610-491a-b139-018dabd5f2f1)

### Separated types into rows
![collection_rows
Large](https://github.com/user-attachments/assets/34556f64-6089-4d65-b243-6df01bae5423)

### Combined type into grid
![collection_grid
Large](https://github.com/user-attachments/assets/feea14ef-2b7a-4c09-bba7-d146ec92eb83)

## AI or LLM usage
None
2026-03-28 10:31:03 -04:00
renovate[bot]
b8a9cb5027
Update Gradle to v9.4.1 (#1119) 2026-03-26 15:52:13 -04:00
renovate[bot]
2ebbaeb860
Update Dependencies (#1125) 2026-03-26 15:51:24 -04:00
renovate[bot]
eaaa587556
Update dependency org.openapi.generator to v7.21.0 (#1135) 2026-03-26 15:51:00 -04:00
renovate[bot]
677cc2527b
Update android-actions/setup-android action to v4 (#1143) 2026-03-26 15:50:40 -04:00
Ray
c92de3c33e
Fix some UI preference changes not being reflected immediately (#1146) 2026-03-25 21:55:00 -04:00
Ray
2d863e9c32
Don't show full playback overlay when pausing (#1144)
## Description
Instead of displaying the full playback overlay when pausing via a
button press, just show a short pause image in the center of the screen.
Any button press that will pause triggers this including a remote Pause,
Pause/Play, or one-click pause Enter.

I decided to make this the standard behavior instead of a toggle because
I think if the user intends to do something with the overlay (change
subtitles, audio, etc), it's still straightforward to open the overlay,
click the pause button (which is focused first), and then move to the
desired option versus assuming click-to-pause will open the overlay so
the user can move to the desired option.

### Related issues
Closes #990

### Testing
Emulator

## Screenshots

[pause_osd.webm](https://github.com/user-attachments/assets/8f908164-2a50-43f9-9e13-a463d2914e42)


## AI or LLM usage
None
2026-03-25 13:59:48 -04:00
Ray
8f9b9813b7
Better date, time, & bitrate formatting, other small UI improvements (#1139)
## Description

Use localized date & time formatting such as using 24 hour clock or
Day-Month-Year date formatting. The localization is based on the
device's locale/language setting. Some pages require reloading if you
switch the locale while Wholphin is running.

Format bitrates using SI 1000 magnitudes instead of 1024. File size
still uses 1024 but formats with "MiB"/"GiB" suffixes now. This aligns
with the server web UI and is more in line with standard practices for
display as well.

Fixes the playback overlay layout being messed up by extremely long
episode names.

Finally, adds jumping by letter using keyboard letter keys on grids if
letter jumping is enabled. I doubt many users have a full keyboard
attached to their TV, but this saves me time using the emulator when I
need to repeatedly open the same item for testing.

### Related issues
Fixes #1132

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-03-24 17:48:48 -04:00
Ray
f2570d4ab0
Add support for music (#1059)
## Description
Adds support for music playback

### Features
- Adds a "Now Playing" page which is accessible from the nav drawer
while music is playing
- Manage the playback queue, add/remove/re-order songs
- Supports synced & non-synced lyrics
- Scroll through lyrics
- Click on line for synced lyrics to seek to that position

### User interface
- Customize what's shown on the "Now Playing" page
- Show album cover art, backdrops, and lyrics
- Show a basic bar visualizer
- Search for albums, artists, & songs
- Links between albums, artists, playlists, & music videos when metadata
is available
- Throughout the app, see which song is playing and which ones are in
the queue

Note: lyrics are synced by line. `10.11` added support for syncing by
word, but Wholphin still supports `10.10`

### Related issues
Closes #2 
Closes #267 

### Testing
Emulator & shield testing

## Screenshots
<details><summary>Click to see screenshots</summary>
<p>

### Album grid
![music_album_grid
Large](https://github.com/user-attachments/assets/4573ad32-0cd7-49db-94db-de37ceb6f641)

### Artist page
![music_artist
Large](https://github.com/user-attachments/assets/fdf3b7b9-5c40-434d-867d-7d56e0b3430e)

### Album details
![music_album_details
Large](https://github.com/user-attachments/assets/494d1a76-9581-4a07-a8ee-5c0957f542e0)

### Album is playing/queued
![music_album_queued
Large](https://github.com/user-attachments/assets/55c72cd2-10a9-411d-8bae-b232150db506)

### Now playing page
![now_playing
Large](https://github.com/user-attachments/assets/b773b1bc-71fd-4789-8509-cebfc0d0ffe5)

### Now playing queue
![now_playing_queue
Large](https://github.com/user-attachments/assets/db244462-e33d-452a-93dc-dd2123a4862d)

</p>
</details> 

## AI or LLM usage
None

## Try it out

See instructions at
https://github.com/damontecres/Wholphin/releases/tag/develop-music
2026-03-24 13:42:18 -04:00
Ray
8a37d63d09
Better playback overlay appearance animations (#1130)
## Description
Updates the playback overlay so that individual component animate in
more deliberately.

For example, the logo & clock slide in from the top instead of the
middle of the screen. And the trick play preview now expands and shrinks
vertically.

### Related issues
I thought there was an issue about this, but I can't find it

### Testing
Emulator, nvidia shield

## Screenshots
N/A

## AI or LLM usage
None
2026-03-23 18:04:11 -04:00
Ray
920733075b
Better error handling for errors on home page rows (#1128)
## Description
Adds better error handling when loading home page rows. For example,
this fixes loading errors if a playlist row is configured, but the
playlist is deleted.

Also fixes an issue from #1116 where changing users doesn't go the home
page.

### Related issues
Related to #1124

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-03-21 19:17:43 -04:00
Ray
dac0b7208c
Refresh seasons on series details page (#1123)
## Description
Simply fetches the tv show's seasons when returning the details page.
This ensures the unwatched counts are updated.

### Related issues
Mentioned as a bug in
https://github.com/damontecres/Wholphin/issues/767#issuecomment-4071852502

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-03-20 12:07:28 -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
d184990b16
Improvements to ffmpeg decoders build script (#1110)
## Description
A few improvements to the ffmpeg decoder & mpv build scripts. Mainly
changes the dependency fetching so the scripts are more idempotent,
though some rebuilds still always occur.

### Related issues
Related to
https://github.com/damontecres/Wholphin/pull/1106#issuecomment-4077678690

### Testing
Local builds

## Screenshots
N/A

## AI or LLM usage
None
2026-03-17 17:04:27 -04:00
Damontecres
3af9775592
Release v0.5.4
Some checks failed
Create release / publish (push) Has been cancelled
2026-03-17 12:47:46 -04:00
renovate[bot]
bc7b751b83
Update Androidx Media3 to v1.9.3 (#1106) 2026-03-16 13:18:40 -04:00
renovate[bot]
f3c0606ebf
Update Kotlin to v2.3.20 (#1105) 2026-03-16 12:59:52 -04:00
renovate[bot]
31db1f59b9
Update Dependencies (#1082) 2026-03-16 12:59:34 -04:00
renovate[bot]
ee0df25b6f
Update Gradle to v9.4.0 (#1038) 2026-03-16 12:59:04 -04:00
Ray
9057dceef6
Various fixes to screensaver, subtitle order, & playlist modifications (#1104)
## Description
Several small tweaks:

- Always put screensaver logo on left instead of random
- Disable playback speed option if audio is being passed through
- When opening playback settings such as speed or scale, focus on the
current value instead of top of the list; doesn't apply to subtitles so
it's easy to toggle them off
- If the user has a preferred subtitle language, show tracks in that
language first/top
- Add toast message after creating or adding to a playlist

### Related issues
Playback speed audio pass through: #164 

### Testing
Emulator & shield

## Screenshots
N/A

## AI or LLM usage
None
2026-03-15 19:31:18 -04:00
Ray
23e5225c19
Debug installs should use same when updating (#1103)
## Description
Fixes debug install updates from trying to install the release APK and
use debug ones instead.

### Related issues
Fixes #1098

### Testing
Unit tests

## Screenshots
N/A

## AI or LLM usage
None
2026-03-14 18:35:32 -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
2da55bb616
Use newer libplacebo branch (#1077)
## Description
Testing newer `libplacebo` to include
c93aa134ab

### Related issues
Related to #806
2026-03-13 11:54:55 -04:00
Ray
8bc3384094
Simplify series overview page refresh (#1092)
## Description
This PR simplifies refreshing the episode data when returning to the
series overview page and reduces the number of queries.

Also fixes a race condition when refreshing a single item in the an
`ApiRequestPager`.

### Related issues
Related to #767

### Testing
Emulator, but will need more testing during actual usage

## Screenshots
N/A

## AI or LLM usage
None
2026-03-12 21:16:56 -04:00
Ray
62965c1b22
Fix slideshow images not resizing properly (#1089)
## Description
During a slideshow of images, the viewpoint would resize to largest
image resolution and images with smaller dimensions would be scaled down
and centered with black bars.

This might be a Coil bug, but I'm not 100% sure yet. This PR works
around the issue by scaling all images to the display resolution instead
of using `Size.ORIGINAL`.

However, we want `Size.ORIGINAL` while zooming so it doesn't pixelate if
the original image's resolution is larger than the display's.

### Related issues
Fixes
https://github.com/damontecres/Wholphin/issues/835#issuecomment-4035005433

### Testing
Emulator with 1920x1080 & 800x1600 images

## Screenshots
N/A

## AI or LLM usage
None
2026-03-12 14:39:26 -04:00
Jannik Mahr
cca818fabb
Improve display of "DTS:HD" and "DTS:HD MA" audio tracks (#1064)
<!-- By submitting this pull request, you acknowledge that you have read
the [contributing
guide](https://github.com/damontecres/Wholphin/blob/main/CONTRIBUTING.md,
including the AI/LLM policy, and [developer's
guide](https://github.com/damontecres/Wholphin/blob/main/DEVELOPMENT.md)
-->

## Description
The formatter for audio codecs did only match the profile of high
definition DTS streams against "DTS:HD" or "DTS:X". At least for DTS:HD
there are also cases where the profile contains "DTS-HD" with "-"
instead of ":".

Made the matcher more generic by allowing both "-"" or ":"" and also
checking if the profile contains "MA" for Master Audio tracks.

This will now correctly display the following high definition DTS types
in the UI:
- DTS:HD
- DTS:HD MA
- DTS:X

**Note:** This is my first public pull request ever, let me know if I
did it wrong :)

### Related issues
Relates to (but does not fix):
https://github.com/damontecres/Wholphin/issues/1057

### Testing
Tested on a Fire TV Cube Gen 3 with multiple files containing DTS HD
audio tracks.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-03-10 23:44:44 -04:00
Ray
0dca02b919
Fix race condition when updating preferences (#1078)
## Description
Fixes a race condition saving preferences. It would be triggered if you
have "Remember selected tabs" enabled and either: 1) changed settings
while on a library grid page or 2) changed settings on a page navigated
_from_ a library grid page and then returned to the grid

### Related issues
I think this will fix #1075

### Testing
Emulator following the steps above

## Screenshots
N/A

## AI or LLM usage
None
2026-03-10 18:10:14 -04:00
Ray
7bb47bf329
Photo/slideshow fixes & Seerr server removal fix (#1076)
## Description
A few fixes

- Better crossfade for photo slideshow
- Fix "Go to" in context menu for photos
- Fix certain home video/photo libraries not working for photos
- Fix removing seerr server in some cases

### Related issues
Closes #835
Fixes #1069
Addresses
https://github.com/damontecres/Wholphin/issues/634#issuecomment-4018580518

### Testing
Emulator mostly

## Screenshots
N/A

## AI or LLM usage
None
2026-03-10 14:16:19 -04:00
Ray
afc47f254b
Request individual seasons from Jellyseerr (#1060)
## Description
Adds a dialog to select individual seasons for a TV show when requesting
it via Discover/Jellyseerr

If a request is already pending, it can be updated. If there's more than
7 seasons, a second submit button is added at the bottom of the dialog
for easier access.

Also, adds a button from Jellyfin series details to its Discover
equivalent and added a request button for partially available series to
request more seasons.

### Related issues
Closes #961

### Testing
Emulator & Jellyseerr 2.7.3

## Screenshots
![discover_seasons
Large](https://github.com/user-attachments/assets/c7781bf4-7c1b-4e4e-a0b8-a572111a8497)

## AI or LLM usage
None
2026-03-10 11:54:00 -04:00
Damontecres
49a6cd8d2f
Release v0.5.3
Some checks failed
Create release / publish (push) Has been cancelled
2026-03-06 18:16:48 -05:00
Ray
256bb3f988
Fix crash after last screensaver image in list (#1055)
## Description
Fix a screensaver crash after the last item

Also adds some extra error checking

### Related issues
Fixes #1053

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-03-06 18:16:35 -05:00
Damontecres
8a4be3dd32
Release v0.5.2
Some checks failed
Create release / publish (push) Has been cancelled
2026-03-06 16:37:06 -05:00
Ray
fe76ee6e10
Translations as of 2026-03-06 (#1050)
Adds batch of translations as of 2026-03-06

Thank you to all of the contributors!
2026-03-06 16:36:02 -05:00
Damontecres
73cf54b844
Fixes to strings 2026-03-06 16:18:50 -05:00
idezentas
4cfb471e5d
Translated using Weblate (Turkish)
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-03-06 21:16:21 +00:00
Outbreak2096
b3be20f6da
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-03-06 21:16:21 +00:00
North-DaCoder
dd0f253524
Translated using Weblate (German)
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-06 21:16:21 +00:00
RafaelSoaresP
95e0725d8d
Translated using Weblate (Portuguese (Brazil))
Currently translated at 76.8% (386 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt_BR/
2026-03-06 21:16:21 +00:00
n8llcaster
d0f7908648
Translated using Weblate (Slovak)
Currently translated at 44.6% (224 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-03-06 21:16:21 +00:00
EmadAljohani
062a3e99a4
Translated using Weblate (Arabic)
Currently translated at 23.9% (120 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ar/
2026-03-06 21:16:21 +00:00
arcker95
b4c35ce41b
Translated using Weblate (Italian)
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-03-06 21:16:21 +00:00
isbit
0c19708774
Translated using Weblate (Swedish)
Currently translated at 54.5% (274 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2026-03-06 21:16:21 +00:00
North-DaCoder
b6cef6a4b1
Translated using Weblate (German)
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-06 21:16:21 +00:00
zyplex
0d326b49e7
Translated using Weblate (German)
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-06 21:16:21 +00:00
idezentas
d69b6d6bb4
Translated using Weblate (Turkish)
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-03-06 21:16:21 +00:00
Outbreak2096
b0236cbd3b
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-03-06 21:16:21 +00:00
SimonHung
3009536670
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-03-06 21:16:21 +00:00
danpergal84
000bd10622
Translated using Weblate (Spanish)
Currently translated at 100.0% (502 of 502 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-03-06 21:16:21 +00:00
idezentas
4ee8a5b4fc
Translated using Weblate (Turkish)
Currently translated at 100.0% (501 of 501 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-03-06 21:16:21 +00:00
Outbreak2096
61369ffac8
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (501 of 501 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-03-06 21:16:20 +00:00
danpergal84
d4cb9a6c92
Translated using Weblate (Spanish)
Currently translated at 100.0% (501 of 501 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-03-06 21:16:20 +00:00
ymir
d051b0d402
Translated using Weblate (French)
Currently translated at 100.0% (499 of 499 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-03-06 21:16:20 +00:00
opakholis
01ac0dec4c
Translated using Weblate (Indonesian)
Currently translated at 98.3% (491 of 499 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-03-06 21:16:20 +00:00
American_Jesus
3284a40084
Translated using Weblate (Portuguese)
Currently translated at 100.0% (499 of 499 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-03-06 21:16:20 +00:00
zyplex
49667bc053
Translated using Weblate (German)
Currently translated at 79.1% (395 of 499 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-03-06 21:16:20 +00:00
American_Jesus
46c7993836
Translated using Weblate (Portuguese)
Currently translated at 100.0% (499 of 499 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-03-06 21:16:20 +00:00
EmadAljohani
9daa6c9337
Added translation using Weblate (Arabic) 2026-03-06 21:16:20 +00:00
idezentas
321ef8965a
Translated using Weblate (Turkish)
Currently translated at 100.0% (499 of 499 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-03-06 21:16:20 +00:00
arcker95
ea4e4ad140
Translated using Weblate (Italian)
Currently translated at 100.0% (499 of 499 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-03-06 21:16:20 +00:00
danpergal84
b2aea426ef
Translated using Weblate (Spanish)
Currently translated at 100.0% (499 of 499 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-03-06 21:16:20 +00:00
Outbreak2096
fd53206aee
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (499 of 499 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-03-06 21:16:20 +00:00
SimonHung
7432df3eb1
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (499 of 499 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-03-06 21:16:20 +00:00
idezentas
9aaee3033e
Translated using Weblate (Turkish)
Currently translated at 100.0% (487 of 487 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-03-06 21:16:20 +00:00
Outbreak2096
c5316343b9
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (487 of 487 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-03-06 21:16:20 +00:00
arcker95
034f91ba3a
Translated using Weblate (Italian)
Currently translated at 100.0% (487 of 487 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-03-06 21:16:20 +00:00
SimonHung
a1d651ae21
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 97.1% (473 of 487 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-03-06 21:16:20 +00:00
American_Jesus
898191e44a
Translated using Weblate (Portuguese)
Currently translated at 93.6% (456 of 487 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-03-06 21:16:20 +00:00
danpergal84
691ae4a1a4
Translated using Weblate (Spanish)
Currently translated at 100.0% (487 of 487 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-03-06 21:16:20 +00:00
arcker95
c5d6eaa271
Translated using Weblate (Italian)
Currently translated at 97.1% (473 of 487 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-03-06 21:16:19 +00:00
idezentas
0c94a0dbd6
Translated using Weblate (Turkish)
Currently translated at 100.0% (472 of 472 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-03-06 21:16:19 +00:00
Outbreak2096
0f8b5db397
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (472 of 472 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-03-06 21:16:19 +00:00
arcker95
cf369b26de
Translated using Weblate (Italian)
Currently translated at 100.0% (472 of 472 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-03-06 21:16:19 +00:00
SimonHung
a479afd10a
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (472 of 472 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-03-06 21:16:19 +00:00
danpergal84
781c020b67
Translated using Weblate (Spanish)
Currently translated at 100.0% (472 of 472 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-03-06 21:16:19 +00:00
Ray
36162d6fc5
Revert "Integrate with libass-android to support SSA/ASS subtitles in ExoPlayer" (#1051)
Unfortunately, there seem to be issues with switching to
`libass-android`, so reverting those changes so I can cut a release and
troubleshoot later.

Reverts damontecres/Wholphin#569
2026-03-06 16:16:12 -05: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
83ebd922d9
Integrate with libass-android to support SSA/ASS subtitles in ExoPlayer (#569)
## Description
Add better ASS/SSA support to ExoPlayer by using
[`libass-android`](https://github.com/peerless2012/libass-android).

This is enabled with the "Use libass for ASS subtitles" advanced
settings for ExoPlayer. It is enabled by default.

### Related issues
Related to #22
2026-03-06 08:34:57 -05:00
Ray
014bed1bf3
Better logic for choosing the display mode (#1039)
## 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
2026-03-04 19:30:14 -05:00
Ray
354577e2e8
Delete media follow up (#1040)
## Description
A follow up to #1014 to add the delete option in more context menus.
Also improves the "reaction" logic which updates previous pages from
deletions on later pages.

### Related issues
Addresses
https://github.com/damontecres/Wholphin/pull/1014#issuecomment-3987205355
Related to #104

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-03-04 19:30:08 -05:00
Ray
ce6461d23b
Fixes for Jellyseerr login issues (#1031)
## 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
2026-03-04 15:11:09 -05:00
renovate[bot]
6867ba7657
Update Dependencies (#1013) 2026-03-03 14:35:11 -05:00
renovate[bot]
6ea5fd2121
Update actions/upload-artifact action to v7 (#1009)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[actions/upload-artifact](https://redirect.github.com/actions/upload-artifact)
| action | major | `v6` → `v7` |

---

### Release Notes

<details>
<summary>actions/upload-artifact (actions/upload-artifact)</summary>

###
[`v7`](https://redirect.github.com/actions/upload-artifact/compare/v6...v7)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v6...v7)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/damontecres/Wholphin).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4zNi4yIiwidXBkYXRlZEluVmVyIjoiNDMuMzYuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-03 14:34:44 -05:00
Ray
be90a42c57
Various fixes (#1030)
## 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
2026-03-03 13:46:55 -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
8935067c5a
Add option to delete media (#1014)
## Description
Adds a setting to enable "Media management" which adds a context menu
item to delete media. This is accessible using either the More button or
long clicking.

You can delete episodes, seasons, series and movies/videos.

If order for the option to appear the current user must have server-side
settings for "Allow media deletion from" enabled for the library and the
in-app setting under Settings->Advanced Settings->"Show media management
options".

### Related issues
Closes #104 

### Testing
Emulator

## Screenshots
### Context menu
![delete_context_2
Large](https://github.com/user-attachments/assets/cabc3c24-1b43-4f88-8d71-b7275f9eadc4)


### Confirmation
![delete_confirm
Large](https://github.com/user-attachments/assets/8036dde1-8a51-424b-bbdd-f2826dd3e0ab)

## AI or LLM usage
None
2026-03-02 15:38:41 -05:00
Justin Caveda
0b73b4cf64
Fix: optimize suggestions performance (#849)
## 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
2026-03-01 16:59:44 -05:00
voc0der
fd9063fbad
Hold-to-seek acceleration for playback DPAD and seekbar (#900)
## 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 #784  
Closes #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>
2026-02-28 14:35:02 -05:00
Ray
55466f803c
Better animation to show/hide headers on grid pages (#1015)
## Description
Changes the animation for showing/hiding the header title/buttons on
grid pages.

This eliminates the awkward jump the grid does to fill the screen.
Instead it smoothly resizes.

### Related issues
Fixes the second item in #221

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-27 01:00:44 -05:00
Ray
b65e55f4ed
A few small UI fixes (#1012)
## Description
- Restores corner text for episode cards
- Always show end time on playback overlay instead of it being
controlled by the "Show Clock" setting
- Better logic to determine person's role in media

### Related issues
Fixes #1010
Fixes #1004
Fixes #975

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-26 20:27:22 -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
37d52ef57e
Update readme images to v0.5.1 2026-02-25 20:38:16 -05:00
Ray
7f10b5a25e
Use Q&A for questions [skip ci] 2026-02-25 15:15:02 -05:00
Damontecres
138efcde00
Fix issue templates [skip ci] 2026-02-25 15:09:46 -05:00
Ray
9d857bc59e
Move new feature requests to discussions (#984)
## Description
Update the issue templates to direct new feature requests to GitHub
Discussions

### Related issues
Closes #982
2026-02-25 15:08:09 -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
renovate[bot]
536e8d366c
Update Dependencies to v3.4.0 (#970)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [io.coil-kt.coil3:coil-svg](https://redirect.github.com/coil-kt/coil)
| `3.3.0` → `3.4.0` |
![age](https://developer.mend.io/api/mc/badges/age/maven/io.coil-kt.coil3:coil-svg/3.4.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.coil-kt.coil3:coil-svg/3.3.0/3.4.0?slim=true)
|
| [io.coil-kt.coil3:coil-gif](https://redirect.github.com/coil-kt/coil)
| `3.3.0` → `3.4.0` |
![age](https://developer.mend.io/api/mc/badges/age/maven/io.coil-kt.coil3:coil-gif/3.4.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.coil-kt.coil3:coil-gif/3.3.0/3.4.0?slim=true)
|
|
[io.coil-kt.coil3:coil-network-okhttp](https://redirect.github.com/coil-kt/coil)
| `3.3.0` → `3.4.0` |
![age](https://developer.mend.io/api/mc/badges/age/maven/io.coil-kt.coil3:coil-network-okhttp/3.4.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.coil-kt.coil3:coil-network-okhttp/3.3.0/3.4.0?slim=true)
|
|
[io.coil-kt.coil3:coil-network-cache-control](https://redirect.github.com/coil-kt/coil)
| `3.3.0` → `3.4.0` |
![age](https://developer.mend.io/api/mc/badges/age/maven/io.coil-kt.coil3:coil-network-cache-control/3.4.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.coil-kt.coil3:coil-network-cache-control/3.3.0/3.4.0?slim=true)
|
|
[io.coil-kt.coil3:coil-compose](https://redirect.github.com/coil-kt/coil)
| `3.3.0` → `3.4.0` |
![age](https://developer.mend.io/api/mc/badges/age/maven/io.coil-kt.coil3:coil-compose/3.4.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.coil-kt.coil3:coil-compose/3.3.0/3.4.0?slim=true)
|
| [io.coil-kt.coil3:coil-core](https://redirect.github.com/coil-kt/coil)
| `3.3.0` → `3.4.0` |
![age](https://developer.mend.io/api/mc/badges/age/maven/io.coil-kt.coil3:coil-core/3.4.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/io.coil-kt.coil3:coil-core/3.3.0/3.4.0?slim=true)
|

---

### Release Notes

<details>
<summary>coil-kt/coil (io.coil-kt.coil3:coil-svg)</summary>

###
[`v3.4.0`](https://redirect.github.com/coil-kt/coil/blob/HEAD/CHANGELOG.md#340---Unreleased)

[Compare
Source](https://redirect.github.com/coil-kt/coil/compare/3.3.0...3.4.0)

- **New**: Add `ConcurrentRequestStrategy` to support combining
in-flight network requests for the same key.
([#&#8203;2995](https://redirect.github.com/coil-kt/coil/pull/2995),
[#&#8203;3326](https://redirect.github.com/coil-kt/coil/pull/3326))
- `DeDupeConcurrentRequestStrategy` enables this behavior and lets
waiters wait for the results of an in-flight network request.
- This behavior is experimental and is currently disabled by default.
- Currently, requests are always combined based on their `diskCacheKey`.
- `OkHttpNetworkFetcherFactory`, `KtorNetworkFetcherFactory`, and
`NetworkFetcher.Factory` now accept `concurrentRequestStrategy`.
- **New**: Decode images on JS/WASM using a web worker to avoid blocking
the browser main thread.
([#&#8203;3305](https://redirect.github.com/coil-kt/coil/pull/3305))
- **New**: Add support for Linux native targets (`linuxX64` and
`linuxArm64`) for non-Compose multiplatform artifacts.
([#&#8203;3054](https://redirect.github.com/coil-kt/coil/pull/3054))
- **New**: Add Compose-only APIs to improve transitions between
subsequent requests.
([#&#8203;3141](https://redirect.github.com/coil-kt/coil/pull/3141),
[#&#8203;3175](https://redirect.github.com/coil-kt/coil/pull/3175))
- `ImageRequest.Builder.useExistingImageAsPlaceholder` enables
crossfading from the previous image when no placeholder is set.
- `ImageRequest.Builder.preferEndFirstIntrinsicSize` lets
`CrossfadePainter` prefer the end painter's intrinsic size.
- **New**: Add `ImageLoader.Builder.repeatCount(Int)` in `coil-gif` to
set a global animated image repeat count.
([#&#8203;3143](https://redirect.github.com/coil-kt/coil/pull/3143))
- **New**: Add support for preferring embedded video thumbnails in
`coil-video`.
([#&#8203;3107](https://redirect.github.com/coil-kt/coil/pull/3107))
- **New**: Publish `coil-lint` with `coil-core` and add a lint check to
catch accidental `kotlin.error()` calls in `ImageRequest.Builder`
blocks.
([#&#8203;3304](https://redirect.github.com/coil-kt/coil/pull/3304))
- Set Kotlin language version to 2.1.
([#&#8203;3302](https://redirect.github.com/coil-kt/coil/pull/3302))
- Make `BitmapFetcher` available in common code.
([#&#8203;3286](https://redirect.github.com/coil-kt/coil/pull/3286))
- Use `applicationContext` when creating the singleton `ImageLoader` on
Android.
([#&#8203;3246](https://redirect.github.com/coil-kt/coil/pull/3246))
- Cache eligible non-2xx HTTP responses by default (e.g. `404`) and stop
caching non-cacheable responses (e.g. `500`).
([#&#8203;3137](https://redirect.github.com/coil-kt/coil/pull/3137),
[#&#8203;3139](https://redirect.github.com/coil-kt/coil/pull/3139))
- Fix potential race condition when consuming OkHttp response bodies.
([#&#8203;3186](https://redirect.github.com/coil-kt/coil/pull/3186))
- Fix `maxBitmapSize` edge case to prevent oversized bitmap crashes on
Android.
([#&#8203;3259](https://redirect.github.com/coil-kt/coil/pull/3259))
- Update Kotlin to 2.3.10.
- Update Compose to 1.9.3.
- Update Okio to 3.16.4.
- Update Skiko to 0.9.22.2.
- Update `kotlinx-io-okio` to 0.9.0.
- Update `androidx.core` to 1.16.0.
- Update `androidx.lifecycle` to 2.9.4.
- Update `androidx.exifinterface` to 1.4.2.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/damontecres/Wholphin).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4zMi4wIiwidXBkYXRlZEluVmVyIjoiNDMuMzIuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-24 18:31:52 -05:00
Damontecres
1b94ab9bec
Release v0.5.1
Some checks failed
Create release / publish (push) Has been cancelled
2026-02-24 16:15:25 -05:00
Damontecres
69be7311ab
Fix verify step 2026-02-24 16:15:04 -05:00
Damontecres
33cd4d04aa
Release v0.5.1 2026-02-24 16:01:52 -05:00
Ray
14483410fd
Translations 2026-02-24 (#969)
Translation updates as on 2026-02-24

Thank you to all contributors!
2026-02-24 16:00:44 -05:00
SimonHung
043f8cd3cf
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 95.3% (450 of 472 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-02-24 17:37:27 +00:00
Fjuro
3bdd177230
Translated using Weblate (Czech)
Currently translated at 100.0% (472 of 472 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-02-24 17:37:27 +00:00
RafaelSoaresP
3b736a136b
Translated using Weblate (Portuguese (Brazil))
Currently translated at 81.9% (387 of 472 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt_BR/
2026-02-24 17:37:27 +00:00
SimonHung
a55399cb68
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 92.5% (437 of 472 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-02-24 17:37:27 +00:00
danpergal84
0ce2ff907c
Translated using Weblate (Spanish)
Currently translated at 100.0% (472 of 472 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-02-24 17:37:27 +00:00
SimonHung
2a90d12cbc
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 91.3% (431 of 472 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-02-24 17:37:27 +00:00
danpergal84
34174cea55
Translated using Weblate (Spanish)
Currently translated at 91.9% (434 of 472 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-02-24 17:37:27 +00:00
ymir
f1c2ff4530
Translated using Weblate (French)
Currently translated at 100.0% (416 of 416 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-02-24 17:37:27 +00:00
opakholis
ca425422be
Translated using Weblate (Indonesian)
Currently translated at 100.0% (416 of 416 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-02-24 17:37:27 +00:00
Sathen
bfa0cd0b05
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (416 of 416 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/uk/
2026-02-24 17:37:27 +00:00
Fjuro
0a4f21cc11
Translated using Weblate (Czech)
Currently translated at 100.0% (416 of 416 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-02-24 17:37:27 +00:00
n8llcaster
191966124b
Translated using Weblate (Slovak)
Currently translated at 53.6% (223 of 416 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2026-02-24 17:37:27 +00:00
idezentas
e536c0a9e7
Translated using Weblate (Turkish)
Currently translated at 100.0% (416 of 416 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-02-24 17:37:27 +00:00
danyrd92
40a3d70ba8
Translated using Weblate (Spanish)
Currently translated at 100.0% (416 of 416 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-02-24 17:37:27 +00:00
arcker95
4ad96ea29e
Translated using Weblate (Italian)
Currently translated at 100.0% (416 of 416 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-02-24 17:37:27 +00:00
alexkahler
6ef4356ad1
Translated using Weblate (Danish)
Currently translated at 97.1% (404 of 416 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-02-24 17:37:26 +00:00
idezentas
6ffc2903f3
Translated using Weblate (Turkish)
Currently translated at 100.0% (416 of 416 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-02-24 17:37:26 +00:00
Outbreak2096
5d4219f933
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (416 of 416 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-02-24 17:37:26 +00:00
Priit Jõerüüt
89bdae9cd7
Translated using Weblate (Estonian)
Currently translated at 100.0% (416 of 416 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-02-24 17:37:26 +00:00
Ray
a14fdac852
A few more home page fixes (#967)
## Description
A few more home page fixes
* Focus issue between settings & preset buttons
* Fix default genre card size for "Wholphin Default" preset
* Cache genre image urls for 2 hours so that they don't change on every
refresh

### Related issues
Related to #399

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-24 12:37:21 -05:00
renovate[bot]
c4cd1fbfd3
Update actions/checkout action to v6 (#965)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://redirect.github.com/actions/checkout) |
action | major | `v5` → `v6` |

---

### Release Notes

<details>
<summary>actions/checkout (actions/checkout)</summary>

### [`v6`](https://redirect.github.com/actions/checkout/compare/v5...v6)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v5...v6)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/damontecres/Wholphin).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNi41IiwidXBkYXRlZEluVmVyIjoiNDMuMjYuNSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-24 10:24:26 -05:00
Ray
7424f812d8
Dev: add CI to build app store releases (#468)
Adding GHA workflows to build the app store releases

Also cleans up gradle build script a bit
2026-02-23 22:04:08 -05:00
Ray
3e2a1869ab
Scroll to current chapter on playback overlay (#964)
## Description
This PR makes it so when you open the playback overlay and move down to
the "Chapters" row, the first focused chapter will be the current one
instead of always the first one.

### Related issues
Closes #695
Closes #951

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-23 18:52:38 -05:00
renovate[bot]
790bb4b535
Update dependency com.google.dagger.hilt.android to v2.59.2 (#937)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
|
[com.google.dagger.hilt.android](https://redirect.github.com/google/dagger)
| `2.59.1` → `2.59.2` |
![age](https://developer.mend.io/api/mc/badges/age/maven/com.google.dagger.hilt.android:com.google.dagger.hilt.android.gradle.plugin/2.59.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/com.google.dagger.hilt.android:com.google.dagger.hilt.android.gradle.plugin/2.59.1/2.59.2?slim=true)
|

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/damontecres/Wholphin).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNi41IiwidXBkYXRlZEluVmVyIjoiNDMuMjYuNSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-23 18:00:53 -05:00
renovate[bot]
781fc93fa3
Update Dependencies to v2.59.2 (#936)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
|
[com.google.dagger:hilt-android-compiler](https://redirect.github.com/google/dagger)
| `2.59.1` → `2.59.2` |
![age](https://developer.mend.io/api/mc/badges/age/maven/com.google.dagger:hilt-android-compiler/2.59.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/com.google.dagger:hilt-android-compiler/2.59.1/2.59.2?slim=true)
|
|
[com.google.dagger:hilt-android](https://redirect.github.com/google/dagger)
| `2.59.1` → `2.59.2` |
![age](https://developer.mend.io/api/mc/badges/age/maven/com.google.dagger:hilt-android/2.59.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/com.google.dagger:hilt-android/2.59.1/2.59.2?slim=true)
|

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/damontecres/Wholphin).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNi41IiwidXBkYXRlZEluVmVyIjoiNDMuMjYuNSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-23 18:00:42 -05:00
renovate[bot]
d483ebf735
Update Gradle to v9.3.1 (#869)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [gradle](https://gradle.org)
([source](https://redirect.github.com/gradle/gradle)) | minor | `9.2.1`
→ `9.3.1` |

---

### Release Notes

<details>
<summary>gradle/gradle (gradle)</summary>

###
[`v9.3.1`](https://redirect.github.com/gradle/gradle/releases/tag/v9.3.1):
9.3.1

[Compare
Source](https://redirect.github.com/gradle/gradle/compare/v9.3.0...v9.3.1)

This is a patch release for 9.3.0. We recommend using 9.3.1 instead of
9.3.0.

The following issues were resolved:

- [Cannot find testcases from Android Screenshot Test plugin since
Gradle 9.3.0](https://redirect.github.com/gradle/gradle/issues/36320)
- [Excluding dependencies from included builds doesn't work in Gradle
9.3.0](https://redirect.github.com/gradle/gradle/issues/36331)
- [ExternalDependency and DependencyConstraint cannot be passed to
DependencyResolveDetails#useTarget](https://redirect.github.com/gradle/gradle/issues/36359)
- [Gradle 9.3.0 generate JUnit test result files with wrong
name](https://redirect.github.com/gradle/gradle/issues/36379)
- [Build cache cannot handle outputs with non-BMP characters in the
filename](https://redirect.github.com/gradle/gradle/issues/36387)
- [Emojis in test names should not break build
caching](https://redirect.github.com/gradle/gradle/issues/36395)
- [Non utf-8 c code is no longer
buildable](https://redirect.github.com/gradle/gradle/issues/36399)
- [Breaking change in 9.3.0 regarding cross-project dependency
manipulation](https://redirect.github.com/gradle/gradle/issues/36428)
- [JUnit3 tests cannot be run with Gradle
9.3.0](https://redirect.github.com/gradle/gradle/issues/36451)
- [Test.setScanForTestClasses(false) causes all junit4 tests to be
skipped](https://redirect.github.com/gradle/gradle/issues/36508)

[Read the Release
Notes](https://docs.gradle.org/9.3.1/release-notes.html)

#### Upgrade instructions

Switch your build to use Gradle 9.3.1 by updating your wrapper:

```
./gradlew wrapper --gradle-version=9.3.1 && ./gradlew wrapper
```

See the Gradle [9.x upgrade
guide](https://docs.gradle.org/9.3.1/userguide/upgrading_version_9.html)
to learn about deprecations, breaking changes and other considerations
when upgrading.

For Java, Groovy, Kotlin and Android compatibility, see the [full
compatibility
notes](https://docs.gradle.org/9.3.1/userguide/compatibility.html).

#### Reporting problems

If you find a problem with this release, please file a bug on [GitHub
Issues](https://redirect.github.com/gradle/gradle/issues) adhering to
our issue guidelines.
If you're not sure you're encountering a bug, please use the
[forum](https://discuss.gradle.org/c/help-discuss).

We hope you will build happiness with Gradle, and we look forward to
your feedback via [Twitter](https://twitter.com/gradle) or on
[GitHub](https://redirect.github.com/gradle).

###
[`v9.3.0`](https://redirect.github.com/gradle/gradle/compare/v9.2.1...v9.3.0)

[Compare
Source](https://redirect.github.com/gradle/gradle/compare/v9.2.1...v9.3.0)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/damontecres/Wholphin).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi45Ny4wIiwidXBkYXRlZEluVmVyIjoiNDMuMjIuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-23 18:00:31 -05:00
Ray
348a3022d6
Fix cut off cards on grids with show details enabled (#963)
## Description
Fixes the previous row of cards showing the bottom cut off when the
"Show details" view option is enabled.

### Related issues
Fixes #404
Fixes #877
Related to #882 

### Testing
Emulator

## Screenshots
![grid_show_details
Large](https://github.com/user-attachments/assets/3376275e-261e-4e5a-b874-18be7606a9bf)

## AI or LLM usage
None
2026-02-23 15:20:25 -05:00
Ray
3b162a2f98
Play theme songs for collections (#962)
## Description
Play theme songs for collections if available

### Related issues
Closes #711

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-23 13:38:02 -05:00
Damontecres
e1c66dfb1d
Fix series overview episode images 2026-02-23 12:34:46 -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
a44fad890f
Add preset for home page episode thumbnails (#954)
## Description
Adds a preset for "Episode thumbnails" which shows episodes using their
Primary image (ie a thumbnail or screenshot of the episode).

The previous "Thumbnails" preset is renamed "Series Thumbs" because it
uses the episode's Series Thumb image.

Also fixes a bug where toggling "Use series" might not update the home
page preview automatically.

### Related issues
Fixes #940

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-23 09:56:22 -05:00
Ray
a51d9e9c65
Fix clicking genre cards on home page (#953)
## Description
Fixes clicking on a genre card added to the home page

### Related issues
Fixes #952

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-22 20:18:18 -05:00
YogiBear12
e8c9b4e208
SeriesOverview Alignment (#930)
## Description
This PR changes the padding on the SeriesOverview to align it with the
updated layout on the Home, MovieDetails and SeriesDetails pages.

There is also a small change to fix an incorrect spacing on the
SeriesDetails, and start padding added to the ItemRow to bring it into
alignment with other text elements on the page and ensure consistent
spacing from the navdrawer

### Related issues
I believe this is the final PR to close out
[704](https://github.com/damontecres/Wholphin/discussions/704)

### Testing
Tested changes on both the emulator and physical tv

## Screenshots
Home screen showing the ItemRow alignment
<img width="1920" height="1080" alt="home"
src="https://github.com/user-attachments/assets/b338e2b4-2c53-45a1-b532-544263632230"
/>

SeriesOverview
<img width="1920" height="1080" alt="Screenshot_20260221_200450"
src="https://github.com/user-attachments/assets/51710ade-5155-41d9-8b89-0678c71e0c76"
/>


## AI or LLM usage
AI was used to identify the current padding and spacing values
2026-02-22 10:27:05 -05:00
Ray
cc052453a4
Add basic music video support (#948)
## Description
Just enables showing and playing music videos as if they were regular
videos. Basic metadata is shown as well.

Navigation isn't great because a music video library goes by folders
which don't necessary have the best UI choices.

### Related issues
Related to #267 

### Testing
Checked playback on the emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-21 15:30:16 -05:00
Cristiano Chelotti
55b148971e
Fixing Settings sticky header text overlapping with menu items (#928)
## Description
- Adding a background for the Settings stickyHeader. This helps with
visibility and prevents overlap with the menu items when scrolling.
### Related issues
In the Settings screen, the header can overlap the text of the menu
items when scrolling down. This is a minor UI bug only and does not
affect functionality at all, but the fix seemed easy and harmless so I
figured I'd give it a go.

### Testing
Tested manually on TCL model 50S434 running Android TV. Tested manually
using AVD emulation on Television (1080p) and Television (4K) preset
devices. Reproduced the problem on all 3 devices and confirmed that my
fix works across all.

## Screenshots
Before:
<img width="1920" height="1080" alt="before-settings-fix"
src="https://github.com/user-attachments/assets/15f9916d-2583-4dc4-8302-cb1f3a395b1a"
/>
After:
<img width="1920" height="1080" alt="after-settings-fix"
src="https://github.com/user-attachments/assets/3f8781aa-7063-4eb4-bf4a-95ff34b9b1a1"
/>


## AI or LLM usage
None

---------

Co-authored-by: Ray <154766448+damontecres@users.noreply.github.com>
2026-02-20 19:24:35 -05:00
Ray
7939fffd48
Override H264/AVC High 10 Profile Level 5.2 support for some devices (#935)
## Description
Several Fire TV devices natively support H264/AVC High 10 Profile Level
5.2 according to [their
specs](https://developer.amazon.com/docs/device-specs/device-specifications-fire-tv-streaming-media-player.html),
but the `MediaCodecInfo` doesn't return this support.

So this PR adds an override for the known devices that support this
which prevents unnecessary transcoding.

### Related issues
Closes #786

### Testing
Verified on a Fire TV 4k Max 2nd Gen

## Screenshots
N/A

## AI or LLM usage
None
2026-02-20 16:07:22 -05:00
Ray
4f8f69c438
Better Jellyseerr connection error handling (#933)
## Description
Show Jellyseerr connection errors in settings. If an error occurred
trying to login, in setting it will show "Server error" and clicking
gives more error info and option to remove the server.

### Related issues
Fixes #907 

### Testing
Emulator with simulated errors

## Screenshots
N/A

## AI or LLM usage
None
2026-02-20 14:29:58 -05:00
Ray
dfd9acf561
Add metadata to media session (#934)
## Description
Adds some metadata about playing media for consumption in remote control
apps such as Google Home or others.

### Related issues
Partially address #889, doesn't add explicit controls yet, but Google
Home works

### Testing
Nvidia shield + Google Home app

## Screenshots
iOS Google Home app:

![ios_google_home](https://github.com/user-attachments/assets/680ccae5-b66e-404a-a3a1-7c89991d841b)


## AI or LLM usage
None
2026-02-20 14:28:57 -05:00
Damontecres
a8ab002dd1
Release v0.5.0
Some checks failed
Create release / publish (push) Has been cancelled
2026-02-20 12:08:08 -05:00
Ray
8113369546
Update readme 2026-02-20 12:07:36 -05:00
Ray
86b50acc76
Translation 2026-02-20 (#931)
Batch of translations as of 2026-02-20

Thank you to all of the contributors!!
2026-02-20 12:03:13 -05:00
Damontecres
2b7a1ecd48
Fix linting 2026-02-20 10:50:07 -05:00
alexkahler
9b10fcd659
Translated using Weblate (Danish)
Currently translated at 57.8% (240 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-02-20 02:51:03 +00:00
alexkahler
17b0a09b7a
Translated using Weblate (Danish)
Currently translated at 21.9% (91 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-02-20 02:51:03 +00:00
alexkahler
9cffc25756
Translated using Weblate (Danish)
Currently translated at 21.4% (89 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/da/
2026-02-20 02:51:03 +00:00
alexkahler
3dbf080e4c
Added translation using Weblate (Danish) 2026-02-20 02:51:03 +00:00
RafaelSoaresP
b1196d2df3
Translated using Weblate (Portuguese (Brazil))
Currently translated at 7.7% (32 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt_BR/
2026-02-20 02:51:03 +00:00
Priit Jõerüüt
46637dca50
Translated using Weblate (Estonian)
Currently translated at 100.0% (415 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-02-20 02:51:03 +00:00
American_Jesus
fb79eff3c7
Added translation using Weblate (Portuguese (Brazil)) 2026-02-20 02:51:03 +00:00
Priit Jõerüüt
7595fad3a1
Translated using Weblate (Estonian)
Currently translated at 94.4% (392 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-02-20 02:51:03 +00:00
ymir
42b686f429
Translated using Weblate (French)
Currently translated at 100.0% (415 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-02-20 02:51:03 +00:00
RafaelSoaresP
9f66c68e92
Translated using Weblate (Portuguese)
Currently translated at 100.0% (415 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-02-20 02:51:03 +00:00
danpergal84
2a3defdcd0
Translated using Weblate (Spanish)
Currently translated at 100.0% (415 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-02-20 02:51:03 +00:00
opakholis
5965fde6b1
Translated using Weblate (Indonesian)
Currently translated at 100.0% (415 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-02-20 02:51:03 +00:00
Priit Jõerüüt
d0c44d4342
Translated using Weblate (Estonian)
Currently translated at 92.5% (384 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-02-20 02:51:03 +00:00
American_Jesus
5ce702b475
Translated using Weblate (Portuguese)
Currently translated at 98.5% (409 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-02-20 02:51:03 +00:00
DoTheBarrelRoll
0d6b82bce0
Added translation using Weblate (Finnish) 2026-02-20 02:51:03 +00:00
Outbreak2096
124b6a741e
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (415 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-02-20 02:51:03 +00:00
Fjuro
12499d1f38
Translated using Weblate (Czech)
Currently translated at 100.0% (415 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-02-20 02:51:03 +00:00
arcker95
5df9791a3c
Translated using Weblate (Italian)
Currently translated at 100.0% (415 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-02-20 02:51:03 +00:00
SimonHung
419cb68af4
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (415 of 415 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-02-20 02:51:03 +00:00
idezentas
7113b43809
Translated using Weblate (Turkish)
Currently translated at 100.0% (414 of 414 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-02-20 02:51:03 +00:00
Fjuro
ce79afcd68
Translated using Weblate (Czech)
Currently translated at 100.0% (414 of 414 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-02-20 02:51:03 +00:00
Outbreak2096
8c997518aa
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (414 of 414 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-02-20 02:51:03 +00:00
arcker95
29063a20e7
Translated using Weblate (Italian)
Currently translated at 100.0% (414 of 414 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-02-20 02:51:02 +00:00
SimonHung
b9cb93f22e
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 96.1% (398 of 414 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-02-20 02:51:02 +00:00
SimonHung
8253b62775
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 94.6% (392 of 414 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-02-20 02:51:02 +00:00
Outbreak2096
f14514f279
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (392 of 392 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-02-20 02:51:02 +00:00
arcker95
c1bd8010b3
Translated using Weblate (Italian)
Currently translated at 100.0% (392 of 392 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-02-20 02:51:02 +00:00
flipip
a0cf71166c
Translated using Weblate (German)
Currently translated at 97.4% (382 of 392 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-02-20 02:51:02 +00:00
arcker95
99d2f37114
Translated using Weblate (Italian)
Currently translated at 100.0% (387 of 387 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-02-20 02:51:02 +00:00
idezentas
543e521972
Translated using Weblate (Turkish)
Currently translated at 100.0% (387 of 387 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-02-20 02:51:02 +00:00
Fjuro
fe9968227f
Translated using Weblate (Czech)
Currently translated at 100.0% (387 of 387 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-02-20 02:51:02 +00:00
arcker95
8530ba334f
Translated using Weblate (Italian)
Currently translated at 99.7% (386 of 387 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-02-20 02:51:02 +00:00
SimonHung
9e1e065b95
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (387 of 387 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-02-20 02:51:02 +00:00
idezentas
a114740614
Translated using Weblate (Turkish)
Currently translated at 100.0% (387 of 387 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-02-20 02:51:02 +00:00
Ray
65f20e2daf
Fix a few customize home bugs (#929)
## Description
Hopefully this is the last round of bug fixes for customizing the home
page!

Fixes a possible crash when scrolling after adding a row after resetting
to default or loading remote settings that have more rows than the
original settings

Fixes some incorrect titles

### Related issues
Related to #399 & #803

### Testing
Emulator & nvidia shield

## Screenshots
N/A

## AI or LLM usage
None
2026-02-19 21:50:55 -05:00
Ray
219e4b01db
Add a collection or playlist to home page (#915)
## Description
Adds a basic way to add a collection or playlist as a row on the home
page.

There's a simple search dialog to find the collection or playlist.

### Related issues
Related to #399 & #803 

### Testing
Emulator testing

## Screenshots
N/A

## AI or LLM usage
None
2026-02-19 19:18:39 -05:00
Ray
9daf679f7d
Fix some image issues (#926)
## Description
Fixes/improves thumb image handling by falling back to backdrop images
if available. Episodes without a parent thumb/backdrop will fall back to
their primary image.

Also fixes the nav drawer selected item not updating (bug introduced by
#886).

### Related issues
Fixes #642
Fixes #914

### Testing
Emulator in various situations with missing thumbs

## Screenshots
N/A

## AI or LLM usage
None
2026-02-19 19:18:14 -05:00
YogiBear12
f57cba85f2
Title/subtitle style update (#775)
## Description
This PR updates cards that have a title and subtitle, so that they are
visually distinct instead of sharing the same font and style.
Titles use bodyMedium typography with semiBold weight. 
Subtitles use bodySmall typography with normal weight.

This affects:
- PersonCard
- EpisodeCard
- DiscoverItemCard
- GridCard
- SeasonCard

### Related issues
Related to [735](https://github.com/damontecres/Wholphin/issues/735)

### Screenshots
<img width="960" height="540" alt="Screenshot_20260125_190057"
src="https://github.com/user-attachments/assets/4bbf29a3-3df8-482b-944e-454a9b091e44"
/>

### AI/LLM usage
No LLM usage
2026-02-19 17:28:21 -05:00
Cristiano Chelotti
8bfe77e6e4
Fixing seek text label overlap and visibility issues. (#923)
## Description
- Smoothly bumps up the title and subtitle text above the seek progress
label when the seek bar is focused to prevent overlap.
- Adds a semi-transparent black backer to the text label that appears
above the seek progress indicator. Helps with visibility when the image
in the playback matches the color of the text. Note: the backer is black
with Alpha of 0.6 for now because I felt that looked good for contrast
across all themes. However, if you prefer it respect a particular theme
color, I'd be willing to make that change.

### Related issues
In the playback screen, when focusing the seek bar on Android TVs (at
least the ones I was able to test), the seek text above the seek bar
will overlap with the media title. This is a purely cosmetic issue in
the UI, but it seemed worth a fix. See before and after images in in the
Screenshots section below

This problem is briefly mentioned in point number 2 of [#528
](https://github.com/damontecres/Wholphin/issues/528)

### Testing
Tested manually on TCL model 50S434 running Android TV. Tested manually
using AVD emulation on Television (1080p) and Television (4K) preset
devices. Reproduced the problem on all 3 devices and confirmed that my
fix works across all.

## Screenshots
Before:
<img width="2046" height="1184" alt="before"
src="https://github.com/user-attachments/assets/64708e13-3fa4-4248-91e3-e203f8ac1f7d"
/>

After:
<img width="2046" height="1183" alt="after"
src="https://github.com/user-attachments/assets/db9d1767-cf93-4153-b13a-9a18fbc89e0c"
/>

## AI or LLM usage
None

---------

Co-authored-by: Ray <154766448+damontecres@users.noreply.github.com>
2026-02-19 16:18:20 -05:00
renovate[bot]
c488c05c12
Update dependency com.google.devtools.ksp to v2.3.6 (#910)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [com.google.devtools.ksp](https://goo.gle/ksp)
([source](https://redirect.github.com/google/ksp)) | `2.3.5` → `2.3.6` |
![age](https://developer.mend.io/api/mc/badges/age/maven/com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin/2.3.6?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin/2.3.5/2.3.6?slim=true)
|

---

### Release Notes

<details>
<summary>google/ksp (com.google.devtools.ksp)</summary>

###
[`v2.3.6`](https://redirect.github.com/google/ksp/releases/tag/2.3.6)

[Compare
Source](https://redirect.github.com/google/ksp/compare/2.3.5...2.3.6)

#### What's Changed

- Fixed an issue where module recompilation would fail on Windows
environments when KSP2 was enabled
([#&#8203;2774](https://redirect.github.com/google/ksp/issues/2774))
- Resolved an issue where generated Java sources were ignored when using
Android Kotlin Multiplatform with IP-compatible paths
([#&#8203;2744](https://redirect.github.com/google/ksp/issues/2744))
- Fixed a KSP version 2.3.5 CI error exception that does not break build
checks
([#&#8203;2763](https://redirect.github.com/google/ksp/issues/2763))
- Added symbol-processing-api and common-deps to compile dependencies
([#&#8203;2789](https://redirect.github.com/google/ksp/issues/2789))
- Improved the detection of built-in Kotlin by removing the reliance on
KotlinBaseApiPlugin
([#&#8203;2772](https://redirect.github.com/google/ksp/issues/2772))
- A back-port of a performance optimization in the Intellij / Analysis
API ([2785](https://redirect.github.com/google/ksp/pull/2785) )

#### Contributors

- Thanks to [@&#8203;salmanmkc](https://redirect.github.com/salmanmkc),
[@&#8203;jaschdoc](https://redirect.github.com/jaschdoc),
[@&#8203;gurusai-voleti](https://redirect.github.com/gurusai-voleti) and
everyone who reported bugs and participated in discussions!

**Full Changelog**:
<https://github.com/google/ksp/compare/2.3.5...2.3.6>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/damontecres/Wholphin).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMi4wIiwidXBkYXRlZEluVmVyIjoiNDMuMjIuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-18 15:57:48 -05:00
renovate[bot]
31ff1b2072
Update dependency org.openapi.generator to v7.20.0 (#904)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| org.openapi.generator | `7.19.0` → `7.20.0` |
![age](https://developer.mend.io/api/mc/badges/age/maven/org.openapi.generator:org.openapi.generator.gradle.plugin/7.20.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/org.openapi.generator:org.openapi.generator.gradle.plugin/7.19.0/7.20.0?slim=true)
|

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/damontecres/Wholphin).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNS4zIiwidXBkYXRlZEluVmVyIjoiNDMuMTUuMyIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-18 15:57:16 -05:00
Ray
8c2227bf67
Various fixes for home page customization (#902)
## Description
Fixes several issues with home page customization such as
- Displaying wrong episode-specific display options
- Smaller text on cards without images
- Prefer fill scaling for posters, like the old home page

### Related issues
Related to #803 

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-18 13:40:18 -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
Ray
0276c5f49b
Fix reordering the nav drawer items not always saving the changes (#898)
## Description
Fixes the nav drawer preference reordering & save logic. Also improves
the UI a bit with animations.

It's also now stored in memory and saved when the dialog is dismissed.

### Related issues
Related to #886

### Testing
Emulator mostly

## Screenshots
N/A

## AI or LLM usage
None
2026-02-14 16:13:45 -05:00
Ray
44eb50910d
Fixes unable to dismiss next up during credits (#899)
## Description
Fixes an issue where the "Next Up" popup continuously pops up when
dismissed if you have "Show next up during credits/outro" enabled.

### Related issues
Fixes #895
Bug introduced by #884

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-14 16:13:38 -05:00
Ray
1e810c1157
Some fixes to customize homes (#893)
## Description
- Update header when editing a row
- Exclude non-empty rows to prevent scroll lock
- Only save settings locally if they are different from the source

### Related issues
Related to #803 

### Testing
Emulator, tested remote & local settings changes

## Screenshots
N/A

## AI or LLM usage
None
2026-02-13 16:30:03 -05:00
Ray
aa69646b2d
Upgrade AGP to 9.x (#892)
## Description
This PR has no user facing changes.

Upgrade AGP to 9.x and Gradle to 9.x. Also updates some dependencies as
well.

Note: still using deprecated `android.newDsl=false` because the protobuf
plugin is not yet compatible:
https://github.com/google/protobuf-gradle-plugin/issues/793.

### Related issues
N/A

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-13 14:49:19 -05:00
Ray
91900af7a9
Improved home page support for Live TV & Recordings (#890)
## 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
2026-02-13 11:41:47 -05:00
Ray
7fcd66f407
Integrate nav drawer settings for default home page (#888)
## 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
2026-02-12 21:19:33 -05:00
Ray
17b0eef5c5
Add ability to order navigation drawer items (#886)
## Description
Allows for reordering the items in the navigation drawer

This does not change the home page row order since #803 decouples the
nav drawer and home page rows.

The initial order will be the "Library Order" settings on the web under
Profile->Home. Making any changes locally in Wholphin will set the
order. If any new libraries are added or if Seerr integration is
enabled, these will added to the end of the "pinned" list.

### Related issues
Closes #822
Related to #399 & #803

### Testing
Tested on emulator
- Re-ordering
- Granting/Revoking user permission to a new library

## Screenshots
N/A, nav drawer is the same, just sorted differently

## AI or LLM usage
None
2026-02-12 19:55:12 -05:00
Ray
fcba1c7444
Customize home page (#803)
## 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 #399
Closes #361
Closes #282
Related to #340
2026-02-12 19:54:51 -05:00
Damontecres
b7679b3aa1
Release v0.4.3
Some checks failed
Create release / publish (push) Has been cancelled
2026-02-12 19:26:19 -05:00
Ray
3f0dedd71e
Fix error pages overlapping with nav drawer (#887)
## Description
As of #842, if an error occurred on some pages that display a generic
error message, it would be shown under the nav drawer making it
difficult to read.

This PR fixes that by passing the `modifier` into the `ErrorMessage`
composables ensuring it has proper offset & padding. Also, for
consistency, passes it the `LoadingPage`s.

### Related issues
Caused by #842

### Testing
Threw an exception during home page loading to observe the issue and fix
on the emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-12 19:21:16 -05:00
Justin Caveda
11fd780b31
Fix suggestions cache miss handling for missing files (#880)
## 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>
2026-02-12 18:27:59 -05:00
Ray
4161ea418c
Fixes the skip segment button appearance behavior (#884)
## Description
Fixes the segment (intro, credits, etc) skip button appearance behavior

For the button shown on the video: if the button for a given segment is
clicked, dismissed by the back button, or times out after 10 seconds, it
will never appear again even if you rewind the video.

However, the current non-ignored segment will always show a button on
the playback controls though so it's possible to still use it.

### Related issues
Fixes #875

### Testing
Tested a bunch of scenarios on the emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-11 20:06:15 -05:00
voc0der
028553dc91
Add Quick Connect authorization to Settings (#820)
## Description
- Adding `Quick Connect` drawer to the Settings under the Seer
integration, and before Additional options.
- When interacted with, A inline-dialog appears (after internally
resetting itself), with gutter options Cancel / OK, like existing
dialogs, and when that is completed, where it completes the other side
of `Quick Connect`, e.g. login to your phone Jellyfin app by using
Wholphin.
- Failure and success both are indicated to the user and if success, it
closes the window.

### Related issues
Related to #819 
Closes #819

### Testing
Tested on development build from my repo on a Nvidia Shield 2019.
Performed several logout/login, both success and fail to observe dialog
state and function.

## AI or LLM usage
Used Claude Sonnet 4.5 for most of the code, ChatGPT, reviewed it, then
I reviewed it, made a few tweaks.
2026-02-11 20:06:03 -05:00
Ray
83e5a44f37
Fixes glitches when scrolling on the home page (#882)
## Description
Fixes the possible glitch on some devices when scrolling left-right on
rows on the home page

### Related issues
Fixes #367
Fixes #872

### Testing
Emulator & shield 2019

## Screenshots
N/A

## AI or LLM usage
None
2026-02-11 16:07:22 -05:00
Ray
6e676b9474
Fixes overscrolling on recommended tabs (#881)
## Description
If the continue watching row is empty on recommended pages, it would
scroll to the first successfully loaded row even if previous ones are
still pending.

This PR fixes that so the page waits until the right first row is ready.

### Related issues
Fixes #839

### Testing
Emulator & shield 2019

## Screenshots
N/A

## AI or LLM usage
None
2026-02-11 16:05:11 -05:00
Damontecres
724d4d0da3
Release v0.4.2
Some checks failed
Create release / publish (push) Has been cancelled
2026-02-10 15:55:45 -05:00
Ray
a9c32edd5d
Update translations 2026-02-10 (#874)
Updating translations as of 2026-02-10

Many thanks to the contributors!
2026-02-10 15:45:50 -05:00
Damontecres
073f030d20
Fix spanish plurals 2026-02-10 15:14:08 -05:00
American_Jesus
9f2ca8ca18
Translated using Weblate (Portuguese)
Currently translated at 100.0% (387 of 387 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-02-10 19:59:42 +00:00
Outbreak2096
f539f69bb6
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (387 of 387 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-02-10 19:59:42 +00:00
Fjuro
2cfd130853
Translated using Weblate (Czech)
Currently translated at 100.0% (384 of 384 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-02-10 19:59:42 +00:00
Outbreak2096
c01235d183
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (384 of 384 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-02-10 19:59:42 +00:00
arcker95
bdebe38b60
Translated using Weblate (Italian)
Currently translated at 100.0% (384 of 384 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-02-10 19:59:42 +00:00
opakholis
b51052acbb
Translated using Weblate (Indonesian)
Currently translated at 100.0% (384 of 384 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-02-10 19:59:42 +00:00
Lorixx
05bc8b67d1
Translated using Weblate (German)
Currently translated at 92.1% (354 of 384 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-02-10 19:59:41 +00:00
Bliiitz
cd540c621b
Translated using Weblate (French)
Currently translated at 99.7% (382 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-02-10 19:59:41 +00:00
Priit Jõerüüt
70ea593bd9
Translated using Weblate (Estonian)
Currently translated at 100.0% (383 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-02-10 19:59:41 +00:00
Priit Jõerüüt
a62b34d372
Translated using Weblate (Estonian)
Currently translated at 100.0% (383 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-02-10 19:59:41 +00:00
idezentas
21010df2aa
Translated using Weblate (Turkish)
Currently translated at 100.0% (383 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-02-10 19:59:41 +00:00
Fjuro
0f33f14972
Translated using Weblate (Czech)
Currently translated at 100.0% (383 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-02-10 19:59:41 +00:00
Sathen
bdac869402
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (383 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/uk/
2026-02-10 19:59:41 +00:00
Outbreak2096
03f57f4679
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (383 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-02-10 19:59:41 +00:00
arcker95
a96b0b428d
Translated using Weblate (Italian)
Currently translated at 100.0% (383 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-02-10 19:59:41 +00:00
SimonHung
f5e0607ae9
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (383 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-02-10 19:59:41 +00:00
idezentas
735d413a74
Translated using Weblate (Turkish)
Currently translated at 94.2% (361 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-02-10 19:59:41 +00:00
efko
eea6003ffb
Translated using Weblate (Greek)
Currently translated at 46.7% (179 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/el/
2026-02-10 19:59:41 +00:00
ictu
a8ac91ecd1
Translated using Weblate (Polish)
Currently translated at 63.1% (242 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2026-02-10 19:59:41 +00:00
SimonHung
24cd367204
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 94.7% (363 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-02-10 19:59:41 +00:00
oyvhov
01bc279ecf
Translated using Weblate (Norwegian Bokmål)
Currently translated at 83.2% (319 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nb_NO/
2026-02-10 19:59:41 +00:00
damontecres
2a264f31e6
Translated using Weblate (Spanish)
Currently translated at 94.2% (361 of 383 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-02-10 19:59:41 +00:00
Outbreak2096
231dc4e6df
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (364 of 364 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-02-10 19:59:41 +00:00
arcker95
dd61bab8d1
Translated using Weblate (Italian)
Currently translated at 100.0% (364 of 364 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-02-10 19:59:41 +00:00
idezentas
47f12b8735
Translated using Weblate (Turkish)
Currently translated at 100.0% (362 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/tr/
2026-02-10 19:59:41 +00:00
subatomic9345
4b8ee5f5de
Translated using Weblate (German)
Currently translated at 94.4% (342 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-02-10 19:59:41 +00:00
idezentas
1a4daf8078
Added translation using Weblate (Turkish) 2026-02-10 19:59:40 +00:00
opakholis
4155556c05
Translated using Weblate (Indonesian)
Currently translated at 100.0% (362 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-02-10 19:59:40 +00:00
SimonHung
1c9afd0408
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (362 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-02-10 19:59:40 +00:00
danyrd92
4dd6e3a6c0
Translated using Weblate (Spanish)
Currently translated at 100.0% (362 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-02-10 19:59:40 +00:00
damontecres
8605fcafa8
Translated using Weblate (Spanish)
Currently translated at 100.0% (362 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-02-10 19:59:40 +00:00
haldq
404a883eb4
Translated using Weblate (German)
Currently translated at 94.1% (341 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-02-10 19:59:40 +00:00
Damontecres
f7efbf9f87
Don't show directed by line if director is unknown 2026-02-10 14:59:27 -05:00
Ray
df2f03cf83
Update dependencies (#866)
## Description
Update dependencies

### Related issues
Just updating some of the dependencies in #858 that don't require AGP 9

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-10 00:26:11 -05:00
renovate[bot]
6b068079fe
Update dependency python to 3.14 (#861) 2026-02-09 20:47:07 -05:00
renovate[bot]
c279d9c9f7
Update Gradle to v8.14.4 (#862) 2026-02-09 20:46:47 -05:00
renovate[bot]
6d5726a582
Update Github Actions (major) (#857)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/cache](https://redirect.github.com/actions/cache) | action |
major | `v4` → `v5` |
| [actions/checkout](https://redirect.github.com/actions/checkout) |
action | major | `v5` → `v6` |
|
[actions/setup-python](https://redirect.github.com/actions/setup-python)
| action | major | `v5` → `v6` |
|
[actions/upload-artifact](https://redirect.github.com/actions/upload-artifact)
| action | major | `v5` → `v6` |

---

### Release Notes

<details>
<summary>actions/cache (actions/cache)</summary>

### [`v5`](https://redirect.github.com/actions/cache/compare/v4...v5)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v4...v5)

</details>

<details>
<summary>actions/checkout (actions/checkout)</summary>

### [`v6`](https://redirect.github.com/actions/checkout/compare/v5...v6)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v5...v6)

</details>

<details>
<summary>actions/setup-python (actions/setup-python)</summary>

###
[`v6`](https://redirect.github.com/actions/setup-python/compare/v5...v6)

[Compare
Source](https://redirect.github.com/actions/setup-python/compare/v5...v6)

</details>

<details>
<summary>actions/upload-artifact (actions/upload-artifact)</summary>

###
[`v6`](https://redirect.github.com/actions/upload-artifact/compare/v5...v6)

[Compare
Source](https://redirect.github.com/actions/upload-artifact/compare/v5...v6)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/damontecres/Wholphin).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi45Ny4wIiwidXBkYXRlZEluVmVyIjoiNDIuOTcuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-09 18:29:27 -05:00
Ray
6954d821ed
Fix two UI issues (#856)
## Description
Fixes UI issues:
- Showing unplayed corner text on multi-part movies
- Fix duplicate or missing rating info on discover movie & series pages

### Related issues
N/A

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-09 18:27:10 -05:00
renovate[bot]
6ec10b9b82
Update dependency com.google.devtools.ksp to v2.3.5 (#855)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [com.google.devtools.ksp](https://goo.gle/ksp)
([source](https://redirect.github.com/google/ksp)) | `2.3.0` → `2.3.5` |
![age](https://developer.mend.io/api/mc/badges/age/maven/com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin/2.3.5?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin/2.3.0/2.3.5?slim=true)
|

---

### Release Notes

<details>
<summary>google/ksp (com.google.devtools.ksp)</summary>

###
[`v2.3.5`](https://redirect.github.com/google/ksp/releases/tag/2.3.5)

[Compare
Source](https://redirect.github.com/google/ksp/compare/2.3.4...2.3.5)

#### What's Changed

- KSPCoreEnvironment.instance\_prop leaks memory when used
programmatically
[#&#8203;2742](https://redirect.github.com/google/ksp/issues/2742)
- Missing first annotation argument when toByte is used
[#&#8203;2672](https://redirect.github.com/google/ksp/issues/2672)
- Fix circular dependency between KSP and KAPT in AGP 9.0
[#&#8203;2743](https://redirect.github.com/google/ksp/issues/2743)

#### Contributors

- Thanks to everyone who reported bugs and participated in discussions!

**Full Changelog**:
<https://github.com/google/ksp/compare/2.3.4...2.3.5>

###
[`v2.3.4`](https://redirect.github.com/google/ksp/releases/tag/2.3.4)

[Compare
Source](https://redirect.github.com/google/ksp/compare/2.3.3...2.3.4)

#### What's Changed

- KSP ignores sources in Kotlin directory
[#&#8203;2730](https://redirect.github.com/google/ksp/issues/2730)
- Avoid recording Java symbol lookups in non-incremental builds
[#&#8203;2728](https://redirect.github.com/google/ksp/issues/2728)
- Clean up ThreadLocals when processing is done
[#&#8203;2709](https://redirect.github.com/google/ksp/issues/2709)

#### Contributors

- Thanks to everyone who reported bugs and participated in discussions!

**Full Changelog**:
<https://github.com/google/ksp/compare/2.3.3...2.3.4>

###
[`v2.3.3`](https://redirect.github.com/google/ksp/releases/tag/2.3.3)

[Compare
Source](https://redirect.github.com/google/ksp/compare/2.3.2...2.3.3)

#### What's Changed

- Migrate away from a deprecated compilerOptions KGP API
[#&#8203;2703](https://redirect.github.com/google/ksp/issues/2703)

#### Contributors

- Thanks to everyone who reported bugs and participated in discussions!

**Full Changelog**:
<https://github.com/google/ksp/compare/2.3.2...2.3.3>

###
[`v2.3.2`](https://redirect.github.com/google/ksp/releases/tag/2.3.2)

[Compare
Source](https://redirect.github.com/google/ksp/compare/2.3.1...2.3.2)

#### What's Changed

**Note:** This release is a hotfix for a regression introduced in 2.3.1

- Fixed an issue where KSP incorrectly processed specific nullable
annotations from Java interfaces, leading to incorrect nullability in
the generated Kotlin code
[#&#8203;2696](https://redirect.github.com/google/ksp/issues/2696)
- Fixed a regression introduced in
[#&#8203;2656](https://redirect.github.com/google/ksp/issues/2656) that
caused runtime failures for projects using AGP 8.8.0 and older due to an
incompatible, version-specific type check.
[#&#8203;2694](https://redirect.github.com/google/ksp/issues/2694)

#### Contributors

Thanks to everyone who reported bugs and participated in discussions!

**Full Changelog**:
<https://github.com/google/ksp/compare/2.3.1...2.3.2>

###
[`v2.3.1`](https://redirect.github.com/google/ksp/releases/tag/2.3.1)

[Compare
Source](https://redirect.github.com/google/ksp/compare/2.3.0...2.3.1)

#### What's Changed

- Added support for AGP 9.0 and built-in Kotlin
[#&#8203;2674](https://redirect.github.com/google/ksp/issues/2674)
- Fixed a bug in getJvmCheckedException that incorrectly handled nested
classes.
[#&#8203;2584](https://redirect.github.com/google/ksp/issues/2584)
- Removed incorrect caching for KSValueArgumentLiteImpl that wrongly
merged arguments with different parents, origins, or locations
[#&#8203;2677](https://redirect.github.com/google/ksp/issues/2677)

#### Contributors

Thanks to everyone who reported bugs and participated in discussions!
**Full Changelog**:
<https://github.com/google/ksp/compare/2.3.0...2.3.1>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/damontecres/Wholphin).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi45Ny4wIiwidXBkYXRlZEluVmVyIjoiNDIuOTcuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-09 17:51:49 -05:00
renovate[bot]
4e45502c1c
Update Androidx Media3 to v1.9.2 (#854)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
|
[androidx.media3:media3-effect](https://redirect.github.com/androidx/media)
| `1.9.1` → `1.9.2` |
![age](https://developer.mend.io/api/mc/badges/age/maven/androidx.media3:media3-effect/1.9.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/androidx.media3:media3-effect/1.9.1/1.9.2?slim=true)
|
|
[androidx.media3:media3-ui-compose](https://redirect.github.com/androidx/media)
| `1.9.1` → `1.9.2` |
![age](https://developer.mend.io/api/mc/badges/age/maven/androidx.media3:media3-ui-compose/1.9.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/androidx.media3:media3-ui-compose/1.9.1/1.9.2?slim=true)
|
|
[androidx.media3:media3-ui](https://redirect.github.com/androidx/media)
| `1.9.1` → `1.9.2` |
![age](https://developer.mend.io/api/mc/badges/age/maven/androidx.media3:media3-ui/1.9.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/androidx.media3:media3-ui/1.9.1/1.9.2?slim=true)
|
|
[androidx.media3:media3-exoplayer-dash](https://redirect.github.com/androidx/media)
| `1.9.1` → `1.9.2` |
![age](https://developer.mend.io/api/mc/badges/age/maven/androidx.media3:media3-exoplayer-dash/1.9.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/androidx.media3:media3-exoplayer-dash/1.9.1/1.9.2?slim=true)
|
|
[androidx.media3:media3-exoplayer-hls](https://redirect.github.com/androidx/media)
| `1.9.1` → `1.9.2` |
![age](https://developer.mend.io/api/mc/badges/age/maven/androidx.media3:media3-exoplayer-hls/1.9.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/androidx.media3:media3-exoplayer-hls/1.9.1/1.9.2?slim=true)
|
|
[androidx.media3:media3-session](https://redirect.github.com/androidx/media)
| `1.9.1` → `1.9.2` |
![age](https://developer.mend.io/api/mc/badges/age/maven/androidx.media3:media3-session/1.9.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/androidx.media3:media3-session/1.9.1/1.9.2?slim=true)
|
|
[androidx.media3:media3-exoplayer](https://redirect.github.com/androidx/media)
| `1.9.1` → `1.9.2` |
![age](https://developer.mend.io/api/mc/badges/age/maven/androidx.media3:media3-exoplayer/1.9.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/androidx.media3:media3-exoplayer/1.9.1/1.9.2?slim=true)
|
|
[androidx.media3:media3-datasource-okhttp](https://redirect.github.com/androidx/media)
| `1.9.1` → `1.9.2` |
![age](https://developer.mend.io/api/mc/badges/age/maven/androidx.media3:media3-datasource-okhttp/1.9.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/maven/androidx.media3:media3-datasource-okhttp/1.9.1/1.9.2?slim=true)
|

---

### Release Notes

<details>
<summary>androidx/media (androidx.media3:media3-effect)</summary>

###
[`v1.9.2`](https://redirect.github.com/androidx/media/blob/HEAD/RELEASENOTES.md#192-2026-02-06)

[Compare
Source](https://redirect.github.com/androidx/media/compare/1.9.1...1.9.2)

This release includes the following changes since
[1.9.1 release](#&#8203;191-2026-01-26):

- ExoPlayer:
- Fix bug where `ProgressiveMediaSource` propagates out-of-date timeline
    info to player and the queued periods unexpectedly get removed

([#&#8203;3016](https://redirect.github.com/androidx/media/issues/3016)).
- Audio:
  - Improve the retry logic of `AudioOutput` initialization in
    `DefaultAudioSink`

([#&#8203;2905](https://redirect.github.com/androidx/media/issues/2905)).
- Session:
- Fix issue where system UI button placement workaround negatively
affects
    other UI surface like Android Auto or manufacturers not needing the
workaround
([#&#8203;3041](https://redirect.github.com/androidx/media/issues/3041)).
- Cast extension:
  - Fix bug where transferring from Cast to local playback was broken.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these
updates again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/damontecres/Wholphin).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0Mi45Ny4wIiwidXBkYXRlZEluVmVyIjoiNDIuOTcuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-09 17:49:17 -05:00
Ray
b06761eaa5
Rewrite SuggestionsCache & limit parallelism (#851)
## 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
2026-02-09 17:39:40 -05:00
renovate[bot]
28ed1a491b
Configure Renovate (#852)
Welcome to [Renovate](https://redirect.github.com/renovatebot/renovate)!
This is an onboarding PR to help you understand and configure settings
before regular Pull Requests begin.

🚦 To activate Renovate, merge this Pull Request. To disable Renovate,
simply close this Pull Request unmerged.



---
### Detected Package Files

 * `.github/actions/native-build/action.yml` (github-actions)
 * `.github/actions/setup/action.yml` (github-actions)
 * `.github/workflows/main.yml` (github-actions)
 * `.github/workflows/pr.yml` (github-actions)
 * `.github/workflows/release.yml` (github-actions)
 * `gradle.properties` (gradle)
 * `settings.gradle.kts` (gradle)
 * `build.gradle.kts` (gradle)
 * `app/build.gradle.kts` (gradle)
 * `gradle/libs.versions.toml` (gradle)
 * `gradle/wrapper/gradle-wrapper.properties` (gradle-wrapper)

### Configuration Summary

Based on the default config's presets, Renovate will:

  - Start dependency updates only once this onboarding PR is merged
  - Hopefully safe environment variables to allow users to configure.
  - Show all Merge Confidence badges for pull requests.
  - Enable Renovate Dependency Dashboard creation.
- Use semantic commit type `fix` for dependencies and `chore` for all
others if semantic commits are in use.
- Ignore `node_modules`, `bower_components`, `vendor` and various
test/tests (except for nuget) directories.
  - Group known monorepo packages together.
  - Use curated list of recommended non-monorepo package groupings.
- Show only the Age and Confidence Merge Confidence badges for pull
requests.
  - Apply crowd-sourced package replacement rules.
  - Apply crowd-sourced workarounds for known problems with packages.
- Ensure that every dependency pinned by digest and sourced from
GitHub.com contains a link to the commit-to-commit diff
  - Correctly link to the source code for golang.org/x packages
  - Link to pkg.go.dev/... for golang.org/x packages' title

🔡 Do you want to change how Renovate upgrades your dependencies? Add
your custom config to `renovate.json` in this branch. Renovate will
update the Pull Request description the next time it runs.

---

### What to Expect

With your current configuration, Renovate will create 11 Pull Requests:

<details>
<summary>Update Androidx Media3 to v1.9.2</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/androidx-media3`
  - Merge into: `main`
- Upgrade
[androidx.media3:media3-effect](https://redirect.github.com/androidx/media)
to `1.9.2`
- Upgrade
[androidx.media3:media3-ui-compose](https://redirect.github.com/androidx/media)
to `1.9.2`
- Upgrade
[androidx.media3:media3-ui](https://redirect.github.com/androidx/media)
to `1.9.2`
- Upgrade
[androidx.media3:media3-exoplayer-dash](https://redirect.github.com/androidx/media)
to `1.9.2`
- Upgrade
[androidx.media3:media3-exoplayer-hls](https://redirect.github.com/androidx/media)
to `1.9.2`
- Upgrade
[androidx.media3:media3-session](https://redirect.github.com/androidx/media)
to `1.9.2`
- Upgrade
[androidx.media3:media3-exoplayer](https://redirect.github.com/androidx/media)
to `1.9.2`
- Upgrade
[androidx.media3:media3-datasource-okhttp](https://redirect.github.com/androidx/media)
to `1.9.2`


</details>

<details>
<summary>Update dependency com.google.devtools.ksp to v2.3.5</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/ksp-monorepo`
  - Merge into: `main`
- Upgrade
[com.google.devtools.ksp](https://redirect.github.com/google/ksp) to
`2.3.5`


</details>

<details>
<summary>Update Dependencies</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/dependencies`
  - Merge into: `main`
- Upgrade
[com.google.dagger:hilt-android-compiler](https://redirect.github.com/google/dagger)
to `2.59.1`
- Upgrade
[com.google.dagger:hilt-android](https://redirect.github.com/google/dagger)
to `2.59.1`
- Upgrade
[com.google.protobuf:protobuf-kotlin-lite](https://redirect.github.com/protocolbuffers/protobuf)
to `4.33.5`
- Upgrade
[com.mikepenz:multiplatform-markdown-renderer-m3](https://redirect.github.com/mikepenz/multiplatform-markdown-renderer)
to `0.39.2`
- Upgrade
[com.mikepenz:multiplatform-markdown-renderer](https://redirect.github.com/mikepenz/multiplatform-markdown-renderer)
to `0.39.2`
- Upgrade
[io.mockk:mockk-android](https://redirect.github.com/mockk/mockk) to
`1.14.9`
- Upgrade
[io.mockk:mockk-agent](https://redirect.github.com/mockk/mockk) to
`1.14.9`


</details>

<details>
<summary>Update dependency com.google.dagger.hilt.android to
v2.59.1</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/hilt`
  - Merge into: `main`
- Upgrade
[com.google.dagger.hilt.android](https://redirect.github.com/google/dagger)
to `2.59.1`


</details>

<details>
<summary>Update dependency python to 3.14</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/github-actions`
  - Merge into: `main`
- Upgrade [python](https://redirect.github.com/actions/python-versions)
to `3.14`


</details>

<details>
<summary>Update Gradle to v8.14.4</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/gradle-8.x`
  - Merge into: `main`
- Upgrade [gradle](https://redirect.github.com/gradle/gradle) to
`8.14.4`


</details>

<details>
<summary>Update Jellyfin SDK to v1.8.6</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/jellyfin-sdk`
  - Merge into: `main`
- Upgrade
[org.jellyfin.sdk:jellyfin-api-okhttp-jvm](https://redirect.github.com/jellyfin/jellyfin-sdk-kotlin)
to `1.8.6`
- Upgrade
[org.jellyfin.sdk:jellyfin-api](https://redirect.github.com/jellyfin/jellyfin-sdk-kotlin)
to `1.8.6`
- Upgrade
[org.jellyfin.sdk:jellyfin-core](https://redirect.github.com/jellyfin/jellyfin-sdk-kotlin)
to `1.8.6`


</details>

<details>
<summary>Update Kotlin</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/kotlin`
  - Merge into: `main`
- Upgrade
[org.jetbrains.kotlinx:kotlinx-coroutines-test](https://redirect.github.com/Kotlin/kotlinx.coroutines)
to `1.10.2`
- Upgrade
[org.jetbrains.kotlin.plugin.serialization](https://redirect.github.com/JetBrains/kotlin)
to `2.3.10`
- Upgrade
[org.jetbrains.kotlin.jvm](https://redirect.github.com/JetBrains/kotlin)
to `2.3.10`
- Upgrade
[org.jetbrains.kotlin.plugin.compose](https://redirect.github.com/JetBrains/kotlin)
to `2.3.10`
- Upgrade
[org.jetbrains.kotlin.android](https://redirect.github.com/JetBrains/kotlin)
to `2.3.10`


</details>

<details>
<summary>Update dependency com.android.application to v9</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/major-agp`
  - Merge into: `main`
- Upgrade
[com.android.application](https://android.googlesource.com/platform/tools/base)
to `9.0.0`


</details>

<details>
<summary>Update Github Actions (major)</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/major-github-actions`
  - Merge into: `main`
- Upgrade [actions/cache](https://redirect.github.com/actions/cache) to
`v5`
- Upgrade
[actions/checkout](https://redirect.github.com/actions/checkout) to `v6`
- Upgrade
[actions/setup-python](https://redirect.github.com/actions/setup-python)
to `v6`
- Upgrade
[actions/upload-artifact](https://redirect.github.com/actions/upload-artifact)
to `v6`


</details>

<details>
<summary>Update Gradle to v9</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/gradle-9.x`
  - Merge into: `main`
- Upgrade [gradle](https://redirect.github.com/gradle/gradle) to `9.3.1`


</details>



🚸 Branch creation will be limited to maximum 2 per hour, so it doesn't
swamp any CI resources or overwhelm the project. See docs for
`prhourlylimit` for details.


---

❓ Got questions? Check out Renovate's
[Docs](https://docs.renovatebot.com/), particularly the Getting Started
section.
If you need any further assistance then you can also [request help
here](https://redirect.github.com/renovatebot/renovate/discussions).


---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/damontecres/Wholphin).


<!--renovate-config-hash:7cb4aaf8303f9b7801754f83b08207c5265d3d176bdb616169c40f6df7c688c2-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Damontecres <damontecres@gmail.com>
2026-02-09 17:28:03 -05:00
Ray
e7da5ca007
Add settings for max days in next up (#850)
## 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
2026-02-09 15:36:20 -05:00
Ray
63b37ef3f4
Fix glitchy nav drawer scrolling when opening (#848)
## Description
The nav drawer would always glitch a little when it gained focus due it
to trying to keep the selected page in view. #842 made this even worst.

This PR fixes that issue by hoisting the list scroll state up higher
which ensures it is remembered between recompositions. This change also
eliminates the need for workarounds to try and scroll it
programmatically which is also a tiny performance boost.

### Related issues
Follow up to #842 

### Testing
Emulator & nvidia shield

## Screenshots
N/A

## AI or LLM usage
None
2026-02-08 16:21:38 -05:00
Ray
72e5e6ae86
Add option to send relevant media info to server for bug reports (#840)
## Description
Adds a item in the More menu to send information about the media item to
the server (and app logs).

This includes the "media sources" which is info about the files and
stream/tracks in the files. It also includes device info, the playback
preferences, and the device's transcoding profile. All of this info is
useful for debugging playback issues.

### Related issues
N/A

### Testing
Emulator testing

## Screenshots
N/A

## AI or LLM usage
None
2026-02-08 13:38:09 -05:00
Ray
d5602aa88c
Navigation drawer improvements (#842)
## Description
This updates the navigation drawer with better behavior and UI. It is
also a bit more optimized and is slightly faster.

- When open, content is pushed to the right instead of being covered
- When open, it uses the same background colors from the dynamic
background
- The icons no longer shift as the drawer opens & closes
- The open drawer is not as wide
- Focus highlight on items is less circular, now matching the other
menus in the app
- The animation is a bit faster too

Dev notes:
- This PR copies & modifies `tv-material` code, so there's a customized
`ModalNavigationDrawer` implementation now
- There are fewer `AnimatedVisibility` composables which improves
performance
- Icons are exactly sized
- Content pages generally shouldn't add start/end padding anymore, the
padding is now added by the nav drawer
(b4f1b111edcd7e7ec05edc1100402b2851721fcd) and is consistent across
every page

### Related issues
Closes #699
Closes #384

### Testing
Tested on the emulator, nvidia shield, & google sabrina

## Screenshots
![nav_drawer_closed
Large](https://github.com/user-attachments/assets/9047eb45-6025-4992-8740-3430f0a8f2a9)
![nav_drawer_open_backdrop_dim
Large](https://github.com/user-attachments/assets/880f24a5-8018-469e-805d-18060c641dc5)



## AI or LLM usage
None
2026-02-08 13:03:43 -05:00
Justin Caveda
8e9a6799f5
Fix: suggestions logic/re-implement logic into worker and address performance issues (#834)
## 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.
2026-02-07 20:38:26 -05:00
Ray
abf5ec8004
Take playback speed into account when calculating the video end time (#843)
## Description
Small change so that the "Ends" time on the playback overlay takes the
current playback speed into account.

### Related issues
Closes #836

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
2026-02-07 13:03:10 -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
d9d5ab02e8
Add support for viewing photos in photo albums (#703)
## Description
Add support for viewing photos in photo albums

### Features
- Browse and view albums & photos
- View an album as a slideshow
- Show overlay with photo details and controls
- Rotate, zoom, & pan photos
- Apply basic filters to photos or albums such as contrast, saturation,
red/green/blue, etc
- Setting to include video clips during slideshows

### Controls
- D-Pad left or right: scroll between photos in album
- Hold D-Pad up or down: zoom in or out of the current photo
- D-Pad left/right/up/down, while zoomed: pan around photo
- Back, while zoomed: revert the zoom
- Otherwise: show photo overlay

### Related issues
Closes #458
Closes #634

### Screenshots
#### Overlay
![photo_overlay
Large](https://github.com/user-attachments/assets/b44ed876-d8bd-4f75-aa1e-4106c2edb2f7)

#### Applying filters
![photo_filters
Large](https://github.com/user-attachments/assets/1379978a-b27c-47ac-80af-f7bb6a2177df)
2026-02-04 20:04:46 -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
YogiBear12
b0b8ae3639
Align typography and placement between Home, MovieDetails and SeriesDetails (#751)
## Description
This PR addresses some quick changes to align the styling and placement
of elements on the Home, Movie details and Series details pages, using
Home as the reference point for the typography.

### Related issues
Ongoing discussion
[here](https://github.com/damontecres/Wholphin/discussions/704)

### Screenshots
Movie Details
<img width="1920" height="1080" alt="Screenshot_20260123_223116"
src="https://github.com/user-attachments/assets/ffd6a5c0-0ed1-4634-8c57-147f7783950f"
/>


Series Details
<img width="1920" height="1080" alt="Screenshot_20260123_223207"
src="https://github.com/user-attachments/assets/59c6be44-fcf4-4e28-8ad2-add304bd7650"
/>



### AI/LLM usage
LLM was only used to identify the current styling applied to the pages.
All code changes were made and reviewed manually
2026-02-03 17:55:05 -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
045b689994
Add ability to sort and filter items in a playlist (#818)
## Description
Add ability to sort and filter items in a playlist. There's a new button
for each added to the playlist details page. Starting playback will run
in the order selected.

The sort and filter are persisted locally per playlist, so returning to
the playlist will remember the previous settings.

Note: playlist playback is still limited to 100 items (#42)

### Related issues
Closes #285

### Testing
Emulator & nvidia shield

## Screenshots
![playlist_sort_filter
Large](https://github.com/user-attachments/assets/9880021b-a1c3-4d8d-ace2-83737f3f772a)

## AI or LLM usage
None
2026-02-03 15:11:37 -05:00
Ray
ae5035703f
Merge pull request #817 from damontecres/fix/805
Always use space for home page header
2026-02-02 12:25:16 -05:00
Damontecres
e6ff173b41
Adopt jellyfin's AI/LLM policy 2026-02-02 10:34:30 -05:00
Ray
7aa4ccc447
Update README
Removed versioning details and simplified media player options.
2026-01-30 17:28:41 -05:00
Damontecres
510ca416f3
Release v0.4.1
Some checks failed
Create release / publish (push) Has been cancelled
2026-01-30 17:10:44 -05:00
Ray
ea414929db
Merge pull request #801 from damontecres/translations-2026-01-30
Translations 2026-01-30
2026-01-30 17:03:31 -05:00
voc0der
1bffa413a2
Fix Seerr Local/Jellyfin authentication and request permissions (#734)
## Description
When using LOCAL or JELLYFIN authentication methods to connect to Seerr,
the login flow was not properly establishing session cookies and user
permissions. The login would 'succeed' and you were led to thinking it
worked, but then...

This caused:
- Request functionality to be greyed out even though the user should
have access
- Users to appear to have no permissions after login (permissions=0)

## Solution
This PR fixes the authentication flow by:

**Fetching full user config after login**: For LOCAL and JELLYFIN
auth methods, the login endpoints (`authLocalPost()` and
`authJellyfinPost()`) establish the session cookie but return incomplete
user data. This PR now calls `authMeGet()` immediately after login to
fetch the fully-populated user configuration including proper
permissions.

## Tests
- Have working apk on my dev fork.
- Ran logins as Local + API. Cannot test Jellyfin due to environment,
but codewise, it looks correct.

## Related issues
Related to https://github.com/damontecres/Wholphin/issues/747


---------

Co-authored-by: Ray <154766448+damontecres@users.noreply.github.com>
2026-01-30 16:16:39 -05:00
Ray
3ada35c731
Fix error reporting & crash during Jellyseerr login (#802)
## Description
Makes sure the invalid credentials error is propagated up instead of
displaying "No URL" as the error message.

Makes sure `OkHttp` is initialized since the startup initialization
provider is disabled. This fixes a possible crash if `OkHttp` needs to
access its assets.

Also, shows the URL when removing the Jellyseerr server.
2026-01-30 16:15:35 -05:00
Ckau
853c224b61
Translated using Weblate (Russian)
Currently translated at 85.6% (310 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ru/
2026-01-30 16:34:17 +00:00
Sathen
cb9eab2aa5
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (362 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/uk/
2026-01-30 16:34:17 +00:00
ymir
f64c9d2e73
Translated using Weblate (French)
Currently translated at 100.0% (362 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-01-30 16:34:17 +00:00
American_Jesus
1ac6c9366b
Translated using Weblate (Portuguese)
Currently translated at 100.0% (362 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-01-30 16:34:17 +00:00
Sk4hnt42
2f19003dd9
Translated using Weblate (German)
Currently translated at 88.9% (322 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-01-30 16:34:17 +00:00
Lorixx
aae7a64cc4
Translated using Weblate (German)
Currently translated at 88.9% (322 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-01-30 16:34:17 +00:00
viretius
d260791e62
Translated using Weblate (German)
Currently translated at 88.9% (322 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-01-30 16:34:17 +00:00
Priit Jõerüüt
1d11eab48d
Translated using Weblate (Estonian)
Currently translated at 100.0% (362 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-01-30 16:34:17 +00:00
Fjuro
e632c76ff2
Translated using Weblate (Czech)
Currently translated at 100.0% (362 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-01-30 16:34:17 +00:00
arcker95
8bf3c48a12
Translated using Weblate (Italian)
Currently translated at 100.0% (362 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-01-30 16:34:17 +00:00
Outbreak2096
c714afeb3b
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (362 of 362 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-01-30 16:34:17 +00:00
veisen
82744322e9
Translated using Weblate (Czech)
Currently translated at 100.0% (361 of 361 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-01-30 16:34:17 +00:00
Sathen
b083bea4bd
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/uk/
2026-01-30 16:34:17 +00:00
veisen
1d5afa7809
Translated using Weblate (Czech)
Currently translated at 100.0% (361 of 361 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-01-30 16:34:17 +00:00
viretius
b189720be5
Translated using Weblate (German)
Currently translated at 85.3% (308 of 361 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-01-30 16:34:17 +00:00
Sathen
d2826d21f7
Added translation using Weblate (Ukrainian) 2026-01-30 16:34:17 +00:00
SimonHung
a99b54bc49
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (361 of 361 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-01-30 16:34:17 +00:00
arcker95
ff7e9517e9
Translated using Weblate (Italian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-01-30 16:34:16 +00:00
opakholis
a48acd5d1c
Translated using Weblate (Indonesian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-01-30 16:34:16 +00:00
Ray
003fe8348c
Improve series overview loading speed (#800)
## Description
Improves loading speed for the series overview page (one with list of
episodes). This is mostly accomplished by querying for the cast/crew in
the episodes on demand instead of during the initial query.

Also stops using the date as a replacement for the episode index. Using
this caused an extra unnecessary (long) query.

Going from home to the 109th of a 230 episode season is ~2x faster with
this change on my test server.


### Related issues
Closes #599
2026-01-30 11:34:12 -05:00
Justin Caveda
e7ed173692
Fix(player): add exoplayer fallback for all 4k playback when MPV software decoding is enabled (#761)
## Description
Updates the logic in the Prefer MPV setting to prevent performance
issues when software decoding is selected. When the MPV backend is
configured to use software decoding, the app will now automatically
fallback to ExoPlayer for 4K content. Most Android TV devices cannot
smoothly render 4K video via software decoding, so falling back to
Exoplayer for this content makes sense in most cases when using the
software renderer. This mirrors the existing fallback mechanism used for
HDR content.

### Related issues
N/A

### Screenshots
N/A

### AI/LLM usage
None

---------

Co-authored-by: Ray <154766448+damontecres@users.noreply.github.com>
2026-01-29 17:11:13 -05:00
Ray
abd8235556
Temporarily restore ExoPlayer as the default player (#797)
## Description
Due a few more MPV bugs showing up, this PR reverts the part of #736
that made "Prefer MPV" the default player option.

User can still manually switch to "Prefer MPV" and its full functional
remains. It's just not set to be the default anymore.
2026-01-29 14:27:01 -05:00
Ray
fa51d1e61e
Bump dependency versions (#796)
## Description
Bumps dependency versions.

AGP (and hilt by extension) are not yet updated cause moving to AGP 9
has many breaking changes and I think one or two may affect the build
scripts.

Also, one day I will add Renovate to automate these PRs.
2026-01-29 14:26:54 -05:00
Justin Caveda
b3ef60f86d
Feat(ui): Add drop shadow to UI dialogs (#678)
## Description
Adds a subtle drop shadow to dialog containers in Dialogs.kt. Should
make the dialogs pop a bit more instead of looking flat.

### Related issues
Fixes #665

### Screenshots

Before:

<img width="1950" height="1123" alt="before1"
src="https://github.com/user-attachments/assets/b3cba7b2-108c-4afe-94ef-474943a6cccf"
/>


After:
<img width="1952" height="1133" alt="after"
src="https://github.com/user-attachments/assets/ff9aaf50-e122-4e27-bae1-e981f257fd4c"
/>



### AI/LLM usage
None

---------

Co-authored-by: Ray <154766448+damontecres@users.noreply.github.com>
2026-01-29 13:32:00 -05:00
Ray
792d3598b7
Fix a few discover UI bugs (#795)
## Description
Just fixes a couple small UI issues on the discover/seerr pages
2026-01-29 13:31:51 -05:00
Ray
f787e9ce1a
Better smart subtitles with no preferred audio language (#789)
## Description
Another small fix to smart subtitle track selection to better handle the
cases where the user does not have a preferred audio language.

### Related issues
Addresses issue raises in
https://github.com/damontecres/Wholphin/issues/570#issuecomment-3714629517
2026-01-28 13:56:33 -05:00
Damontecres
5975302fb9
Allow app to install on external storage #733 2026-01-28 13:56:20 -05:00
Ray
e93100c788
Fixes & improvements to Jellyseer integration (#783)
## 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 #778
Fixes #750
Fixes #740
Might help with #747
Fixes
https://github.com/damontecres/Wholphin/issues/764#issuecomment-3801324648
2026-01-27 11:44:56 -05:00
Ray
3428f2b208
Fixes possible crash if a home row is removed (#779)
## Description
Fixes a possible crash if a row on the home page is removed.

This could potentially happen if you only have one thing on the resume
row and watch it by accessing it from the bottom row.
2026-01-26 16:34:50 -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
03871579f5
Fixes MPV crash when playing next up (#772)
## Description
Ensure that MPV is always destroyed before being initialized again.
Also, now creating a new player is only done when switching backends
which means faster, more efficient playing the next up.

### Related issues
Fixes #766
2026-01-25 21:38:41 -05:00
Justin Caveda
7b8f5c3e63
Fix(player): Disable gpu-next by default (#760)
## Description
Disables gpu-next by default in MPV settings. This renderer is
experimental and causes broken playback (purple screen) on some older
devices like the NVIDIA Shield 2019 Pro.

Also adds a migration to ensure gpu-next is turned off for existing
users who upgrade, avoiding playback regressions for users being newly
moved over to the Prefer MPV settings so that they can experience a
smooth transition.

### Related issues
Fixes #754?

### Screenshots
N/A

### AI/LLM usage
PR drafting
2026-01-24 19:24:23 -05:00
Ray
47e54aa675
Catch errors on discover request tab (#765)
## Description
Handle errors on the Discover/Jellyseerr requests tab preventing crashes

Note: this PR fixes the crash, but the error was a 400, which is likely
due to the older Jellyseerr version.

### Related issues
Fixes #764
2026-01-24 19:23:15 -05:00
Ray
fc0de2144d
Small optimizations to home page & nav drawer (#743)
## Description
This PR has several micro optimizations to the home page & nav drawer.
Basically it computes text off of the main thread and removes redundant
calculations.
2026-01-23 09:13:27 -05:00
Ray
0639a7a1da
Add setting to automatically switch between ExoPlayer & MPV (#736)
## Description
The MPV backend is no longer considered experimental. This PR adds a
setting for player backend "Prefer MPV" which uses MPV for all playback
unless the video is HDR which then uses ExoPlayer.

Switching occurs per video. So if there's a playlist with both SDR &
HDR, the player will switch back and forth as needed.

### Related issues
Closes #253
Related to #235
2026-01-23 08:50:01 -05:00
Damontecres
16ac02a3fd
Release v0.4.0
Some checks failed
Create release / publish (push) Has been cancelled
2026-01-20 15:17:10 -05:00
Damontecres
ee142a740e
Merge branch 'readme/0.4.0' 2026-01-20 15:16:29 -05:00
Ray
936a2fc551
Merge pull request #726 from damontecres/translation-2026-01-19
Add batch of translations
2026-01-20 13:50:20 -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
7a20fedef4
More minor bug fixes (#728)
## Description
- Better focus handling on discover page
- Follow up to #723 to dim the user image when the nav drawer is closed
- Add a missed closeable to stop theme music when leaving a series page
2026-01-19 19:32:43 -05:00
Damontecres
2572a38450
Fix crash (#725) 2026-01-19 13:44:44 -05:00
Damontecres
7ec986d2e6
Readme updates for v0.4.0 2026-01-19 13:38:56 -05:00
Damontecres
9c5acd51c4
Fix a few translation issues 2026-01-19 13:37:19 -05:00
Outbreak2096
686db9cb01
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (361 of 361 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-01-19 16:39:37 +00:00
ymir
570c260eab
Translated using Weblate (French)
Currently translated at 100.0% (361 of 361 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-01-19 16:39:37 +00:00
Priit Jõerüüt
1ca3e3d4cb
Translated using Weblate (Estonian)
Currently translated at 100.0% (361 of 361 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-01-19 16:39:37 +00:00
American_Jesus
b0eccfad0c
Translated using Weblate (Portuguese)
Currently translated at 100.0% (361 of 361 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-01-19 16:39:37 +00:00
miausalvaje
441674268d
Translated using Weblate (Spanish)
Currently translated at 100.0% (361 of 361 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2026-01-19 16:39:37 +00:00
Fjuro
55b6861dba
Translated using Weblate (Czech)
Currently translated at 3.0% (11 of 360 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/cs/
2026-01-19 16:39:37 +00:00
SimonHung
7a9a09ce4d
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (360 of 360 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-01-19 16:39:37 +00:00
subatomic9345
9a6c2cabc3
Translated using Weblate (German)
Currently translated at 85.0% (306 of 360 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-01-19 16:39:37 +00:00
Fjuro
355b441ae8
Added translation using Weblate (Czech) 2026-01-19 16:39:37 +00:00
Priit Jõerüüt
d9362f4bf4
Translated using Weblate (Estonian)
Currently translated at 100.0% (360 of 360 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-01-19 16:39:37 +00:00
Outbreak2096
b4bbb07a7d
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (360 of 360 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-01-19 16:39:37 +00:00
arcker95
02458db48c
Translated using Weblate (Italian)
Currently translated at 100.0% (360 of 360 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-01-19 16:39:37 +00:00
SimonHung
0269c0cbce
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (360 of 360 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-01-19 16:39:37 +00:00
opakholis
9292c3bf34
Translated using Weblate (Indonesian)
Currently translated at 100.0% (360 of 360 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2026-01-19 16:39:37 +00:00
Outbreak2096
939947709d
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (342 of 342 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-01-19 16:39:37 +00:00
arcker95
03962454b7
Translated using Weblate (Italian)
Currently translated at 100.0% (342 of 342 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-01-19 16:39:37 +00:00
SimonHung
d7b68b89f3
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (342 of 342 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-01-19 16:39:37 +00:00
Priit Jõerüüt
8adf86c51b
Translated using Weblate (Estonian)
Currently translated at 100.0% (342 of 342 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-01-19 16:39:37 +00:00
American_Jesus
2d72e738a6
Translated using Weblate (Portuguese)
Currently translated at 100.0% (342 of 342 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-01-19 16:39:37 +00:00
Outbreak2096
d231dab5e5
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (341 of 341 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-01-19 16:39:37 +00:00
arcker95
fff6b537d4
Translated using Weblate (Italian)
Currently translated at 100.0% (341 of 341 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-01-19 16:39:36 +00:00
SimonHung
66bec53d5f
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 96.7% (330 of 341 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-01-19 16:39:36 +00:00
Priit Jõerüüt
dec734e91b
Translated using Weblate (Estonian)
Currently translated at 100.0% (341 of 341 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-01-19 16:39:36 +00:00
Priit Jõerüüt
5084fdcfbb
Translated using Weblate (Estonian)
Currently translated at 100.0% (340 of 340 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-01-19 16:39:36 +00:00
Outbreak2096
f7757459d8
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (329 of 329 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-01-19 16:39:36 +00:00
arcker95
1984f0e47a
Translated using Weblate (Italian)
Currently translated at 100.0% (329 of 329 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-01-19 16:39:36 +00:00
ymir
6f9a5e7bdc
Translated using Weblate (French)
Currently translated at 100.0% (329 of 329 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-01-19 16:39:36 +00:00
Priit Jõerüüt
111f88b929
Translated using Weblate (Estonian)
Currently translated at 100.0% (329 of 329 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-01-19 16:39:36 +00:00
Outbreak2096
3eab616bf7
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 99.3% (327 of 329 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-01-19 16:39:36 +00:00
arcker95
ba233d4642
Translated using Weblate (Italian)
Currently translated at 100.0% (327 of 327 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-01-19 16:39:36 +00:00
RabSsS
2ee432e812
Translated using Weblate (French)
Currently translated at 99.6% (326 of 327 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-01-19 16:39:36 +00:00
lazigeri
a2c6f12f8b
Translated using Weblate (Hungarian)
Currently translated at 100.0% (327 of 327 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2026-01-19 16:39:36 +00:00
lazigeri
950ce06669
Translated using Weblate (Hungarian)
Currently translated at 99.6% (325 of 326 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2026-01-19 16:39:36 +00:00
subatomic9345
b38e962633
Translated using Weblate (German)
Currently translated at 89.3% (286 of 320 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-01-19 16:39:36 +00:00
texxlan
3236bf5978
Translated using Weblate (German)
Currently translated at 89.3% (286 of 320 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2026-01-19 16:39:36 +00:00
oyvhov
a0eecf9917
Translated using Weblate (Norwegian Bokmål)
Currently translated at 100.0% (320 of 320 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nb_NO/
2026-01-19 16:39:36 +00:00
Outbreak2096
4f0f8cb39d
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (320 of 320 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-01-19 16:39:36 +00:00
SimonHung
8100d0b166
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 99.6% (319 of 320 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-01-19 16:39:36 +00:00
arcker95
930fe834a6
Translated using Weblate (Italian)
Currently translated at 100.0% (320 of 320 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-01-19 16:39:36 +00:00
ymir
f0ba9da5bc
Translated using Weblate (French)
Currently translated at 100.0% (320 of 320 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2026-01-19 16:39:36 +00:00
Priit Jõerüüt
16bf015ad9
Translated using Weblate (Estonian)
Currently translated at 100.0% (320 of 320 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-01-19 16:39:36 +00:00
American_Jesus
68cea647f0
Translated using Weblate (Portuguese)
Currently translated at 100.0% (320 of 320 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2026-01-19 16:39:36 +00:00
lazigeri
87ac139f9f
Translated using Weblate (Hungarian)
Currently translated at 100.0% (319 of 319 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2026-01-19 16:39:36 +00:00
Outbreak2096
65b0e1fdf0
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (319 of 319 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-01-19 16:39:36 +00:00
lazigeri
667008ec31
Translated using Weblate (Hungarian)
Currently translated at 100.0% (319 of 319 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2026-01-19 16:39:36 +00:00
arcker95
bed45814d6
Translated using Weblate (Italian)
Currently translated at 100.0% (319 of 319 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-01-19 16:39:36 +00:00
Priit Jõerüüt
7ec36917db
Translated using Weblate (Estonian)
Currently translated at 100.0% (319 of 319 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-01-19 16:39:36 +00:00
lazigeri
05bb2d4c65
Translated using Weblate (Hungarian)
Currently translated at 100.0% (316 of 316 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2026-01-19 16:39:36 +00:00
SimonHung
ca48d59b07
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (316 of 316 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2026-01-19 16:39:36 +00:00
Outbreak2096
8f0b620435
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (316 of 316 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2026-01-19 16:39:36 +00:00
lazigeri
a2b8ab5918
Translated using Weblate (Hungarian)
Currently translated at 100.0% (316 of 316 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2026-01-19 16:39:36 +00:00
arcker95
c64b09d891
Translated using Weblate (Italian)
Currently translated at 100.0% (316 of 316 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2026-01-19 16:39:35 +00:00
Priit Jõerüüt
449185b9c3
Translated using Weblate (Estonian)
Currently translated at 100.0% (316 of 316 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2026-01-19 16:39:35 +00: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
946043342a
Use the user's profile image on nav drawer (#723)
## 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"
/>
2026-01-19 11:39:21 -05:00
Ray
4dd44b935c
Add optional AV1 software decoding via dav1d for ExoPlayer (#721)
## 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.
2026-01-18 18:35:41 -05:00
Damontecres
a0329b2c1d
Fix episode row focus 2026-01-17 22:51:28 -05:00
Ray
08355bf24b
Fixes focusing issues on many rows (#718)
## 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
2026-01-17 18:09:17 -05:00
Ray
154713d3f1
Several small bug fixes (#717)
## 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
2026-01-17 18:09:05 -05:00
Ray
bbeb42dab9
Keep grid controls accessible if error occurs (#714)
## 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
2026-01-16 17:15:16 -05:00
Damontecres
88de065fe0
Another cancel button fix 2026-01-16 14:38:16 -05:00
Ray
03b332e4c4
Fixes a few discover page UI bugs (#710)
## 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
2026-01-16 13:43:59 -05:00
Damontecres
ccd8c80d5e
Fix not showing some multiple versions in labels 2026-01-14 18:33:15 -05:00
Ray
c9d1244057
Optimize quick details UI (#674)
## 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
2026-01-14 17:07:17 -05:00
Ray
3215326515
Fixes "Pause with one click" for MPV (#701)
## 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
2026-01-14 17:06:40 -05:00
Justin Caveda
d725821011
[FEAT] Add voicesearch button to search page (#637)
## 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>
2026-01-14 17:06:22 -05:00
Ray
c4e64c3367
Fixes to genre grids (#693)
## 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 #689
Fixes #690
2026-01-13 16:05:09 -05:00
Justin Caveda
18e4877736
Fix wrong dialog showing after Seerr login (#688)
**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.
2026-01-13 15:32:29 -05:00
Ray
7c018f9a37
Use circular cards for people (#691)
## 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

![Image](https://github.com/user-attachments/assets/9ca5c25b-5ba0-49d9-a680-6520804b0ace)
2026-01-13 14:16:49 -05:00
Ray
5cb3662995
Fix jumping to a letter for many grids (#687)
## 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
2026-01-12 22:47:35 -05:00
Ray
80670c3e0f
Support requesting 4k media in discover (#685)
## Description
If the server support it and the user has permission, allows for
requesting 4K media

### Related issues
A follow up to #558
2026-01-12 21:55:50 -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
Damontecres
f4e1b0e171
Release v0.3.12
Some checks failed
Create release / publish (push) Has been cancelled
2026-01-12 16:01:37 -05:00
Ray
230b937048
Bug fixes (#680)
## Description
Gathering a couple final bug fixes

- Fixes the audio change dialog copy-paste error
2026-01-12 15:53:09 -05:00
Justin Caveda
8111f8bab2
Fix season numbers not appearing when scrolling (#679)
**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>
2026-01-12 15:33:02 -05:00
Ray
d5192abf62
Return focus to the tabs if a grid has no results (#676)
## 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
2026-01-11 21:36:24 -05:00
Ludovico de Nittis
197265e7f5
Add option to always direct play Dolby Vision Profile 7 (#670)
## 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>
2026-01-11 21:11:25 -05:00
Ray
b78f5fcc77
Better support for remote media (HLS/DASH) (#675)
## 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 #471
Closes #666
2026-01-11 19:43:21 -05:00
Ray
75a98ddaa5
Minor UI changes (#673)
## 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
2026-01-11 17:07:47 -05:00
Justin Caveda
19833a1a03
Add scrim behind season tabs for readability (#657)
## 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
2026-01-11 17:07:40 -05:00
Ray
2377a15611
Disable subtitle download button if user doesn't have permission (#671)
## 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
2026-01-11 13:21:02 -05:00
Justin Caveda
ed67e5575f
Add "Only Forced Subtitles" option to subtitle selection (#589)
## 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>
2026-01-11 13:11:39 -05:00
Ray
8bdc8a6f8f
Consistent audio/subtitle info across dialogs (#664)
## Description
Makes the labels for audio & subtitle stream consistent across the app

The More->Choose dialogs now highlight which track is currently choosen.

Also uses more of the brand names for audio codecs (eg DD+) and allows
for these to be translated into other languages.

### Related issues
Closes #621 
Fixes #668
Related to
https://github.com/damontecres/Wholphin/issues/528#issuecomment-3678876207,
I adjusted the background & selection colors of the dialogs during
playback

### Screenshots

#### Choose subtitles from More dialog
![subtitle_dialog1
Large](https://github.com/user-attachments/assets/edaf96a0-fea4-4110-b274-e8c57494bc59)

#### Choose subtitles during playback
![subtitle_dialog2
Large](https://github.com/user-attachments/assets/c0dd76ab-bb2c-42a9-bfa7-2d3b6ffe2b35)

#### Choose audio
![audio_tracks
Large](https://github.com/user-attachments/assets/3214879d-9845-48ff-918c-e9cd470ed0bc)
2026-01-11 12:46:38 -05:00
Ray
ee73e7d723
Fix duplicate commands from key presses/MediaSession (#663)
## 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
2026-01-09 16:59:37 -05:00
Ray
2cd34692e9
Show indicator for multiple versions of media file (#652)
## Description
Adds an indicator to cards on grid pages if there are more than a single
version of the item. Also adds the count to the video stream label, much
like for multiple audio/subtitle tracks.

No behavior changes, just visual changes. You still need to select the
version from More->Choose Version.

### Related issues
Closes #612

### Screenshots
<img width="535" height="476" alt="image"
src="https://github.com/user-attachments/assets/5bf80d36-7abd-4a0e-a14d-062439f024d9"
/>
<img width="958" height="581" alt="image"
src="https://github.com/user-attachments/assets/e3a0ac4e-a283-4bcf-bc16-e61d81401207"
/>
2026-01-09 14:01:37 -05:00
Ray
d2e0d527dd
Fix some track selection bugs (#649)
## 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
2026-01-07 17:27:54 -05:00
Damontecres
6906813bbc
Fix mpv error with MediaSession 2026-01-07 14:03:28 -05:00
Ray
4df17e41cd
Fix timing issues with segments (#648)
## 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 #369
Fixes #647
2026-01-07 13:23:55 -05:00
Ray
49740c47cf
Create media session during playback allowing for external control (#640)
## 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
2026-01-07 12:49:43 -05:00
Ray
79c614218d
Bump dependency versions (#645)
## Description
Bumps various third-party dependency versions
2026-01-06 22:51:25 -05:00
Damontecres
6c7a703b98
Release v0.3.11
Some checks failed
Create release / publish (push) Has been cancelled
2026-01-05 15:51:06 -05:00
Ray
356a93310f
MPV: Handle resolution switch (#635)
## 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
2026-01-05 15:38:36 -05:00
Ray
4f1c730736
Don't process reqeued MPV commands (#633)
## 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
2026-01-04 17:25:43 -05:00
Ray
02d6a98ba8
More subtitle selection fixes (#630)
## Description
Fixes more subtitle selection issues, primarily fixes the sort order of
subtitle streams. But also prefer default subtitle in some cases

Adds test cases for the described situations below

Also adds an option in the More dialog to clear track selections made on
an item/series.

### Related issues
Addresses
https://github.com/damontecres/Wholphin/issues/570#issuecomment-3707255647
&
https://github.com/damontecres/Wholphin/issues/570#issuecomment-3707292928
2026-01-04 17:06:54 -05:00
joshjryan
3d4b06db44
Guard against navigating to a non-existant tab (#629)
Just make sure we don't crash if the selectedTabIndex isn't a positive
int. Fixes #628.

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2026-01-03 18:38:07 -05:00
Ray
9345d0a698
Fix home header not updating & row movement (#627)
## Description
Removes an incorrect optimization for showing the home page header

Separates logic for focusing on rows on the home page

### Related issues
Fixes #624 
Fixes #620
2026-01-03 18:09:40 -05:00
Damontecres
36d26a5ead
Release v0.3.10
Some checks failed
Create release / publish (push) Has been cancelled
2026-01-02 15:52:36 -05:00
Ray
4d9e3cb6de
Fix MPV race condition causing no video output (#619)
## 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
2026-01-02 14:13:03 -05:00
Ray
0fe8e5d988
Show button for trailers instead of card row (#618)
## 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
2026-01-02 13:50:45 -05:00
Damontecres
b8e8a1d913
Fix yet another focus problem 2026-01-02 11:50:23 -05:00
Ray
83a543ad7d
Fix some crashes due to bad UI state (#617)
## Description
Fixes two possible crashes:
- When saving view options for a favorite page tab
- When there are no results on a grid page/tab
2026-01-02 00:24:17 -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
d15ffa13b8
Merge pull request #602 from damontecres/translations-2025-12-30
Translations as of 2025-12-30
2026-01-01 15:07:46 -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
3c11d4ba12
Fix some issues with restoring focus between pages (#613)
## Description
Yet more focus issue fixes:
- Restore focus when going back to home page from a later row
- Don't jump to episode row when going back to series overview (such as
from a person page)

This PR also changes how backgrounding the app during playback works.
Now the only goes back to the previous page when the app comes back to
the foreground. This helps with focusing back on the page properly.
There's also a bit more clean up performed.
2026-01-01 14:37: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
38697b011d
Switch from synchronized mutations to copy-on-write (#609)
## Description
Instead of synchronizing all access to event observers, use a
`CopyOnWriteArrayList`. This means sending events doesn't need to lock.
2025-12-31 16:10:29 -05:00
Ray
7e5a491d8e
Allow trusting user provided SSL/TLS certificates (#608)
## Description
Allow for trusting user added certificates

### Related issues
Closes #254
2025-12-31 14:13:07 -05:00
Ray
a5a7ed97b7
A few more UI tweaks (#603)
## Description
- Use series poster for seasons without a poster
- Larger logo on playback overlay
- Fix an issue where corner text on card on the home page would not
properly update

### Related issues
Closes #590
Closes #601
Fixes
https://github.com/damontecres/Wholphin/pull/586#issuecomment-3698613927
2025-12-30 18:39:48 -05:00
Damontecres
2b68b91d9e
Fix invalid string characters 2025-12-30 13:49:20 -05:00
lazigeri
b62cc84bf9
Translated using Weblate (Hungarian)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2025-12-29 20:39:11 +00:00
lazigeri
bad567f2bb
Translated using Weblate (Hungarian)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2025-12-29 20:39:11 +00:00
SimonHung
d2e8440a03
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2025-12-29 20:39:11 +00:00
lazigeri
0e0e0b12eb
Translated using Weblate (Hungarian)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2025-12-29 20:39:11 +00:00
lazigeri
716d81f0d2
Translated using Weblate (Hungarian)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2025-12-29 20:39:11 +00:00
SimonHung
b9a4bbd3f3
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2025-12-29 20:39:11 +00:00
jab3
f463e707cc
Translated using Weblate (Spanish)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2025-12-29 20:39:11 +00:00
butterflyoffire
ab9dbed022
Translated using Weblate (Kabyle)
Currently translated at 14.9% (47 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/kab/
2025-12-29 20:39:11 +00:00
butterflyoffire
1c92934b33
Added translation using Weblate (Kabyle) 2025-12-29 20:39:11 +00:00
opakholis
5a8dc23989
Translated using Weblate (Indonesian)
Currently translated at 97.7% (308 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2025-12-29 20:39:11 +00:00
oyvhov
c74c40dfd8
Translated using Weblate (Norwegian Nynorsk)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nn/
2025-12-29 20:39:11 +00:00
lazigeri
f240498aaa
Translated using Weblate (Hungarian)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2025-12-29 20:39:11 +00:00
texxlan
8f923c187f
Translated using Weblate (German)
Currently translated at 87.9% (277 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2025-12-29 20:39:11 +00:00
lazigeri
a2e72ddf45
Translated using Weblate (Hungarian)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2025-12-29 20:39:11 +00:00
jab3
5c9ca3b24a
Translated using Weblate (Spanish)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2025-12-29 20:39:11 +00:00
texxlan
295faf51aa
Translated using Weblate (German)
Currently translated at 77.1% (243 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2025-12-29 20:39:11 +00:00
lazigeri
6af8204402
Translated using Weblate (Hungarian)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2025-12-29 20:39:11 +00:00
OmriKumri
dda8baa46d
Translated using Weblate (Hebrew)
Currently translated at 10.7% (34 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2025-12-29 20:39:11 +00:00
OmriKumri
6f5e0d82d1
Translated using Weblate (Hebrew)
Currently translated at 6.6% (21 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/he/
2025-12-29 20:39:11 +00:00
RabSsS
6cb320c43f
Translated using Weblate (French)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2025-12-29 20:39:11 +00:00
Outbreak2096
a2535b098d
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2025-12-29 20:39:11 +00:00
adambibor
aad81f85c9
Translated using Weblate (Hungarian)
Currently translated at 99.3% (313 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2025-12-29 20:39:11 +00:00
lazigeri
8ee6b9d8b5
Translated using Weblate (Hungarian)
Currently translated at 99.3% (313 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2025-12-29 20:39:11 +00:00
arcker95
3cd26759f4
Translated using Weblate (Italian)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-12-29 20:39:11 +00:00
Priit Jõerüüt
0ccdf391de
Translated using Weblate (Estonian)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-12-29 20:39:11 +00:00
American_Jesus
8d4fcb377d
Translated using Weblate (Portuguese)
Currently translated at 100.0% (315 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2025-12-29 20:39:11 +00:00
lazigeri
dfa1ba079c
Translated using Weblate (Hungarian)
Currently translated at 81.5% (257 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2025-12-29 20:39:11 +00:00
lazigeri
ff8a9a7125
Translated using Weblate (Hungarian)
Currently translated at 78.7% (248 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2025-12-29 20:39:11 +00:00
lazigeri
225b3c0455
Translated using Weblate (German)
Currently translated at 76.8% (242 of 315 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2025-12-29 20:39:11 +00:00
SimonHung
1289bed0c4
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 88.5% (278 of 314 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2025-12-29 20:39:10 +00:00
oyvhov
955d577624
Translated using Weblate (Norwegian Nynorsk)
Currently translated at 100.0% (314 of 314 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nn/
2025-12-29 20:39:10 +00:00
SimonHung
76db702564
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 87.2% (274 of 314 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2025-12-29 20:39:10 +00:00
oyvhov
dbe8074997
Translated using Weblate (Norwegian Nynorsk)
Currently translated at 8.2% (26 of 314 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nn/
2025-12-29 20:39:10 +00:00
Outbreak2096
05fffe63a2
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (314 of 314 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2025-12-29 20:39:10 +00:00
arcker95
7930b1d4d0
Translated using Weblate (Italian)
Currently translated at 100.0% (314 of 314 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-12-29 20:39:10 +00:00
American_Jesus
ca3c5e8de9
Translated using Weblate (Portuguese)
Currently translated at 100.0% (314 of 314 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2025-12-29 20:39:10 +00:00
Ray
e3fa45d12f
Trigger tv guide scroll animation during key presses (#595)
## Description
Instead of triggering the tv guide scroll animation during composition,
do it during key presses. This fixes an edge case that crashes the main
thread when returning the guide but the underlying UI data structures
haven't been updated.

### Related issues
Fixes #592
2025-12-29 15:39:07 -05:00
Ray
243e08b635
Some minor optimizations to improve UI speed (#593)
## Description
Many tiny changes that improve UI rendering speed and reduces
recompositions

Still plenty of room for improvement though.
2025-12-29 15:36:05 -05:00
Ray
622a99631b
Fix movie page library & collection tabs not updating (#594)
## Description
Ensures the tabs on movie page use separate view models so the data
isn't shared between them.

### Related issues
Fixes #591
2025-12-29 15:35:42 -05:00
Ray
aa194f5a2d
Fix duplicates being added to Android TV watch next channel (#588)
## Description
Fixes the logic for determining which items need to be added to the
watch next channel row

Also adds some code to clean up existing duplicates. This code can be
removed around the next release.

Adds a channel to the home screen for recently added media. This is
needed to ensure full integration with the OS to allow toggling off any
of the Wholphin channels including watch next.

### Related issues
#372 introduced the bug
Fixes #587
2025-12-28 17:14:14 -05:00
Ray
ad64f9a136
Minor UI tweaks (#586)
## Description
- Show unwatched counts on the cards in the latest rows instead of child
count, which is more in line with the web UI & official app
- Show exact runtime on the overview dialog for each version
- Don't show controls after clicking up next

### Related issues
Closes #431
2025-12-27 15:59:51 -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
494ff07b72
Fix smart & default subtitle selection logic (#579)
## Description
Fixes the logic for subtitle selection when using `Smart` or `Default`
server modes. Also takes into account unknown languages.

This is heavily inspired by [the server selection
logic](https://github.com/jellyfin/jellyfin/blob/release-10.11.z/Emby.Server.Implementations/Library/MediaStreamSelector.cs).
For better or for worse, Wholphin implements this client side.

### Related issues
Fixes #570
2025-12-27 14:22:40 -05:00
Ray
fd1feddab3
Improvements to overview/media info dialog (#580)
## Description
* Splits video details into two columns
* Show index number if multiple streams of the same type
* Different font color for media section titles
* Adjustments to spacing
* Add hearing impaired flag to & remove AVC from subtitle info

### Related issues
Closes #549
2025-12-27 12:47:01 -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
Ray
bdec72752d
Use backdrop images for genre cards (#578)
## Description
Uses backdrop instead of thumb images for the genre cards since the
backdrops tend to have little text to distract from the card title

### Related issues
Closes #574
2025-12-26 15:40:43 -05:00
Damontecres
5d1f4b6154
Merge branch 'release/v0.3.9' 2025-12-25 08:57:20 -05:00
Damontecres
5d3ad335cb
Release v0.3.9
Some checks failed
Create release / publish (push) Has been cancelled
2025-12-25 08:50:46 -05:00
Damontecres
c00c7b8aa9
Catch exceptions during app startup 2025-12-25 08:49:57 -05:00
Damontecres
72f910582f
Only clear backdrop on home for full reloads 2025-12-25 08:49:33 -05:00
Damontecres
13370db48d
Only clear backdrop on home for full reloads 2025-12-24 18:15:17 -05:00
Ray
2ecc3f262d
Optimize series data loading speed (#567)
## Description
Optimizes how both series pages are loaded. If the series has a lot of
seasons and/or lots of episodes per season, the pages load much faster.

This is a combination of reducing queried information, optimizing
finding the right data to display first, and lazy loading seasons.

Note: Series that don't have episode numbers, like daily TV shows, are
not as optimized yet

### Rough speed-ups
These numbers are from my (beefy) server, so it will vary based on your
server

Series details page (one with seasons row) w/ ~50 seasons: 5s->300ms,
~13x faster

Series overview page (one with episode row) w/ ~50 seasons & 25
episodes: 6s->1.1s, ~5x faster

### Related issues
Related to #562
2025-12-24 16:47:17 -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
Justin Caveda
bcc7e660ea
Allow debug builds to to be installed alongside the stable app (#565)
## Description

This PR updates the debug build config to allow side by side
installation with the mainline/stable release by changing the app name
and app ID.

This is a convenience change to allow contributors to test debug builds
on their personal devices without overwriting the existing stable
version of the app
2025-12-24 13:31:06 -05:00
Damontecres
86e5e5ab54
Release v0.3.8
Some checks failed
Create release / publish (push) Has been cancelled
2025-12-23 12:43:20 -05:00
Ray
2b53a53155
Prevent quick details from using more veritical space if not enough horizontal space (#544)
## Description
Prevents the quick details (the dot separated details) from using more
vertical space if there is not enough horizontal space. Instead, the row
will be clipped at the total width.

This PR also gives more horizontal space to the details header on grid
pages. This matches the home page now.

### Related issues
Fixes #542
2025-12-22 19:16:12 -05:00
Ray
9a08cf3f25
Don't use saved library info for genre grids (#540)
## Description
The filtering & sorting for the genre results grid is now independent
from the actual library.

This prevents having a filter saved on the actual library that conflicts
with the Genre grid.

Also shows the genre name in the title of the page

### Related issues
Fixes #536
2025-12-22 15:44:08 -05:00
Ray
af54e777dc
Fix settings changes requiring an app restart (#539)
## Description
- Ensures settings changes take effect immediately instead of requiring
an app restart
- Add conditional & catch when checking for app updates

### Related issues
Fixes issues introduced in #538
2025-12-22 15:28:21 -05:00
Ray
eb4e9b93be
Add setting to toggle automatic sign in (#538)
## Description
The major user facing change in this PR is adding back the setting to
toggle automatic sign in on or off. This controls whether to restore the
app to where you left off or go the user selection page.

If enabled: re-opening the app will restore the previous user's session

If disabled: re-opening the app will always start with the user
selection page for the most recent server, or if there isn't a recent
server, the server selection page

This also separate this setting from the PIN protection settings in
previous PRs.

### Dev changes

This PR refactors the "setup" UI (server & user selection) to be
separate from the "app" UI (the actually app content). There is now a
`NavDisplay` for the setup flow which ends with displaying the
`ApplicationContent`'s `NavDisplay`. Most of the logic is moved out of
Compose code and into a `ViewModel`.

This means to switch users/server, the `SetupNavigationManager` must be
used instead of `NavigationManager`.


### Related issues
Closes #396
Fixes #376
2025-12-22 14:52:09 -05:00
Ray
20fe5e2626
Move MPV init & destroy to looper thread (#534)
## Description
No user facing changes in this PR

This is a continuation of #481 to move initializing and tearing down
`libmpv` onto the looper thread to prevent main thread blocking.

~The only function left that runs on the main thread is
`setVideoSurfaceView`. Since this has to directly interact with the UI,
I need to read the docs and ExoPlayer code a bit more to see how this
can safely done multi-threaded.~ It is safe/correct to attach on another
thread.
2025-12-22 10:40:42 -05:00
Ray
60838c2a86
Fixes a few bugs (#535)
## Description

A few minor bug fixes across the app

- Fix the language not showing after search on the subtitle search &
download dialog
- Clear the backdrop whenever the home page is reloading from scratch
- Enable pressing a remote's play (or play/pause) button on the series
page to play the focused episode

### Related issues
Play button follow up from #424
2025-12-21 21:31:01 -05:00
Ray
925a4b9d82
Add multi-part videos to playback queue (#520)
## Description
When playing media with multiple parts, the additional parts will be
added to the playback queue. This means a multi-part movie or episode
will be play all of the parts in order before the next item in the
queue.

This only affects the playback queue. There's no UI changes and no
indication that a movie or episode has multiple parts. I'll work on that
in a separate PR.

### Related issues
Related to #455
2025-12-21 21:30:00 -05:00
Ray
6df89fe8c9
Apply combined continue/next up, if enabled, on library recommended tabs (#533)
## Description
Respect the combine Continue Watching & Next Up setting on TV Show
library recommended tabs.

This uses the same implementation on the home screen, but only includes
items for that particular tv show library.

### Related issues
Fixes #532
2025-12-21 19:00:59 -05:00
Ray
11dda19b8a
TV Guide UI followup to #517 (#521)
## Description
- Instead of just replacing newline characters, collapse white-space
into single space characters which is roughly how the web UI does it
with CSS's `white-space: nowrap;`
- Use an actual arrow indicator instead of a `<` character for when a
program begins before the guide start

### Related issues
#517
https://github.com/damontecres/Wholphin/issues/434
https://github.com/damontecres/Wholphin/issues/435
https://github.com/damontecres/Wholphin/issues/436

### Screenshots
<img width="605" height="316" alt="image"
src="https://github.com/user-attachments/assets/6a5227b5-9960-4c6d-a086-a4a9867ea60b"
/>
2025-12-20 14:47:28 -05:00
Ray
a65b93e064
Enforce a minimum aspect ratio (#519)
## Description
When using a server provided aspect, force a minimum value (.666) in
case the server returns unusable data like zero.

Note: I have not been able to reproduce the errors in #451 to verify
that this PR will fix it. And it's not clear to me why #453 did not fix
it either. However, this PR shouldn't impact anything if the server is
sending valid aspect ratios and should at least prevent the crash on the
UI side.

### Related issues
Follow up to #453
Hopefully fixes #451
2025-12-20 14:39:59 -05:00
Ray
13d70e7623
Rework internals of ApiRequestPager (#518)
## Description
Changes how `init()` works for `ApiRequestPager` so that it 1) always
forces updating the total count if needed and 2) propagates errors
during the initial fetch up to the original caller

This means if an error occurs during the initialization of the pager
data, it won't be quietly logged and ignored. Instead, ViewModel code
will catch it and display an error in the UI.

### Related issues
Should fix #492
2025-12-20 14:39:48 -05:00
Ray
5c5db4c1ef
TV Guide title clean up & align start to half hours (#517)
## Description
- Cleans up program titles & subtitles by removing newline characters
- Align the start of the guide to half hour, rounded down, ie 9:52->9:30
or 11:05->11:00
- Add some visual indication that a program has started before the guide
start time
- This is done by adding a `<` prefix to the title and using sharp 90
degree corners at the start instead of rounded

### Related issues
Closes #434
Closes #435
Closes #436
2025-12-20 11:54:41 -05:00
Ray
693398536e
Better images for genre grid (#501)
## Description
Overhauls the genre cards for Movies & TV Show libraries.

Now the cards use a semi-transparent, consistent, generated color using
the same logic as in #478. And a random item from the library with that
genre is show in the background. This is heavily inspired from Plex.

If there are not applicable items for the genre, then just the colored
card is shown.

### Related issues
Fixes #490
2025-12-19 10:46:57 -05:00
Justin Caveda
e60748a0d0
[UI] Add playback overlay dark gradient background (#505)
## Description

This PR adds a background dimming effect behind the playback controls to
fix readability issues against bright video content. It implements a
full-screen vertical gradient that ensures white text and buttons remain
legible.

I added a vertical gradient fading from transparent at the top to ~95%
black at the bottom which adds contrast for UI elements without
obscuring the center of the video.

### Related issues

Solves [#485](https://github.com/damontecres/Wholphin/issues/485)

### Screenshots


<img width="1948" height="1112" alt="playback"
src="https://github.com/user-attachments/assets/f671072c-e37a-491d-b82f-fb6f888a42c7"
/>

<img width="1918" height="1081" alt="no_gradient"
src="https://github.com/user-attachments/assets/30bc2fee-ef5a-4d63-87a6-953e401cd84b"
/>


### AI/LLM usage

This change was developed with the assistance of AI, but was not wholly
generated by AI.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Damontecres <damontecres@gmail.com>
Co-authored-by: Ray <154766448+damontecres@users.noreply.github.com>
2025-12-19 10:42:34 -05:00
Damontecres
0c823b91cd
Release v0.3.7
Some checks failed
Create release / publish (push) Has been cancelled
2025-12-18 18:26:24 -05:00
Ray
dda1c39dd2
Add Amazon app store link 2025-12-18 18:20:57 -05:00
Ray
c5214d886e
Fix backdrop not clearing on favorite tab changes (#507)
## Description
Fixes the backdrop not being cleared when switching tabs on the
favorites page by ensuring each has a unique view model.

Also clears the backdrop if the view option to show details is turned
off.

### Related issues
#476 
#499
2025-12-18 16:20:24 -05:00
Amar Sandhu
fd260f2709
Add "Ends at" time display. (#475)
Addresses #441 .

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2025-12-18 15:52:27 -05:00
Ray
1855c4666b
Always show video as Dolby Vision if it has DoVi title (#502)
## Description
Always show the "Dolby Vision" label instead of just HDR if the video
has a Dolby Vision title. This matches the behavior of the official app.

### Related issues
Fixes #491
2025-12-18 14:28:22 -05:00
Ray
644bf51aff
Fix backdrop not clearing between tabs (#500)
## Description
Follow up to #476 to clear the backdrop when switching tabs

### Related issues
Fixes #499
2025-12-18 14:01:21 -05:00
Ray
8b40e89b8a
Update MPV playback threading model (#481)
There's no user facing changes in this PR

This PR updates `MpvPlayer` to use a threading model more similar to
[ExoPlayer's
model](https://developer.android.com/reference/androidx/media3/exoplayer/ExoPlayer#threading-model).

There's now a dedicated thread looper message queue between the UI
interactions on the main thread and calls to `libmpv`. This is necessary
because previously, the main thread would make the calls into `libmpv`.
`libmpv` uses internal mutex locking for some operations which meant
that the main thread would be blocked momentarily.

Now, the UI issues a command (eg seek to X position) and the command is
added to the thread looper queue and processed on that separate thread.
This uses the standard Android Handler/Looper mechanisms.

Related to #235
2025-12-17 18:24:25 -05:00
Ray
7320c12f0c
Merge pull request #497 from damontecres/translations-2025-12-17
Fix merge
2025-12-17 17:19:36 -05:00
Ray
8c0defb772
Add translations as of 2025-12-17 (#495)
Adds another batch of translated strings.

Thank you to all of the contributors!

---------

Co-authored-by: Priit Jõerüüt <jrtcdbrg@noreply.codeberg.org>
Co-authored-by: ymir <ymir@noreply.codeberg.org>
Co-authored-by: ianvm <ianvm@noreply.codeberg.org>
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: Outbreak2096 <outbreak2096@noreply.codeberg.org>
Co-authored-by: American_Jesus <american_jesus@noreply.codeberg.org>
Co-authored-by: Vistaus <vistaus@noreply.codeberg.org>
Co-authored-by: Geekice <geekice@noreply.codeberg.org>
Co-authored-by: opakholis <opakholis@noreply.codeberg.org>
Co-authored-by: RabSsS <rabsss@noreply.codeberg.org>
Co-authored-by: arcker95 <arcker95@noreply.codeberg.org>
Co-authored-by: Rand0mB0y <rand0mb0y@noreply.codeberg.org>
Co-authored-by: SimonHung <simonhung@noreply.codeberg.org>
Co-authored-by: efko <efko@noreply.codeberg.org>
Co-authored-by: DragoWing <dragowing@noreply.codeberg.org>
Co-authored-by: oyvhov <oyvhov@noreply.codeberg.org>
2025-12-17 17:01:08 -05:00
Ray
8479d8621d
Bump dependency versions, notably media3 (#496)
## Description
Bumps dependency versions, notably media3/ExoPlayer to `1.9.0`

### Related issues

Fixes #108
Fixes #355
Fixes #479
2025-12-17 17:00:15 -05:00
Priit Jõerüüt
badb56880b
Translated using Weblate (Estonian)
Currently translated at 100.0% (314 of 314 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-12-17 18:25:40 +00:00
oyvhov
e19cf0de2c
Translated using Weblate (Norwegian Nynorsk)
Currently translated at 8.0% (25 of 311 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nn/
2025-12-17 18:25:40 +00:00
oyvhov
0ed15a3251
Added translation using Weblate (Norwegian Nynorsk) 2025-12-17 18:25:40 +00:00
efko
c94dcbf3dd
Translated using Weblate (Greek)
Currently translated at 57.8% (180 of 311 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/el/
2025-12-17 18:25:40 +00:00
Outbreak2096
461caaa8fd
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (311 of 311 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2025-12-17 18:25:40 +00:00
DragoWing
5ca5826cb3
Translated using Weblate (Dutch)
Currently translated at 99.3% (309 of 311 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2025-12-17 18:25:40 +00:00
Priit Jõerüüt
e816151651
Translated using Weblate (Estonian)
Currently translated at 100.0% (311 of 311 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-12-17 18:25:40 +00:00
arcker95
da12095434
Translated using Weblate (Italian)
Currently translated at 100.0% (311 of 311 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-12-17 18:25:40 +00:00
American_Jesus
5f3a7ebff1
Translated using Weblate (Portuguese)
Currently translated at 100.0% (311 of 311 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2025-12-17 18:25:40 +00:00
efko
a67bd0f83f
Translated using Weblate (Greek)
Currently translated at 44.6% (139 of 311 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/el/
2025-12-17 18:25:40 +00:00
SimonHung
cb62344e53
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 87.1% (271 of 311 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2025-12-17 18:25:40 +00:00
ymir
f2e3ae2447
Translated using Weblate (French)
Currently translated at 100.0% (310 of 310 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2025-12-17 18:25:40 +00:00
Codeberg Translate
131b44c439
Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/
2025-12-17 18:25:40 +00:00
Rand0mB0y
7da9189b4d
Translated using Weblate (Greek)
Currently translated at 20.3% (63 of 310 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/el/
2025-12-17 18:25:40 +00:00
Rand0mB0y
1497a0718b
Added translation using Weblate (Greek) 2025-12-17 18:25:40 +00:00
opakholis
6a7a2a5c79
Translated using Weblate (Indonesian)
Currently translated at 98.0% (304 of 310 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2025-12-17 18:25:40 +00:00
RabSsS
d0000e4811
Translated using Weblate (French)
Currently translated at 98.7% (306 of 310 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2025-12-17 18:25:40 +00:00
Outbreak2096
cae07b0acd
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (310 of 310 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2025-12-17 18:25:40 +00:00
Vistaus
79b84cfe6b
Translated using Weblate (Dutch)
Currently translated at 100.0% (310 of 310 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2025-12-17 18:25:40 +00:00
arcker95
a68e9d8eb9
Translated using Weblate (Italian)
Currently translated at 100.0% (310 of 310 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-12-17 18:25:40 +00:00
RabSsS
c0a68c5daa
Translated using Weblate (French)
Currently translated at 98.0% (304 of 310 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2025-12-17 18:25:40 +00:00
opakholis
dcd9560113
Translated using Weblate (Indonesian)
Currently translated at 84.1% (261 of 310 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2025-12-17 18:25:40 +00:00
Priit Jõerüüt
5dbc2049cb
Translated using Weblate (Estonian)
Currently translated at 100.0% (310 of 310 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-12-17 18:25:40 +00:00
American_Jesus
1c38979c48
Translated using Weblate (Portuguese)
Currently translated at 100.0% (310 of 310 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2025-12-17 18:25:40 +00:00
Outbreak2096
1caa7b32c4
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 91.5% (280 of 306 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2025-12-17 18:25:40 +00:00
American_Jesus
9fde9c70ed
Translated using Weblate (Portuguese)
Currently translated at 99.6% (305 of 306 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2025-12-17 18:25:40 +00:00
Geekice
b8891eee35
Translated using Weblate (Italian)
Currently translated at 97.0% (297 of 306 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-12-17 18:25:40 +00:00
Priit Jõerüüt
4b3efc097e
Translated using Weblate (Estonian)
Currently translated at 100.0% (306 of 306 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-12-17 18:25:40 +00:00
American_Jesus
eef886b285
Translated using Weblate (Portuguese)
Currently translated at 99.3% (304 of 306 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2025-12-17 18:25:40 +00:00
Outbreak2096
1c890f64aa
Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (272 of 272 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hans/
2025-12-17 18:25:40 +00:00
Vistaus
8b20c392f3
Translated using Weblate (Dutch)
Currently translated at 100.0% (272 of 272 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2025-12-17 18:25:40 +00:00
ymir
56416da486
Translated using Weblate (French)
Currently translated at 100.0% (272 of 272 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2025-12-17 18:25:40 +00:00
American_Jesus
ca7c8b2e5f
Translated using Weblate (Portuguese)
Currently translated at 90.0% (245 of 272 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2025-12-17 18:25:40 +00:00
Outbreak2096
8bdd030f42
Added translation using Weblate (Chinese (Simplified Han script)) 2025-12-17 18:25:40 +00:00
Codeberg Translate
6e77afe672
Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/
2025-12-17 18:25:39 +00:00
ianvm
4fac067fb0
Translated using Weblate (Dutch)
Currently translated at 100.0% (272 of 272 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2025-12-17 18:25:39 +00:00
ymir
18a2f648ca
Translated using Weblate (French)
Currently translated at 100.0% (272 of 272 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2025-12-17 18:25:39 +00:00
Priit Jõerüüt
6776640087
Translated using Weblate (Estonian)
Currently translated at 99.6% (271 of 272 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-12-17 18:25:39 +00:00
Ray
7581b05b97
Move time tracking into a CompositionLocal (#493)
## Description
Creates a `CompositionLocal` which tracks the current time in a single
place and is usable anywhere in the app.

Can be used such as:
```kotlin
val now = LocalClock.current.now
val details = remember(item, now) {
  // Create detail strings based the item and current time
  // This will be recomposed/updated whenever the time changes
}
```

### Related issues
Related to #475
2025-12-17 13:25:36 -05:00
YogiBear12
2908f539e2
Redesign alphabet picker to fit all letters on screen (#480)
- Switch to tv.material3.Button with ButtonDefaults for better styling
- Add opacity changes based on focus (0.85f focused, 0.2f unfocused)
- Keep selected letter fully visible when picker is unfocused
- Reduce button size from 24.dp to 14.dp to fit all letters
- Reduce spacing between letters (1.1.dp vertical, 2.dp horizontal)
- Use transparent backgrounds for unfocused letters
- Use border color for selected letter when focused, tertiary when
unfocused
- Add Box with CircleShape clip to prevent focus indicator overflow
- Reduce font size by 15%
- Add 16.dp end padding to push picker away from screen edge

When using the 'Show details' view option, the alphabet picker will
still overflow the bottom edge of the screen. I find this an acceptable
option though, as the backdrop pushes the card grid further down the
page.

Closes https://github.com/damontecres/Wholphin/issues/386

>[!NOTE]
> AI was used in the making of this PR

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2025-12-17 12:16:46 -05:00
YogiBear12
801ff5e67b
Redesign Select Server and Select User screens (#478)
Redesign Select Server and Select User screens with horizontal card
layout, similar to traditional streaming services

- Replace vertical lists with horizontal list of icons
- Center align text in Quick Connect and Username/Password modals
- Display Jellyfin profile images
- Fallback to first letter of username if no image is available
- Filter out duplicate server entries in the auto-discovery list

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2025-12-17 11:56:19 -05:00
Ray
bbfbc13532
Backdrop improvements (#476)
## Description

The backdrop image shown on most pages is now configurable in advanced
settings:
* Image w/ dynamic color - Shows the backdrop with dynamic color filling
the entire space (new default)
* Image only - Shows only the backdrop image (current Wholphin behavior)
* None - Don't show any backdrops

Additionally, the backdrop is retained between page loads for the same
items.

## Acknowledgements

Big thanks to @YogiBear12 from which the idea and implementation for the
dynamic color was adapted from!

Code adapted from
https://github.com/YogiBear12/Halfin/blob/main/COLOR_EXTRACTION_AND_BACKDROP.md#backdrop-fading-and-transitions

## Issues

Closes #221
Closes #461
Closes #442



https://github.com/user-attachments/assets/1acbc487-c697-44d7-89ed-e7e4c9169360
2025-12-17 11:18:57 -05:00
Ray
8224f83678
Add pull request template 2025-12-16 20:01:56 -05:00
Damontecres
5b21e352bc
Update development.md with info on setting up the dev environment 2025-12-16 19:35:14 -05:00
Damontecres
f77d345abf
Allow null ItemTrackModification from DB 2025-12-16 13:52:44 -05:00
damontecres
d5a55ce4ad
Fix trickplay image scaling (#477)
Fix to #467 to scale the tiles by the actual calculated ratio based on
the server's fixed width and our target width instead of using the
general density scaling

I verified this on 1080p & 4k with 4:3, 16:9, & 2.35:1 videos.

Fixes
https://github.com/damontecres/Wholphin/issues/432#issuecomment-3657381461
2025-12-15 16:53:06 -05:00
damontecres
ebfc00c016
Always save series audio & subtitle preferences regardless of preferred languages (#473)
## Description

This PR adjust the logic for audio & subtitle track selection for TV
series. Instead of only saving for future episodes if the newly selected
track is a different language than the user's preferred, always save it.

Basically this means if you change the audio or subtitle language for an
episode, the same audio or subtitle language will be used for future
episodes (if available).

Also, fixes the "default" subtitle mode to take the user's preferred
language into consideration.

## Issues
Follow up to https://github.com/damontecres/Wholphin/issues/378 &
https://github.com/damontecres/Wholphin/issues/427

Fixes #469
2025-12-15 12:15:17 -05:00
damontecres
ab8bbf2bd7
MPV: Adjust subtitle delay/offset (#470)
**This is for MPV playback backend only!** Since this only works in MPV
for now, the option is not shown if using ExoPlayer.

Adds a setting (the gear icon during playback) to adjust the subtitle
delay.

A negative delay shows subtitles sooner. A positive delay shows them
later. For example, if a subtitle is supposed to show between
1000ms-2000ms, a negative 250ms delay will show it at 750ms-1750ms
instead.

The delay is persisted between plays of the same media & subtitle track.

Ref: #12 


![subtitle_delay](https://github.com/user-attachments/assets/824e3a28-650a-4619-9535-b67addaabf1e)
2025-12-15 10:06:36 -05:00
Damontecres
3f5ce703d2
Release v0.3.6
Some checks failed
Create release / publish (push) Has been cancelled
2025-12-14 19:21:33 -05:00
damontecres
7ab39eba57
Improve trickplay image preview speed (#467)
Moves the clipping of trickplay images from the image loading layer to
the composition/graphics layer.

Effectively, this PR loads and renders the entire trickplay image which
is then moved around to the right position and finally is clipped so
only a single tile is shown. Since moving the image around on screen
plsu clipping is 1) very fast and 2) done during recompositions, this
means it is _much_ faster at rendering each trickplay tile image and
Coil only has to load the image once.

Fixes #432
2025-12-14 19:04:01 -05:00
damontecres
ff43f8b1b5
Use sdk provided device name (#457)
Use the SDK to get the device name since it handles older Android
versions and has a fallback.

This should not change the device ID or name if the device was working
before this change.

Maybe related to #454
2025-12-14 10:07:46 -05:00
damontecres
43703436c5
Switch back to older text input (#459)
Revert some of the changes to input text fields from #327. This PR
switches back to using the `value/onValueChange` variants for single
line input.

Fixes #413
2025-12-13 21:03:41 -05:00
damontecres
a386028b6b
Fix possible NPE during refresh rate switch (#448)
Use current thread's looper or fallback to the main looper for the
display change callback.

This is the behavior in newer versions of Android whereas older versions
assumed a non-null looper.

Fixes #446
2025-12-13 17:58:30 -05:00
damontecres
d2e1c4683d
Only use server provided aspect ratios if >0 (#453)
Fixes #451
2025-12-13 16:43:49 -05:00
damontecres
6edcfb1067
Customize TV guide display (#443)
Adds a view options button for the TV guide to allow some basic
customization:
* Show details header
* Adjust how channels are sorted
* Toggle color coding for programs

Related to #250 
Closes #288 
Fixes #439
2025-12-12 22:05:01 -05:00
damontecres
0d8800863b
Prefetch trickplay images (#445)
Prefetch trickplay images and cache them so that quick scrolling doesn't
require network calls and images will appear significantly faster

Fixes #432
2025-12-12 22:04:52 -05:00
Damontecres
652becd685
Disable pin check 2025-12-12 19:16:43 -05:00
Amar Sandhu
c3f6fd7f60
Add media information dialog. (#416)
Addresses #414

---------

Co-authored-by: Damontecres <damontecres@gmail.com>
2025-12-12 11:49:30 -05:00
damontecres
6d6156917c
Update readme with features 2025-12-11 16:28:03 -05:00
damontecres
ce2e34294c
Merge pull request #426 from damontecres/translations-2025-12-11
Fix merge from #425
2025-12-11 13:05:10 -05:00
damontecres
e381df0295
Add batch of translations (#425)
Update translations from
https://translate.codeberg.org/projects/wholphin/

---------

Co-authored-by: damontecres <damontecres@noreply.codeberg.org>
Co-authored-by: n8llcaster <n8llcaster@noreply.codeberg.org>
Co-authored-by: RabSsS <rabsss@noreply.codeberg.org>
Co-authored-by: Vistaus <vistaus@noreply.codeberg.org>
Co-authored-by: Priit Jõerüüt <jrtcdbrg@noreply.codeberg.org>
Co-authored-by: sarjona98 <sarjona98@noreply.codeberg.org>
Co-authored-by: SimonHung <simonhung@noreply.codeberg.org>
Co-authored-by: arcker95 <arcker95@noreply.codeberg.org>
Co-authored-by: streamyfinchris <streamyfinchris@noreply.codeberg.org>
Co-authored-by: opakholis <opakholis@noreply.codeberg.org>
Co-authored-by: Tellos <tellos@noreply.codeberg.org>
Co-authored-by: SuperCool <supercool@noreply.codeberg.org>
Co-authored-by: Pawsi <pawsi@noreply.codeberg.org>
Co-authored-by: American_Jesus <american_jesus@noreply.codeberg.org>
Co-authored-by: ictu <ictu@noreply.codeberg.org>
Co-authored-by: adambibor <adambibor@noreply.codeberg.org>
2025-12-11 13:03:27 -05:00
damontecres
2403a44c18
Add view option for hiding card titles (#422)
Adds a view option for grids to show/hide titles. It is enabled by
default.

Closes #403
2025-12-11 13:03:16 -05:00
damontecres
f63a30d295
Add additional ffmpeg audio decoders (#423)
Adds a few more ffmpeg audio decoders for ExoPlayer

Fixes #417
2025-12-11 12:55:52 -05:00
Damontecres
c2ad0a6a8a
Fix pre-commit 2025-12-11 12:55:31 -05:00
damontecres
c5c0dff705
Fix subtitle smart mode to check chosen audio language (#420)
Fixes how the smart subtitle mode logic works

Fixes #406
2025-12-11 12:52:21 -05:00
damontecres
319b46983f
Play item directly when clicking play button on remote (#424)
On the home page or any grid, if focused on a "playable" item (movie,
episode, etc), pressing the Play or Play-Pause button on the remote will
immediately start playback.

Dev note: Also guards against an edge case where an item passed into
playback doesn't have media sources due to more limited item fields in
the query.
2025-12-11 12:52:13 -05:00
Priit Jõerüüt
e4e56bf7e1
Translated using Weblate (Estonian)
Currently translated at 100.0% (271 of 271 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-12-11 15:40:38 +00:00
adambibor
163da18f73
Translated using Weblate (Hungarian)
Currently translated at 62.3% (169 of 271 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/hu/
2025-12-11 15:40:38 +00:00
adambibor
86d58eba04
Added translation using Weblate (Hungarian) 2025-12-11 15:40:38 +00:00
Pawsi
2f5d6f7e1c
Translated using Weblate (Polish)
Currently translated at 89.6% (243 of 271 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2025-12-11 15:40:38 +00:00
ictu
7d465b1a60
Translated using Weblate (Polish)
Currently translated at 89.6% (243 of 271 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2025-12-11 15:40:38 +00:00
Pawsi
048bf89a66
Translated using Weblate (Polish)
Currently translated at 89.6% (243 of 271 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pl/
2025-12-11 15:40:38 +00:00
American_Jesus
f0b3f01fba
Translated using Weblate (Portuguese)
Currently translated at 27.3% (74 of 271 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/pt/
2025-12-11 15:40:38 +00:00
Pawsi
5ee8df7939
Added translation using Weblate (Polish) 2025-12-11 15:40:38 +00:00
streamyfinchris
c5712415f2
Translated using Weblate (Swedish)
Currently translated at 85.2% (231 of 271 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2025-12-11 15:40:38 +00:00
SuperCool
4428f6b093
Added translation using Weblate (Hebrew) 2025-12-11 15:40:38 +00:00
Vistaus
4dd4aa8198
Translated using Weblate (Dutch)
Currently translated at 100.0% (271 of 271 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2025-12-11 15:40:38 +00:00
SimonHung
ecab42e443
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 90.0% (244 of 271 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2025-12-11 15:40:38 +00:00
arcker95
66b60bab4d
Translated using Weblate (Italian)
Currently translated at 100.0% (271 of 271 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-12-11 15:40:38 +00:00
Tellos
527d104074
Translated using Weblate (Russian)
Currently translated at 100.0% (267 of 267 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ru/
2025-12-11 15:40:38 +00:00
Vistaus
c984c02ff5
Translated using Weblate (Dutch)
Currently translated at 100.0% (267 of 267 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2025-12-11 15:40:37 +00:00
arcker95
a0472fdbed
Translated using Weblate (Italian)
Currently translated at 100.0% (267 of 267 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-12-11 15:40:37 +00:00
Tellos
01d10ea128
Translated using Weblate (Russian)
Currently translated at 91.7% (244 of 266 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ru/
2025-12-11 15:40:37 +00:00
Tellos
062e7f331d
Translated using Weblate (Russian)
Currently translated at 65.7% (175 of 266 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/ru/
2025-12-11 15:40:37 +00:00
arcker95
ac8a2a40b7
Translated using Weblate (Italian)
Currently translated at 100.0% (266 of 266 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-12-11 15:40:37 +00:00
Tellos
34bc553419
Added translation using Weblate (Russian) 2025-12-11 15:40:37 +00:00
Vistaus
232ba120e0
Translated using Weblate (Dutch)
Currently translated at 100.0% (266 of 266 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2025-12-11 15:40:37 +00:00
arcker95
18c7a05dd3
Translated using Weblate (Italian)
Currently translated at 100.0% (264 of 264 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-12-11 15:40:37 +00:00
Priit Jõerüüt
b409b42300
Translated using Weblate (Estonian)
Currently translated at 100.0% (246 of 246 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-12-11 15:40:37 +00:00
Vistaus
f462c7e78f
Translated using Weblate (Dutch)
Currently translated at 100.0% (246 of 246 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2025-12-11 15:40:37 +00:00
arcker95
78d0a3062c
Translated using Weblate (Italian)
Currently translated at 100.0% (246 of 246 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-12-11 15:40:37 +00:00
opakholis
3dd6d82f58
Translated using Weblate (Indonesian)
Currently translated at 99.1% (243 of 245 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2025-12-11 15:40:37 +00:00
streamyfinchris
84a3e899ff
Translated using Weblate (Swedish)
Currently translated at 84.8% (208 of 245 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2025-12-11 15:40:37 +00:00
Vistaus
88a76fb959
Translated using Weblate (Dutch)
Currently translated at 100.0% (245 of 245 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2025-12-11 15:40:37 +00:00
arcker95
288a64fb35
Translated using Weblate (Italian)
Currently translated at 100.0% (245 of 245 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-12-11 15:40:37 +00:00
Priit Jõerüüt
caf2f99141
Translated using Weblate (Estonian)
Currently translated at 100.0% (245 of 245 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-12-11 15:40:37 +00:00
SimonHung
9e82f42083
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 99.1% (243 of 245 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2025-12-11 15:40:37 +00:00
arcker95
bb238fed43
Translated using Weblate (Italian)
Currently translated at 100.0% (244 of 244 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-12-11 15:40:37 +00:00
SimonHung
77f12d7933
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 99.1% (242 of 244 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2025-12-11 15:40:37 +00:00
RabSsS
26c17bf84a
Translated using Weblate (French)
Currently translated at 100.0% (244 of 244 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2025-12-11 15:40:37 +00:00
sarjona98
5386040c1c
Added translation using Weblate (Catalan) 2025-12-11 15:40:37 +00:00
Priit Jõerüüt
988bead39c
Translated using Weblate (Estonian)
Currently translated at 100.0% (244 of 244 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-12-11 15:40:37 +00:00
Vistaus
8edb5b3c36
Translated using Weblate (Dutch)
Currently translated at 100.0% (244 of 244 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2025-12-11 15:40:37 +00:00
RabSsS
a8eef63a18
Translated using Weblate (French)
Currently translated at 100.0% (244 of 244 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2025-12-11 15:40:37 +00:00
n8llcaster
76d5d19ba0
Translated using Weblate (Slovak)
Currently translated at 43.4% (106 of 244 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2025-12-11 15:40:37 +00:00
damontecres
2331f8362a
Translated using Weblate (Spanish)
Currently translated at 99.5% (243 of 244 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2025-12-11 15:40:37 +00:00
damontecres
c97553f82a
Support login for users without passwords (#421)
Just requires a username to be entered to enable the login button.

Fixes #419
2025-12-11 10:40:32 -05:00
damontecres
0f4254c6ec
Persist audio & subtitle track changes for series if in different language (#405)
This only applies if the user has set a preferred audio and/or subtitle
language on the web UI!

If the user chooses an audio/subtitle track that is a different language
than their preferred, it will be remembered for that series. Playback of
future episodes of the series will pick a matching language.

The audio & subtitle selections are independent. You can make the choice
before playback or during playback.

Closes #378
2025-12-11 10:16:45 -05:00
damontecres
033bdccaa2
Add Google Play Store link to readme
Added a link to the Google Play Store for Wholphin.
2025-12-10 13:06:12 -05:00
damontecres
7644b4c586
Minor UI changes (#407)
- Semi-bold the movies or series title on the details pages to match
home page
- Shows total subtitle track count if subtitles disabled
- Remove the springy animations from the play/watch/favorite/more
buttons
- Go back a page when playback finishes and there's nothing left in the
queue

Closes #393
2025-12-09 14:00:06 -05:00
damontecres
25fa8a73c2
Readme updates for v0.3.5
Updated images and added new features to the README.
2025-12-08 10:34:41 -05:00
Damontecres
2cc3de6eff
Release v0.3.5
Some checks failed
Create release / publish (push) Has been cancelled
2025-12-08 10:22:26 -05:00
damontecres
82927d1968
Remove PIN & sign-in auto settings (#397)
Unfortunately, I think the PIN and automatic sign-in features require
more thought before it should be rolled out as a stable feature.

This PR disables the two settings and removes them from the UI.

See discussions in #321 and will reopen #268
2025-12-08 10:13:51 -05:00
damontecres
fa141adc60
Ignore auto sign in if user has PIN (#392)
If the current user has a PIN, the sign in automatically setting is
ignored if the app is put in the background or restarted.

Follow up to #356 &
https://github.com/damontecres/Wholphin/issues/321#issuecomment-3621312496

I think the PIN protection needs some more thought overall.
2025-12-07 17:48:46 -05:00
damontecres
e0d50baf15
Fix bugs in #380 & #381 (#391)
Just a few minor bugs fixes introduced in recent PRs
2025-12-06 21:28:29 -05:00
damontecres
9e7742034a
Update style for ktlint 1.8 (#390)
Updated `ktlint`, so lots of code style changes, no user facing changes
2025-12-06 18:11:07 -05:00
Damontecres
e9c54adaba
Update pre-commit config 2025-12-06 18:00:45 -05:00
damontecres
20bef8fb6a
Automatic refresh rate switching setting (#382)
Adds an advanced playback setting to enable automatic refresh rate
switching during playback

When enabled, the app looks at the available display modes, chooses the
best candidate, and attempts to switch to it. After playback ends, the
display is switched back to the original mode.

Closes #62
Closes #379
2025-12-06 16:35:43 -05:00
damontecres
e669f3ad59
Fix combining continue/next-up for new episode premieres (#381)
Better handling for combining continue/next-up by considering the
premiere date of the episode. So episodes will use the previously
watched one's last played date or the episode's premiere date, whichever
is newer.

This means when a new season comes out or the show returns from a break,
the new episodes will show earlier in the list like the next up rows
shows when not combined.

Fixes #377
2025-12-06 15:35:29 -05:00
damontecres
793f9802e4
Resize & reshape buttons (#380)
Adds a new button implementation which allows for icon buttons to be
circular. Overall the buttons are reduced in size.

Also, the playback controls are are reduced in size, buttons are smaller
and vertical padding is reduced.

![buttons
Large](https://github.com/user-attachments/assets/2211d00f-60a7-4a61-be51-a09046fc7433)
2025-12-06 15:35:17 -05:00
damontecres
ac10517028
Add setting for subtitle outline size (#375)
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

![outline
Large](https://github.com/user-attachments/assets/dfef74a5-a4d4-49db-a8d2-231e46ab151e)

Dev note: the ExoPlayer implementation is bad code, don't write code
like this. It works though
2025-12-05 16:55:46 -05:00
damontecres
a197f6911b
Fixes to user interface (#374)
- Fixes a crash if the same person has multiple roles in an item
- Better tab focus behavior, especially on series pages
- Use consistent series production years on all pages
- Slightly decrease size and increase space on home page
2025-12-05 11:35:50 -05:00
damontecres
887b7b597e
Show people in epsisodes on series overview page (#373)
On the series overview page, show rows below the play/watch/favorite
buttons for cast & crew and guest stars in the episode


![episode_people](https://github.com/user-attachments/assets/2d469369-82d8-4338-9d56-325d4e49addf)
2025-12-04 21:12:53 -05:00
damontecres
8ea84b3efe
Add setting to protect a user profile with a PIN (#356)
Allows for setting a per-user profile PIN that must be entered to switch
to the user. The PIN is a sequence of directional buttons, ie D-Pad
left/right/up/down.

This PIN is per device per user per server. The PIN is stored locally
and not sent nor shared with the server. If you forget your PIN, can
still access the app using server credentials which also removes the
PIN.

Additionally, there is another setting to disable automatically sign in.
When enabled (default) the last user is automatically restored, even if
there is a PIN. If disabled, a server & user must always be selected
when opening the app.

Closes #268
Closes #321
2025-12-04 14:37:27 -05:00
damontecres
a294661520
Support custom mpv.conf (#327)
Part of #235 
Closes #305

Adds a preference for specifying a custom `mpv.conf`

Also adds a preference to toggle between `vo=gpu` and `vo=gpu-next`, see
https://github.com/mpv-player/mpv/wiki/GPU-Next-vs-GPU

There are some caveats:
* Multi-line text entry via a remote is rough
* [Track selection
options](https://mpv.io/manual/stable/#track-selection) may not work as
expected because Wholphin manages this itself
* Bad settings will crash the app hard

Dev note: This rewrites the text field inputs to use the [state based
implementations](https://developer.android.com/develop/ui/compose/text/migrate-state-based)
2025-12-04 13:21:40 -05:00
damontecres
45cb385b9b
Bump compose version (#371)
The blog says this compose version improved scroll
performance:https://android-developers.googleblog.com/2025/12/whats-new-in-jetpack-compose-december.html

Dev notes: Also switches a few more compose libraries to use the BOM
version instead of defining per library
2025-12-04 12:45:02 -05:00
damontecres
384401a72c
Customize display of library grid pages (#368)
## Details

Adds options to customize how grid pages are displayed. Every grid page
can be individually customized.

The options are saved per user on the device and restored when
navigating back to the page. The pages that support it have a new button
next to the sort/filter buttons and changes are updated live.

The options are:
* Primary or Thumb image
* Aspect ratio
* Show details header w/ backdrop (similar to the one on the home page)
* Number of columns
* Spacing between cards
* Card image content scale (eg Fit, Fill, Crop, etc)

Note: the Genre tabs' grids cannot be customized yet

### Other changes
- Nav drawer now opens over top of content instead of pushing it
- Images for most cards are fetched with actual fill size

### Possible future work
* Customize Genre grids
* Add more image types, banners?
* Add similar customization options to pages with rows, eg Home page and
Recommended tabs

## Example

For example, this is a movie library using thumb images with 8 columns
and reduced spacing. It also shows the details header

## Issues
Closes #186
Related to #72
2025-12-04 12:20:50 -05:00
Damontecres
b98fb4a1d8
Release v0.3.4
Some checks failed
Create release / publish (push) Has been cancelled
2025-12-03 15:57:22 -05:00
damontecres
bddf51e076
Fix some threading issues (#364)
Fixes thread used by changes from #359 

Also create the device direct play profile lazily-ish off of the main
thread because this can take some time to create.

Should help address some of the "too much work" reported in #363, and
also fixes the network-on-main exception in the logs on #363
2025-12-02 19:17:28 -05:00
damontecres
56bf3cb7a0
Optimizes recommended content & image caching (#359)
## Details
This PR contains a number of optimizations

### Images
Updates to request images with maximum sizes

The max sizes are estimates on the maximum number of pixels that will be
actually displayed. This means the server is downscaling large images
which means 1) better quality downscaling and 2) smaller images sent
over the network and cached

This should mean slightly better quality images and more images that can
be cached locally

### Paging
Reduces network calls when fetching paginated data, ie grid pages and
some rows. This PR combines the total record count & first page queries
which slightly reduces total time to load paginated data.

Additionally, on movies/tv show recommended pages, the rows now only
fetch a limited number of items (same as number on home page). While
they previously fetched using pagination, there is an overhead required
to paginate requests, so this is slightly faster.

### Async recommended fetching
On movies/tv show recommended pages, the rows are now fetched completely
separately from each other. Previously, the rows were requested
separtely, but each was only added to the UI in order as each completed.
Now they will add as the data arrived.

Note: Top unwatched TV shows is still very slow on `10.11.x`, see #204


### Image cache

The local, on-disk image cache size is increased from 100mb to 200mb.
Combined with the smaller images, this covers about 1500-2000 poster
images, but will vary by server.

The cache size is now also configurable in advanced settings from 25mb
to 1000mb. Additionally, you can see how much of both the disk and
memory caches are in use.

## Issues
Closes #330 
Related to #120
2025-12-02 16:00:21 -05:00
damontecres
10233eb459
Support VC-1 direct play if device supports it (#362)
Adds VC-1 to ExoPlayer direct play profile, if the device supports it.

Also proposed to add this to the official app in
https://github.com/jellyfin/jellyfin-androidtv/pull/5208
2025-12-02 10:56:39 -05:00
damontecres
8b33dd6d1f
Updates series details, adds movie genres, & adds critic rating (#358)
## Details

Updates the series details page to match style & layout of the new movie
details page (from #279 & #327)

Adds genre list to movie details, it was already on series details

Adds critic rating using fresh or rotten tomato icons, if available

## Screenshots
### Movie

![movie](https://github.com/user-attachments/assets/9bb9cb2e-921c-4f4b-8ea1-a1e73b1842f1)
### TV Show

![series](https://github.com/user-attachments/assets/542989da-bf39-46a4-aefb-7728613a97b1)

## Issues
Closes #357
Closes #347

## Attribution
The fresh & rotten tomato images were sourced from:
- https://commons.wikimedia.org/wiki/File:Rotten_Tomatoes.svg
- https://commons.wikimedia.org/wiki/File:Rotten_Tomatoes_rotten.svg
Credit: Emily Oberman from Pentagram, modified by Throast, Public
domain, via Wikimedia Commons
2025-12-01 18:04:40 -05:00
damontecres
fc330a3400
Improve search keyboard dismissal (#317)
I need to test this more

Fixes the imeAction for search

Initiating the search will move focus down if results are available

Pressing back while focused on the search text box will move focus to
either the results or the nav drawer. I think will will help devices
that do not dismiss on the soft keyboard on back.

Might fix #311
2025-12-01 10:44:39 -05:00
Damontecres
d0bfdac3bd
Fix version name from #350 2025-11-30 22:24:34 -05:00
damontecres
258c50a95e
Add support for Android TV 6 & 7.0 (#350)
Adds support for Android TV 6.0 & 7.0 by reduce the min SDK to 23. Going
below 23 isn't practical since new releases of Jetpack libs require 23+.

Also includes some clean up for better gradle syncs & actually use the
datetime APIs available from desugaring on older versions.

Needs more testing before merge

Closes #341
2025-11-30 20:26:42 -05:00
damontecres
eca3268a7d
Add setting to increase logging verbosity (#354)
Adds a toggle to increase the verbosity of the app logs for
release/production builds

Most users won't need to use this, but it can be helpful when [gathering
app
logs](https://github.com/damontecres/Wholphin/wiki/Gathering-app-logs)
to report an issue.
2025-11-30 17:27:41 -05:00
Damontecres
a320875af6
Release v0.3.3
Some checks failed
Create release / publish (push) Has been cancelled
2025-11-30 09:35:35 -05:00
Damontecres
ef20dad993
Fix settings changes needing a restart to apply 2025-11-29 19:39:23 -05:00
damontecres
8a2481362d
Fix live tv being unplayable (#346)
Fixes issue with playing live TV

Fixes #344
2025-11-28 23:58:05 -05:00
damontecres
f4c602ef3f
Fix broken scroll on home page (#345)
Fixes an edge case on the home page where having at least two rows that
don't have any latest items (such as collections or playlists) in
between two regular libraries (as configured on your home profile
settings) would prevent scrolling up from the second regular library
2025-11-28 23:38:29 -05:00
damontecres
ff91fa87e2
Few minor UI fixes (#343)
- Keep the currently selected nav drawer item in view and focus on when
entering
- Adjust star rating height so that the row is consistent preventing
resizing when moving between episodes with & without a rating
- Simplify checks when switching to a new server and use more specific
keys to prevent needing to switch servers twice
2025-11-28 21:31:02 -05:00
damontecres
13a3c6c831
Merge pull request #339 from damontecres/translations-2025-11-28
Update translations
2025-11-28 17:17:39 -05:00
arcker95
117d67240a
Translated using Weblate (Italian)
Currently translated at 100.0% (243 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-11-28 20:57:15 +00:00
Vistaus
dcfe0086c1
Translated using Weblate (Dutch)
Currently translated at 100.0% (243 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nl/
2025-11-28 20:57:15 +00:00
streamyfinchris
ffa423f4f2
Translated using Weblate (Swedish)
Currently translated at 85.1% (207 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2025-11-28 20:57:15 +00:00
damontecres
90db5820ba
Translated using Weblate (Spanish)
Currently translated at 100.0% (243 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2025-11-28 20:57:15 +00:00
rcv11x
5ad66a1783
Translated using Weblate (Spanish)
Currently translated at 100.0% (243 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2025-11-28 20:57:15 +00:00
damontecres
f1bcc57726
Translated using Weblate (Spanish)
Currently translated at 100.0% (243 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2025-11-28 20:57:15 +00:00
Vistaus
0bef9045d4
Added translation using Weblate (Dutch) 2025-11-28 20:57:15 +00:00
arcker95
dd198de152
Translated using Weblate (Italian)
Currently translated at 100.0% (243 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-11-28 20:57:15 +00:00
arcker95
0e4f214f7a
Translated using Weblate (Italian)
Currently translated at 100.0% (243 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-11-28 20:57:15 +00:00
SimonHung
e59cc30db0
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 99.1% (241 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2025-11-28 20:57:15 +00:00
RabSsS
aa67a15f29
Translated using Weblate (French)
Currently translated at 100.0% (243 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2025-11-28 20:57:15 +00:00
alexq83
0b104a4e08
Translated using Weblate (German)
Currently translated at 99.5% (242 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2025-11-28 20:57:15 +00:00
SimonHung
c976df2359
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 98.3% (239 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2025-11-28 20:57:15 +00:00
RabSsS
630321b7ab
Translated using Weblate (French)
Currently translated at 99.5% (242 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2025-11-28 20:57:15 +00:00
Priit Jõerüüt
0036e83e0d
Translated using Weblate (Estonian)
Currently translated at 100.0% (243 of 243 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-11-28 20:57:15 +00:00
arcker95
3165c58278
Translated using Weblate (Italian)
Currently translated at 61.8% (149 of 241 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-11-28 20:57:15 +00:00
RabSsS
ef9cb65d37
Translated using Weblate (French)
Currently translated at 99.5% (240 of 241 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2025-11-28 20:57:15 +00:00
Priit Jõerüüt
b9cf7dd679
Translated using Weblate (Estonian)
Currently translated at 73.0% (176 of 241 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-11-28 20:57:15 +00:00
arcker95
907df16112
Translated using Weblate (Italian)
Currently translated at 46.4% (112 of 241 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/it/
2025-11-28 20:57:15 +00:00
arcker95
bb61162752
Added translation using Weblate (Italian) 2025-11-28 20:57:14 +00:00
SimonHung
5dcf5e867c
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 88.7% (214 of 241 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2025-11-28 20:57:14 +00:00
junyou
b0f17a5062
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 88.7% (214 of 241 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2025-11-28 20:57:14 +00:00
junyou
24c11aa9ab
Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 88.7% (214 of 241 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/zh_Hant/
2025-11-28 20:57:14 +00:00
opakholis
e96f17d3c6
Translated using Weblate (Indonesian)
Currently translated at 100.0% (241 of 241 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2025-11-28 20:57:14 +00:00
junyou
70a085eef2
Added translation using Weblate (Chinese (Traditional Han script)) 2025-11-28 20:57:14 +00:00
RabSsS
6b83a93f4d
Translated using Weblate (French)
Currently translated at 57.2% (138 of 241 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2025-11-28 20:57:14 +00:00
Codeberg Translate
1c7206a1ff
Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/
2025-11-28 20:57:14 +00:00
RabSsS
1f81157454
Translated using Weblate (French)
Currently translated at 7.0% (17 of 241 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/fr/
2025-11-28 20:57:14 +00:00
RabSsS
78d626ea88
Added translation using Weblate (French) 2025-11-28 20:57:14 +00:00
alexq83
cea0412dd5
Translated using Weblate (German)
Currently translated at 100.0% (230 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2025-11-28 20:57:14 +00:00
Priit Jõerüüt
8dda285005
Translated using Weblate (Estonian)
Currently translated at 62.6% (144 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-11-28 20:57:14 +00:00
Sk4hnt42
8f15533218
Translated using Weblate (German)
Currently translated at 99.5% (229 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2025-11-28 20:57:14 +00:00
streamyfinchris
b8646df0c3
Translated using Weblate (Swedish)
Currently translated at 86.9% (200 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2025-11-28 20:57:14 +00:00
damontecres
296d7b9226
Setting for subtitle margin & fix subtitle opacity settings (#338)
Adds a subtitle style option to set the margin below unstyled subtitles.
This is roughly the percent of the screen below the subtitles. It
applies to both ExoPlayer & MPV.

Also fixes font & background opacity not being applied in some cases.

Fixes #225
Closes #178
2025-11-28 15:57:11 -05:00
damontecres
7f50cd83ee
Add default sort option for collections (#337)
Adds a "default" sort option for collections/boxsets. This makes the app
request no sort at all and leaves it up to the server to order the
items.

Fixes #83
2025-11-28 15:33:03 -05:00
damontecres
8bc6fce0cb
Show time on playback overlay (#336)
If enable, show the current time in the upper right corner of the
playback overlay/controls

Closes #219
2025-11-28 13:03:41 -05:00
damontecres
babfe011a1
Filter out invalid TV guide programs (#334)
Filters out TV programs that end before the guide start time. In theory,
the server shouldn't return these, but it seems to for some users.

Related to #276
2025-11-28 10:36:12 -05:00
damontecres
10c08c5515
Display episode even if season metadata is missing (#329)
Adds a simple fallback page to show an episode in cases when it doesn't
have an associated season due to missing metadata,see #280.

The missing metadata is probably a server bug, but it's good to have a
fallback anyway.
2025-11-28 09:41:09 -05:00
Damontecres
70f569208c
Release v0.3.2
Some checks failed
Create release / publish (push) Has been cancelled
2025-11-25 16:43:22 -05:00
damontecres
c0237a6123
Few UI tweaks (#325)
- Move loading indicator to center of grid when changing filters so it's
more obvious
- Move 'remove' filter to top for the first filter dropdown for
consistency
- Use theme color for download progress indicator
- Show playback controls at the end of playing a video if there's
nothing up next
2025-11-25 16:36:33 -05:00
damontecres
0bfe3d2653
Move stream labels to top of description (#323)
Follow up to #279
Closes #162

Move the stream labels to the top under the dot-separated details. Also
move the rating up to the dot-separated details line and add it to home
page.
2025-11-25 15:51:30 -05:00
damontecres
5b96be6662
Updates to details pages including videos' stream details display (#279)
## Details

This PR updates the detail pages for Movies & TV Shows.

- Increase backdrop image size & brightness (now matches home page)
- Increase max width for titles
- Decrease max width for detail text
- Switch to using a customized display labels for video, audio, &
subtitle streams

Additional change for movie pages:

- Don't dim movie details text
- Move video stream details below play buttons
- Move first row up higher

### Labels

Instead of using the server provided `DisplayTitle`, create display
labels in-app. In general, this shortens the text without losing
significant detail. It is similar to the official app.

Some of the changes:
- Don't show "SDR", only "HDR"
- Simplify audio channel info, eg "Surround 5.1" => "5.1"
- Show chosen subtitle language
- Show chosen subtitle codec
- Show "(+#)" if there are additional audio or subtitle tracks
- Use gray background slightly tinted by the theme color

## Issues
Closes #239
2025-11-24 15:35:55 -05:00
damontecres
7be4fb5fdf
Fix possible TV guide crash & update Play All libraries (#315)
- Fixes a possible crash if the server has Live TV enabled, but no
channels
- Disable Play All/Shuffle for Collections "library"
2025-11-24 11:40:15 -05:00
damontecres
6004687deb
Fix black theme playback button highlighting (#314)
On the playback buttons, inverts the icon color when highlighted for
contrast.

Closes #295

<img width="518" height="109" alt="image"
src="https://github.com/user-attachments/assets/d689f347-a395-40e4-89e2-3122ab2a9e13"
/>
2025-11-24 10:01:19 -05:00
damontecres
7e21033f58
Adjust theme colors (#302)
Adjusts some theme colors for various controls

Fixes #295
2025-11-23 19:50:28 -05:00
damontecres
5fbe12e6cf
Fix crash when go backing too quickly (#309)
Fixes #306 

Also fixes network-on-main and potential concurrency issues
2025-11-23 17:19:01 -05:00
damontecres
78e7eb421f
Add ability to filter libraries, collection, etc (#308)
Adds a button to filter contents next to the sort button on grid pages

The filters are saved and restored you return to the page. It works with
#278, playing the filtered items in the sorted order.

The drop down menus for both sorting & filtering are updated to closer
match the overall look.

Filters for:
- Played or unplayed
- Favorite or not
- Genres
- Community (star) rating
- Parental rating
- Video type (HD vs SD, etc)
- Release year and/or decade
2025-11-23 10:40:18 -05:00
damontecres
50dc4b9a35
Play all or shuffle libraries, shows, collections, etc (#278)
Adds ability to play all (or shuffle play) most libraries, collections,
folders, TV series, TV seasons, etc

This adds buttons to play all or shuffle play at the top of grid pages
that support. Additionally, a shuffle button is added to TV Series
details. And play all or shuffle play is added when long clicking TV
seasons.

Note: Until #42 is completed, only the first 100 items will play
back-to-back

Closes #197
2025-11-22 18:40:08 -05:00
damontecres
9a3a33fdd3
Use full phases for skip buttons for better translations (#304)
Changes the skip segment button text to use a single phrase instead of
concatenating two strings. This allows correct context when translating.

Follow up from #292
2025-11-22 18:39:58 -05:00
damontecres
6029db8021
Update README 2025-11-22 16:14:46 -05:00
Damontecres
eea650bb39
Fix debug page crash on Android <11 2025-11-22 14:07:52 -05:00
damontecres
78e6304571
Prep work for play store distribution (#301)
Allows for disabling in-app updates and more flexible tag/version codes
2025-11-22 12:57:47 -05:00
damontecres
df78b42bac
Easier initial setup for server URLs (#300)
Use the server scoring API when adding a new server. This allows just
entering a hostname or IP address without the scheme (`http`/`https`) or
port in most cases.

Note: non-standard ports would still need to be provided

Closes #5 
Closes #296
2025-11-22 12:48:39 -05:00
Damontecres
df8ec83841
Merge remote-tracking branch 'weblate/main' 2025-11-22 11:10:22 -05:00
damontecres
20ab146c97
Update device profile for Jellyfin 10.11 (#293)
## Details

This PR updates the device profile used by ExoPlayer. It should more
accurately report support for HDR formats.

## Acknowledgements
This PR contains a commit (cd6351912af72d274b0baf50af4d777b986f0f46)
that has code copied and slightly modified from
https://github.com/jellyfin/jellyfin-androidtv `v0.19.4`.

Major acknowledgement & credit to the Jellyfin Android TV developers for
this code!

## Issues
Related to #15
2025-11-21 20:16:49 -05:00
Damontecres
ec852906b1
Release v0.3.1
Some checks failed
Create release / publish (push) Has been cancelled
2025-11-21 17:20:22 -05:00
damontecres
c600d2eb28
Few dev related fixes (#291) 2025-11-21 16:05:27 -05:00
damontecres
72970b9a4b
Update release workflows (#290)
Use `gh` CLI instead of third-party actions
2025-11-21 15:26:25 -05:00
damontecres
a3305b6b57
First batch of translations (#289)
Adding several translations contributed by users!

Thanks!

---------

Co-authored-by: damontecres <damontecres@noreply.codeberg.org>
Co-authored-by: streamyfinchris <streamyfinchris@noreply.codeberg.org>
Co-authored-by: Priit Jõerüüt <jrtcdbrg@noreply.codeberg.org>
Co-authored-by: alexq83 <alexq83@noreply.codeberg.org>
Co-authored-by: n8llcaster <n8llcaster@noreply.codeberg.org>
Co-authored-by: opakholis <opakholis@noreply.codeberg.org>
Co-authored-by: texxlan <texxlan@noreply.codeberg.org>
Co-authored-by: Gigaa <gigaa@noreply.codeberg.org>
2025-11-21 15:25:02 -05:00
damontecres
815ce71735
Translated using Weblate (German)
Currently translated at 100.0% (230 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2025-11-21 20:18:49 +00:00
Sk4hnt42
364a74c791
Translated using Weblate (German)
Currently translated at 100.0% (230 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2025-11-21 20:18:49 +00:00
Gigaa
7250007bc4
Translated using Weblate (Norwegian Bokmål)
Currently translated at 2.1% (5 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/nb_NO/
2025-11-21 20:18:49 +00:00
Gigaa
1834b06edb
Added translation using Weblate (Norwegian Bokmål) 2025-11-21 20:18:49 +00:00
texxlan
df930b8548
Translated using Weblate (German)
Currently translated at 99.5% (229 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2025-11-21 20:18:49 +00:00
texxlan
ef7d0ad07c
Translated using Weblate (German)
Currently translated at 76.0% (175 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2025-11-21 20:18:49 +00:00
opakholis
4f8fcc4d9b
Translated using Weblate (Indonesian)
Currently translated at 100.0% (230 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2025-11-21 20:18:49 +00:00
Priit Jõerüüt
7395e56818
Translated using Weblate (Estonian)
Currently translated at 63.0% (145 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-11-21 20:18:49 +00:00
opakholis
6ba5c675a5
Translated using Weblate (Indonesian)
Currently translated at 83.9% (193 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/id/
2025-11-21 20:18:49 +00:00
Priit Jõerüüt
fac28fa60d
Translated using Weblate (Estonian)
Currently translated at 26.5% (61 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-11-21 20:18:49 +00:00
alexq83
d34a05b2dc
Translated using Weblate (German)
Currently translated at 30.4% (70 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2025-11-21 20:18:49 +00:00
opakholis
b9d206379b
Added translation using Weblate (Indonesian) 2025-11-21 20:18:49 +00:00
n8llcaster
a53f980409
Translated using Weblate (Slovak)
Currently translated at 45.6% (105 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sk/
2025-11-21 20:18:49 +00:00
alexq83
62bab657cd
Translated using Weblate (German)
Currently translated at 26.5% (61 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2025-11-21 20:18:49 +00:00
n8llcaster
f7c6cd6c49
Added translation using Weblate (Slovak) 2025-11-21 20:18:49 +00:00
alexq83
3c407ff88c
Translated using Weblate (German)
Currently translated at 12.1% (28 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/de/
2025-11-21 20:18:49 +00:00
Priit Jõerüüt
d024bef122
Translated using Weblate (Estonian)
Currently translated at 5.2% (12 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/et/
2025-11-21 20:18:49 +00:00
Priit Jõerüüt
6854e4fd98
Added translation using Weblate (Estonian) 2025-11-21 20:18:49 +00:00
damontecres
209127ce2c
Translated using Weblate (Spanish)
Currently translated at 39.5% (91 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2025-11-21 20:18:49 +00:00
streamyfinchris
9a7706fd8e
Translated using Weblate (Swedish)
Currently translated at 87.3% (201 of 230 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/sv/
2025-11-21 20:18:49 +00:00
streamyfinchris
83df14e02f
Added translation using Weblate (Swedish) 2025-11-21 20:18:49 +00:00
damontecres
4bfa11de52
Translated using Weblate (Spanish)
Currently translated at 1.7% (3 of 168 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2025-11-21 20:18:49 +00:00
damontecres
b6ae85c29e
Translated using Weblate (Spanish)
Currently translated at 1.7% (3 of 168 strings)

Translation: Wholphin/Wholphin
Translate-URL: https://translate.codeberg.org/projects/wholphin/wholphin/es/
2025-11-21 20:18:49 +00:00
damontecres
e0ecfc3c6d
Added translation using Weblate (Portuguese) 2025-11-21 20:18:48 +00:00
damontecres
3ca964c09d
Added translation using Weblate (German) 2025-11-21 20:18:48 +00:00
damontecres
c627239b2a
Added translation using Weblate (Spanish) 2025-11-21 20:18:48 +00:00
damontecres
46094a5202
Deleted translation using Weblate (Spanish) 2025-11-21 20:18:48 +00:00
damontecres
9a54d6ed66
Added translation using Weblate (Spanish) 2025-11-21 20:18:48 +00:00
damontecres
4bd202e013
Add two new themes (#266)
Adds "Bold Blue" and Black/OLED themes

Also tweaks Orange & Green a bit to make the borders stronger and
backgrounds darker/less colored

Related to #91
2025-11-21 15:18:43 -05:00
Damontecres
6d338bde59
Add translation info to readme 2025-11-21 14:05:59 -05:00
damontecres
ccc900cee7
Don't query for Live TV info if user does not have access (#286)
Fixes #281
2025-11-21 13:15:52 -05:00
damontecres
99e8b69c0e
Fix nav drawer focus when pressing back on home page (#274)
Fixes the nav drawer not being focused on when pressing back to open it
on the home page

Dev notes: I think the bug was introduced in #228 due to the new
`Column` not being focusable, but we don't need it to be since we want
to focus on the selected page.
2025-11-20 18:04:45 -05:00
damontecres
45567bf4eb
Variety of UI changes for grids, home page order, & tv guide (#272)
## Details
This PR ended up being larger than I meant. It contains some UI changes
and code organization.

## TV & DVR
- Uses the "Recordings" library position to order "Recently added in
Recordings" instead of Live TV position
- The times at the top of the TV guide are more distinguishable from
each other
- Removes the duplicate title on the Live TV->Recordings tab

## Other libraries
- Bare-bone support for "Music Video" libraries. Full support for "Music
Video" is work-in-progress in #267, but the libraries should be
browseable now.
- Bare-bone support for "Other" (aka Mixed Movies & TV Shows) libraries.
Note: this type of library is considered [broken and
deprecated](https://jellyfin.org/docs/general/server/media/mixed-movies-and-shows),
so this won't be expanded any further


## Grids
Updates various grid types to use poster vs wide cards. For example,
Favorite TV Episodes will use episode images in a wide card now.

## Issues
Closes #249 
Closes #271
2025-11-20 17:00:11 -05:00
damontecres
39ad7e564f
Bump dependency versions & update issue templates (#270)
Adds a new bug template for reporting playback specific issues


Also bumps a few dependency versions
2025-11-20 12:55:49 -05:00
damontecres
1f1d3443e3
Handle more remote & controller buttons during playback (#269)
## Details
Adds support for a few more media remote buttons

Adds support for common controller buttons

### Dev notes
Lifts the playback dialogs to the top level allowing them to be called
from anywhere during playback

## Issues
Closes #260
2025-11-20 12:29:17 -05:00
damontecres
d3a3a2093a
Fix a couple issues with transcoding (#261)
If playing with transcoding, changing tracks during playback will
continue to transcode instead of possibly switching to direct play.

Also support embedded subtitles that are being delivered externally.


Fixes #256
Fixes #257
2025-11-20 09:15:47 -05:00
damontecres
77755d04ba
Update readme 2025-11-19 18:04:20 -05:00
damontecres
59fc88f395
Updates to TV guide UI (#263)
Reduces the size of the rows on the TV guide allowing for 7-8 rows to
show. Also hide the tab row if scrolled down on the guide.

Move the "watch live" button to be initially focused

Channels are sorted with favorites appearing first

Back button behavior is tweaked to move to start of row first, then top
of the guide, then normal back behavior

Closes #259 
Related to #250
2025-11-19 17:06:00 -05:00
damontecres
461fbe846c
Add extra debug info to overlay (#258)
Adds some extra info to the playback debug info overlay such as
ExoPlayer decoder names and transcode info
2025-11-19 14:58:57 -05:00
damontecres
4517dcd0cf
Simplify PlaybackViewModel (#252)
`PlaybackViewModel` was becoming very unwieldy at over 1,300 LOC. This
PR breaks it up and simplifies some fields so it's easier to work with.

There are no user facing changes
2025-11-18 19:18:38 -05:00
damontecres
1fd497d18c
Add playlist & collection specific grid layout UIs (#246)
Adds grid layouts specifically for playlists and collections/boxsets
instead of falling back to the generic grid.

In #238, the generic grid was updated to show 4 columns of 16x9 cards.
Now, collections will use 2x3 (poster) cards (like Movies) and playlists
will use square cards.

This also means collections & playlists have more relevant sort options.

Fixes #243
Closes #226
2025-11-18 17:20:28 -05:00
damontecres
cda5b118a3
Option to combine continue watching & next up rows (#192)
A real implementation for combining the `Continue Watching` and `Next
Up` rows on the home page.

It requires a query for every next up item, so it could become slow
especially for many items and larger servers. The app makes a best
effort to cache the information.

The order is determined by querying each episode in next up to associate
it to its immediately previous episode's last played date. Then both
continue watching & next up items are reversed sorted by the last played
data. So this only makes sense if you watch series in order.

Closes #19
2025-11-18 15:21:50 -05:00
damontecres
46415fcdd0
Fix release workflow 2025-11-18 12:56:59 -05:00
damontecres
a133475b00
Update screenshots in README 2025-11-18 12:56:24 -05:00
Damontecres
afe638a97c
Release v0.3.0
Some checks failed
Create release / publish (push) Has been cancelled
2025-11-18 12:43:17 -05:00
Damontecres
2111699abc
Smaller maximum size for logo 2025-11-18 11:48:04 -05:00
damontecres
90956bd0e2
Fix several playback issues (#240)
Fixes issues introduced in #222:
- Switching tracks
- Downloading subtitles
- Trickplay
2025-11-17 22:08:26 -05:00
Damontecres
037e846599
Revert "Use useSeriesForPrimary"
This reverts commit f5f75f9e9e8f3316c689b85d3b8781c932a7c0ff.
2025-11-17 16:12:59 -05:00
damontecres
a9f2e684f1
Add logo to playback overlay (#172)
## Details

Adds the logo (if available) to the upper left corner of the playback
controls overlay.

I am still experimenting with how logos can be used on the details
pages. It's problematic because of the variety of colors, eg dark logos
can be hard to see on the dark background.

## Issues
Related to #92

## Screenshot
<img width="1280" height="771" alt="logo_playback Large"
src="https://github.com/user-attachments/assets/c6cb43fe-f3bc-42a4-9ef4-0397267e3073"
/>
2025-11-17 14:14:35 -05:00
Damontecres
0d148e2556
Move strings into one file 2025-11-17 14:02:17 -05:00
damontecres
98fde1b65e
Better video grid, poster images, & home page images (#238)
- General video library grid now shows 16:9 cards
- Ensures the posters on Movie/TV show grid pages fill the bounds (TVDB
posters are often very slightly wider than 2x3)
- Always crossfade home page backdrop image

Related to #66
2025-11-17 13:44:03 -05:00
damontecres
4d34c977c8
Fix issue when holding down on playback overylay queue (#237)
Fixes an issues where if the playback overlay is showing and there is a
queue of items, then holding the D-Pad down button would scroll down but
visually lose focus on the queue items.
2025-11-16 21:24:07 -05:00
damontecres
b42aa297f6
Add dimming effect to highlight which episode is currenty selected (#232)
On the series overview page, when moving off of the episode row, such as
to do the play/more buttons, the other episodes will now become dimmer
to indicate which episode you're interacting with.

Also, when moving back to the page from the nav drawer, focus is
restored to where it left from. This is useful when scrolling fast left
on the season tabs and you overshoot onto the nav drawer, moving right
once will always move back to the season tabs.

In this example screenshot, Episode 3 is selected, so the others are
darker.
<img width="1280" height="771" alt="dim Large"
src="https://github.com/user-attachments/assets/fa21ade4-3345-4e97-873f-d654123a1ae5"
/>

This is a follow up from #158
2025-11-16 21:23:55 -05:00
damontecres
e3d94ad819
Increase JVM memory for gradle builds (#236)
Updates to increase amount of memory available during CI builds. I think
this will help with the intermittent `A failure occurred while executing
"com.android.build.gradle.tasks.PackageAndroidArtifact$IncrementalSplitterRunnable`
CI errors.

No user facing changes
2025-11-16 19:44:39 -05:00
damontecres
6be2662d4e
Add experimental MPV player backend (#161)
Experimental MPV player backend

Related to #14, #22, & #85

You can switch to MPV in advanced settings and toggle using hardware
decoding or not.

This uses code and buildscripts from
https://github.com/mpv-android/mpv-android, plus other third party
libraries to build MPV
2025-11-16 18:46:25 -05:00
damontecres
d7b333e519
Adjust color & size of the nav drawer (#228)
The navigation drawer now appear more subtle when closed using a neutral
background and dimmer, slightly more compact icons. When open, it looks
much like it did before.

The profile button stays fixed at the top since it is the only
indication for which user & server.

Closes #176

Closed
<img width="1280" height="771" alt="drawer_closed Large"
src="https://github.com/user-attachments/assets/54c154eb-fefe-4029-9af8-a57473f3535e"
/>

Open
<img width="1280" height="771" alt="drawer_open Large"
src="https://github.com/user-attachments/assets/1e0f95a7-e98f-4e96-988a-8b1d97bdd757"
/>
2025-11-15 15:42:26 -05:00
damontecres
93d82a263d
Show extras for Movies & TV Shows (#224)
## Description
Shows local extras (clips, interviews, etc) for movies and tv shows

Extras are grouped by type and clicking on the type will go to a grid
page listing all of the items. However, if there is just one of that
type, it is shown in-line.

Trailers still have on their own row. And theme songs & theme videos are
not shown.

## Issues
Closes #123
2025-11-15 13:25:44 -05:00
damontecres
2f56a3a114
Add a few more sort options (#223)
Adds parental rating, runtime, & play count sorting options

Closes #198
2025-11-14 19:48:47 -05:00
damontecres
72fb732e63
Fix & refactor some image aspect ratios (#222)
Fix image aspect ratios for episode cards that differ from the video's
aspect ratio.

Also use the video's aspect ratio for chapter cards instead of assuming
16x9.

Dev: Create constants for the main ratios used

Fixes #215
2025-11-14 16:13:18 -05:00
Damontecres
6a912c0af3
Release v0.2.9
Some checks failed
Create release / publish (push) Has been cancelled
2025-11-14 14:06:52 -05:00
Damontecres
00521578d5
Another fix for server/user list 2025-11-14 14:01:20 -05:00
Damontecres
b7dfb2ca5f
Always initialize server/user view models 2025-11-14 13:33:33 -05:00
damontecres
c5a5345f6c
Fix focusing before loading on recommended pages (#220)
Basically now will wait until a row is loaded before sending the focus
request
2025-11-14 11:40:38 -05:00
damontecres
b7cb6efc4f
Dev: Move services into their own package (#214)
Moves service classes into their own files and package for easier
organization and discovery

No user facing changes in the PR
2025-11-13 19:39:37 -05:00
damontecres
76b7a33328
Fix two issues on recommended pages (#213)
Fix an issue where if there are no items for the "Continue Watching"
row, the tab row would disappear

Fix an issue where some rows (eg Recently released, recently added,
suggestions, etc) on the recommended pages might never show up

Fixes #207
2025-11-13 18:40:52 -05:00
damontecres
fe29b0cd88
Show local & remote trailers for TV Shows (#212)
Shows local & remote trailers for TV Shows similar to how its shown for
Movies

Closes #141
2025-11-13 18:31:23 -05:00
Jakub Syty
f61e819a98
Add FLAG_ACTIVITY_NEW_TASK to fix updates for some older devices (#211)
Without this the installation intent is crashing with an error on my
Xiaomi Mi Box S (Android 8.1)
2025-11-13 17:27:20 -05:00
damontecres
19b712d54b
More favorite tabs & add people to favorites (#210)
Adds Video, Playlists, & People tabs to the favorites page

Also adds ability to add people to favorites via context menu or on
their page

Closes #199
2025-11-13 16:12:42 -05:00
Damontecres
655720fc05
Small fix for deleting current server or user 2025-11-13 11:52:52 -05:00
damontecres
5061277fd1
Keep activity tracking alive even after errors (#209)
Previously if the activity tracker got an error from the server during
one of the reports, the timer thread would stop and no further reports
would be sent.

This PR fixes that so it will continue to attempt to send reports since
if playback is continuing without error, the report error is likely
transient. For example, I observed the server returning an error during
its DB optimization scheduled task.

I think this will fix #208
2025-11-13 11:36:57 -05:00
damontecres
58395e9adf
Refactoring switching server or user (#205)
Now when initiating a user or server switch, you can't back out of it
and must select a user/server. This will be useful when PIN code are
implemented down the road.

The app will now disconnect the web socket when backgrounded.

Also lots of internal refactoring
2025-11-12 17:44:16 -05:00
damontecres
2e3e4e3f5f
Add long clicking context menus everywhere (#202)
Adds long click context menus to just about every card throughout the
app. Previously only a few pages had this.

Closes #196
2025-11-12 15:36:36 -05:00
damontecres
5e2c75bb70
Log while navigating around the app (#195)
Just adds some logging while navigating around the app. If an error or
crash occurs and users want to share their logs, this information is
useful to know what page.

Logs are never sent without explicit user content and they are only
shared with the Jellyfin server the app is currently connected to. [See
here for more
details](https://github.com/damontecres/Wholphin/wiki/Gathering-app-logs#privacy)

Also stops theme music when the app is paused similar to #191
2025-11-11 20:10:16 -05:00
Damontecres
ed2ec2f97e
Release v0.2.8
Some checks failed
Create release / publish (push) Has been cancelled
2025-11-11 15:57:35 -05:00
Damontecres
5b1c46f0b7
Fix focus on recommended 2025-11-11 15:45:22 -05:00
damontecres
f8a2bb1545
Fix showing home page refreshes (#194)
Fixes the UI not showing that a refresh is in progress
2025-11-11 13:32:01 -05:00
Damontecres
d6c9b236ef
Fix reversed logic 2025-11-10 18:45:31 -05:00
damontecres
fbf77b9376
Better playback lifecycle handling (#191)
Refactors lifecycle event observers

When the app is "paused", aka not actively focused, but still visible
(such as when opening Android TV OS menu), playback will pause.

When the app is "stopped", aka not visible (such as pressing Home
button), playback will stop, report playback progress, and return to the
previous screen.

I think this will fix #181, but I don't have a controller to test with
2025-11-10 17:55:58 -05:00
damontecres
ce8cab0d1f
Home page improvements (#190)
Separates the loading logic into two groups:
1. Continue Watching & Next Up
2. Latest media

The home page displays once the first group is ready and shows
placeholders while the latest media is fetched. This also speeds up
refreshes when returning home after watching an episode.

Also ensure grouped Movie/TV libraries are displayed as Movie/TV
libraries instead of falling back to a generic grid display.

Fixes #189
2025-11-10 13:18:36 -05:00
damontecres
98fc996bd5
Load movie & tv recommendations asynchronously (#183)
Previously all of the rows on the recommended tab for Movies & TV Shows
were loaded sequentially before any results were shown. This is not a
great experience because some of the queries (eg top rated unwatched)
can take a while sometimes.

This PR starts displaying content once the first 1 or 2 rows is loaded
and then shows placeholders while the rest are querying.

Finally the row queries are now mostly independent so if one errors out,
the rest of the rows will still be displayed.

Closes #180
2025-11-09 17:40:47 -05:00
damontecres
0fb1a69556
Fix only loading 100 episode in a season (#187)
Fixes #184

Data was being fetched, but the UI wasn't being properly notified to
display the data.

This PR also adjusts so that "focused episode header" still takes up the
space even if the focused episode hasn't fully loaded yet. This prevents
the UI from jumping up and down.

Also #42 is _not_ fixed by this PR because it is a different issue.
2025-11-09 17:40:13 -05:00
damontecres
5720603447
Add ability to style subtitles (#177)
Adds settings for styling text subtitles such as srt/subrip

## Issues
Closes #11
2025-11-08 19:54:51 -05:00
damontecres
762e8f287d
When scrubbing show position's time even without preview images (#175)
When scrubbing along the seek bar, always show the position's time even
if there is no trickplay/preview image to display.
2025-11-08 09:34:12 -05:00
Damontecres
7bdf7952c9
Release v0.2.7
Some checks failed
Create release / publish (push) Has been cancelled
2025-11-07 17:50:07 -05:00
Damontecres
76a46bfeb7
Fix subtitle search crash 2025-11-07 17:49:24 -05:00
damontecres
44825d0c25
Several UI changes (#171)
## Changes:
- Hopefully finally fixes next up card image issues
- Remove the slider wraparound behavior in settings
- Removes the shadows/blurry effect on movie & person titles
- Adds the official rating to the home page
- Consistently order the official rating on home, movie, & series pages
- Add 1.75x speed for playback
- Don't time out the audio track selection/playback speed/video scale
dialogs


## Issues
Closes #166 
Closes #168
Closes #169
Related to #162
Follow up to #167
2025-11-07 10:51:31 -05:00
damontecres
18556b5058
Bump dependency versions (#170)
Also tries out using assisted injection for item IDs

No user facing changes
2025-11-07 10:09:59 -05:00
damontecres
bdc475e7b6
Fix next up image showing very small (#167)
Fixes #163 

Should fix the next up images rendering very small. Also tweaks some
padding and background for the popup.
2025-11-06 18:58:35 -05:00
damontecres
5d085a3ab4
Show update download progress (#165)
Shows a simple dialog while the app update is downloading to show
progress
2025-11-06 18:15:25 -05:00
Damontecres
fc35bafceb
Release v0.2.6
Some checks failed
Create release / publish (push) Has been cancelled
2025-11-06 16:46:01 -05:00
Damontecres
0d48fd67cf
Fix clock not updating 2025-11-06 08:41:05 -05:00
damontecres
b6df72c3c9
Optional setting for one click pause w/ d-pad center (#160)
Closes #152 

Adds a setting to enable one-click-pausing via the D-Pad center button.
It is disabled by default.

If enabled, when the video is playing, it will be pause and show the
controls which is just like pressing the Pause or Play/Pause button.

If enable, when the video is paused and controls is not showing, it will
resume playback including using the skip back on resume setting.
2025-11-05 16:35:20 -05:00
damontecres
f12785aaee
Various UI fixes & improvements (#159)
Various fixes:
* Fixes misnamed "Continue watching" home row title
* Fix person page layout to account for the new clock
* Switch the 5 star rating to a decimal & icon

Additionally, the tab row has been rewritten to allow for: 
* Always left justify tab rows
* Always scroll tab rows to ensure the currently selected index is
showing
* Always start focus on currently selected index when entering tab rows
2025-11-05 12:08:45 -05:00
damontecres
77c787b51f
Add settings to show the current time (#155)
Closes #154 

Adds a settings to show the current time in the upper right corner.

If enabled, it will also show the end time for the playback on the
overlay.
2025-11-04 15:27:38 -05:00
damontecres
40a8249469
Support basic adding to playlists (#157)
Closes #143 

Adds option in the "More" menu to add the item to a playlist. Can also
create a playlist if needed.
2025-11-04 15:27:26 -05:00
damontecres
86977f846c
Handle larger TV guide data (#132)
Fixes #110 

Switches loading the EPG/TV Guide data to lazy loading. The app will
load all of the channels, but only keep ~100 channels worth of data in
memory. As the user scrolls up and down, new channel program data will
be fetched as needed.

Eventually this technique can be applied to supporting more than 48
hours of data.

The logic for determine which program to focus on is also simplified and
more of it is delegated to composition.

Credit to @joshjryan for #131 hinting to use a different API endpoint to
avoid 414 errors.
2025-11-04 13:33:32 -05:00
damontecres
13c2bb32d2
Fix favorites not loading (#156)
Fixes bug introduced in #150 that prevented the favorites page from
loading anything

Also remembers the sort for each favorites tab
2025-11-04 13:33:03 -05:00
damontecres
a0b9ac7673
Add option to play item forcing transcoding (#153)
Closes #97 

Adds an option to force an item to play by transcoding. This can be
useful in the few cases where the device reports that it supports direct
play for a file, but it really doesn't.

Also rearranges the "More" dialog list to move picking audio & subtitle
track near the top.
2025-11-03 20:34:46 -05:00
damontecres
2840fa3f2a
Extract strings into resources (#151)
Move all strings into resource files to make future translating easier

No user facing changes in this PR
2025-11-03 15:38:12 -05:00
damontecres
40267be633
Remember library/collection sorting (#150)
Closes #148 
Closes #83

Remember the chosen sort order & direction per user profile on device.
Also adds a few more sort options.

This is only saved on the device and not saved/loaded from the server.
2025-11-03 12:15:41 -05:00
damontecres
08577b8a31
Play theme song for movies (#149)
Closes #133 
Fixes #142 

Theme songs will play for both TV shows and movies, if available. Also,
if there are multiple theme songs, one will be picked randomly instead
of always playing the first.

Additionally, the music now waits to start until the UI has loaded.
2025-11-03 11:50:47 -05:00
Damontecres
d658ec16f3
Release v0.2.5
Some checks failed
Create release / publish (push) Has been cancelled
2025-11-02 17:33:51 -05:00
damontecres
78322ab0a5
Fix deserialization error during login (#145)
Fixes #144 

The error was occurring when querying for server information. The
endpoint used returns information about previous installations and seems
some may be missing a field marked as required in the API. This causes
the deserialization error.

Since the query is just to get the server's name for the UI, we can use
the public server info endpoint instead and also wrap it in a try-catch
since it's not absolutely required.

Additionally, this PR adds exceptions for cases when necessary
information is not available (user id, access token, etc).
2025-11-02 17:28:18 -05:00
damontecres
fe0f96d413
Revert nav drawer on focus #127 (#140)
Temporarily reverts #127 and reopens #95

It needs more refinement.
2025-11-02 17:06:06 -05:00
damontecres
d76f829804
Option to show next up during credits/outros (#130)
Adds an option to the show the next up during the end credits (outro)
instead of at the very end of the video.

The next up pushes up from the bottom and the video scales down to fit
above.

Still a WIP though

### Sample screenshot
<img width="1280" height="771" alt="next_up_credits Large"
src="https://github.com/user-attachments/assets/9a03d0ec-325d-4efe-a28e-51270ddaaa89"
/>
2025-11-02 10:51:59 -05:00
damontecres
715dc1abda
Fix network on main during letter position lookups (#136)
Maybe fixes #135

Yet another network-on-main exception fix.
2025-11-01 19:25:19 -04:00
Damontecres
b288f0f100
Release v0.2.4
Some checks failed
Create release / publish (push) Has been cancelled
2025-11-01 09:54:01 -04:00
Damontecres
c47a34a25f
Don't generate release notes 2025-11-01 09:53:33 -04:00
damontecres
efde6deb58
Adjust audio index when downloading subtitles (#129)
If the user has picked a specific audio track, then downloads a new
external subtitle, the audio track index must be adjusted (add 1) to
account for the shift in track indices.

This is because external subtitles are always indexed first.
2025-10-31 22:20:00 -04:00
damontecres
9f3691fd1b
Optional to automatically go to new page on focus instead of clicking (#127)
Closes #95 

Adds a setting (defaults to enabled) to automatically go to a page when
the navigation drawer item is focused after a brief delay.

Basically, if you open the left nav drawer, and scroll down to "Movies"
after a brief delay (600ms), the app will switch to the Movies page
without having to click.

It can be disabled in advanced settings.
2025-10-31 15:09:13 -04:00
Damontecres
26cc092402
Enable subtitles button when no subtitle tracks 2025-10-31 14:37:30 -04:00
damontecres
e9e5aee01c
Follow up to #122 for selecting subtitles tracks (#124)
Handles selecting subtitle tracks when transcoding now too

Also fixes the adjustment if there's many external subtitles
2025-10-31 13:56:42 -04:00
damontecres
1508fa1ea3
Show file details in overview dialog (#126)
When clicking the overview for an item, the dialog now shows details
about each file/media source including video, audio, & subtitle
information.

It only shows the full file path if the user is an administrator.
2025-10-31 12:27:30 -04:00
Damontecres
ba09ac09e4
Save build cache on main 2025-10-31 12:14:12 -04:00
damontecres
799255814f
Fix selecting subtitle tracks (#122)
Fixes subtitle track selection especially when there is a mix of
embedded & external subtitles

Fixes #116
2025-10-30 22:37:28 -04:00
damontecres
c65831cb05
Fix switching audio/subtitle tracks during playback (#121)
An error would occur when switching audio or subtitle tracks during
playback, but would be ignored. This meant the tracks can't be changed.

#111 was too aggressive on switching dispatchers. The player can only be
accessed on `Main`, so this PR fixes that.
2025-10-30 19:24:53 -04:00
damontecres
982506555f
Add page to details about people (#119)
Adds a page to show details about people (actors, directors, etc) when
clicking on the person card

<img width="1280" height="771" alt="wholphin_person"
src="https://github.com/user-attachments/assets/ceba6c77-2943-4faf-a2c2-81471fb3acff"
/>
2025-10-30 19:24:36 -04:00
Damontecres
90f4ca7306
Release v0.2.3
Some checks failed
Create release / publish (push) Has been cancelled
2025-10-30 14:52:09 -04:00
damontecres
4945bef008
Cache ffmpeg build (#115)
Speed up PR builds by caching the ffmpeg module build artifact
2025-10-30 14:32:07 -04:00
damontecres
3f7c2ac730
UI Updates & Fixes (#114)
Fixes #113 

- Ensures all audio and subtitle options are shown during playback
- Fix home rows not being at top when going back
- Show error message when background home page refresh fails

Also shows more details for login failures and adds a link to the debug
page (with log info) after multiple login failure attempts. Hopefully
this can help diagnose #112 and #18
2025-10-30 13:41:46 -04:00
damontecres
b1275ae210
Include ffmpeg extensions for some audio formats (#109)
Related to #103

Adds ffmpeg decoder module to support a few extra audio formats such as
AC3, EAC3, DTS, & TrueHD.

This increases the apk size by about 5.4mb (release 17.4mb->22.8mb), but
definitely worth it for the added support.
2025-10-29 20:34:41 -04:00
damontecres
f76556f90e
Fix network on main exception trying to play theme songs (#111)
Fetch the theme song info on the IO thread

This would throw an exception on some devices, but it would be caught
without affecting the app, other than just not playing theme music.
2025-10-29 20:07:00 -04:00
Damontecres
84ee57225b
Release v0.2.2
Some checks failed
Create release / publish (push) Has been cancelled
2025-10-29 13:59:03 -04:00
damontecres
cbcae526b4
Don't report playback started until it actually starts (#107)
Wait until playback actually beings before reporting status to the
server. And also ensure progress isn't reported until after reporting
start.
2025-10-29 13:15:17 -04:00
damontecres
576c20edff
Fixes & optimizations to series pages (#106)
Simplifies the process for finding the right season id & episode id
after clicking on an episode. This can also improve loading time for
seasons that have a large number of episodes (hundreds).

Also fixes handling for "unknown" seasons which don't have an
index/season number.
2025-10-29 11:02:21 -04:00
Damontecres
808e379e02
Use neutral color for buffered & remaining seek bar 2025-10-28 21:49:43 -04:00
Damontecres
5682f61b02
Always scroll rows w/ overview to top 2025-10-28 21:47:46 -04:00
damontecres
d578dca0cf
Make watched indicator more clear (#102)
Closes #89

Updates the watched indicator to be more clear. The checkmark is filled
in and there's a slight border to add contrast.

Also this PR makes the location of the watched indicator consistent
between card types and makes child counts always use a semi-transparent
black background.

## Before
<img width="451" height="260" alt="watched_before 2"
src="https://github.com/user-attachments/assets/734d1bb7-4604-4ef1-bb40-fc08403d58fe"
/>

## After
<img width="458" height="272" alt="watched_after 2"
src="https://github.com/user-attachments/assets/d39c4e8e-e483-4843-9083-33209302feb1"
/>
2025-10-28 20:47:17 -04:00
damontecres
06f2c1260d
Add slf4j-simple (#101)
Fixes #99 

While not an actual error, it does look like a transitive dependency of
the Jellyfin Kotlin SDK may log using slf4j, so it would be good to
include an slf4j implementation just in case.
2025-10-28 19:28:54 -04:00
damontecres
f291a0ec04
Move focus from username to password field on enter (#96)
Closes #93
2025-10-28 17:35:26 -04:00
damontecres
3ab9eafc7c
Fix crash when changing seasons & improve error page (#94)
Related to #64 

If an error occurred while querying for a season's episodes, the app
would crash instead of display the error message.

This PR fixes the app crash, but not the underlying query issue. It also
changes the error page to show a button for sending logs to the server
immediately.
2025-10-28 17:04:15 -04:00
Damontecres
50b0b0a508
Release v0.2.1
Some checks failed
Create release / publish (push) Has been cancelled
2025-10-28 13:23:38 -04:00
damontecres
1db48334ab
Sort & format subtitle download options (#88)
Sorts the subtitle download options by community rating and downloads.

Also abbreviates the download count if it's large (eg 5133->5.1k).
2025-10-28 13:07:30 -04:00
damontecres
af0e853c21
Don't send crash reports as json (#84)
This is a workaround for jellyfin/jellyfin-web#7267 to ensure the client
logs are not valid JSON and can be rendered on the `10.11.x` web UI.

Related to #82
2025-10-27 21:22:44 -04:00
damontecres
f1f6b729aa
Fix several UI focus issues (#81)
Remembers the page position & focused item on both Movie & Series detail
pages

Only loads similar movies & series once so that if you click on one and
go back, it doesn't refresh the entire list

Remember the tab on the Favorite page (if remember tabs is enabled in
settings)
2025-10-27 20:44:04 -04:00
damontecres
c4df751918
Fallback to transcoding if direct play fails (#79)
If direct playing (or direct streaming) fails on device, attempt to fall
back to transcoding.

Also display more error information if available
2025-10-27 16:11:30 -04:00
damontecres
07a0db8e98
Add ability to search & download subtitles (#78)
Closes #74 

Adds an option to download subtitles from a remote source. This uses the
Jellyfin server's API, so you need to install a plugin on your server
that provides subtitle searching such as the [official OpenSubtitles
plugin](https://github.com/jellyfin/jellyfin-plugin-opensubtitles).

Initially it will search for subtitles in the device's current language,
but you can search for a different language if needed.
2025-10-27 15:53:58 -04:00
damontecres
4f5b0c7eee
Update bug.yml 2025-10-27 15:50:00 -04:00
damontecres
bafd635402
Add setting to change default content scaling (#77)
Closes #53 #71

Adds an advanced playback setting to set a different default global
content scaling. It can still be changed per playback as well.
2025-10-27 14:25:07 -04:00
damontecres
4269d27dad
Search page improvements (#76)
Update the search page results to indicate whether 1) the search is in
progress, 2) there are no results, or 3) the actual results. This makes
it clear when there's actually no results versus the search is just
running slowly.

Also search for collections as well
2025-10-27 10:28:27 -04:00
damontecres
1a7684b458
Minor UI changes (#75)
* Revert transparent cards from #61
* Don't show queue during playback if playing the final item
* Fix nav drawer collection page from showing everything recursively
* Immediately hide the skip intro button when pressed
2025-10-27 10:17:50 -04:00
damontecres
89b9364807
Dependency updates & removing unused code (#70)
Just bumping dependencies and deleting some unused code. No user facing
changes
2025-10-26 18:17:47 -04:00
damontecres
1c1e64a5b9
Fix focus on home page load defaulting to nav drawer (#68)
When the home page loaded, it would only try to focus on the "Resume"
row even if there were no items in that row (so it doesn't appear). This
resulted in the focus defaulting instead to the nav drawer.

This PR fixes focus so it always starts with the first item in the first
row of the home page.
2025-10-26 17:05:41 -04:00
3352 changed files with 185516 additions and 14212 deletions

View file

@ -1,6 +1,4 @@
name: "Feature Request" title: "<title>"
description: Request a new feature
title: "[FEA] - <title>"
labels: [ labels: [
"enhancement" "enhancement"
] ]

View file

@ -1,6 +1,5 @@
name: "Bug Report" name: "Bug Report"
description: Report a bug description: Report a bug
title: "[BUG] - <title>"
labels: [ labels: [
"bug" "bug"
] ]
@ -17,18 +16,28 @@ body:
attributes: attributes:
label: "Reproduction steps" label: "Reproduction steps"
description: Please enter the steps to reproduce description: Please enter the steps to reproduce
value: | placeholder: |
1. Click on '...' 1. Click on '...'
2. Scroll to '....' 2. Scroll to '....'
3. See error 3. See error
validations: validations:
required: false required: false
- type: dropdown
id: experimental
attributes:
label: Experimental settings
description: Do you have experiment settings turn on?
options:
- "Yes"
- "No"
validations:
required: true
- type: input - type: input
id: app-version id: app-version
attributes: attributes:
label: "App Version" label: "App Version"
description: What version of the app? description: What version of the app?
placeholder: "v0.1.0" placeholder: "v0.5.1"
validations: validations:
required: true required: true
- type: input - type: input
@ -36,7 +45,7 @@ body:
attributes: attributes:
label: "Server Version" label: "Server Version"
description: What version of the server? description: What version of the server?
placeholder: "10.10.7" placeholder: "10.11.6"
validations: validations:
required: true required: true
- type: input - type: input
@ -51,7 +60,7 @@ body:
id: logs id: logs
attributes: attributes:
label: "Logs" label: "Logs"
description: Please gather any logs or crash reports and include them here. description: Please [gather any logs or crash reports](https://github.com/damontecres/Wholphin/wiki/Gathering-app-logs) and include them here.
placeholder: Review logs for personal information before sharing them here publicly! placeholder: Review logs for personal information before sharing them here publicly!
render: bash render: bash
validations: validations:

76
.github/ISSUE_TEMPLATE/01-playback.yml vendored Normal file
View file

@ -0,0 +1,76 @@
name: "Playback Problem"
description: Report an issue with playback
labels: [
"bug", "playback"
]
body:
- type: textarea
id: description
attributes:
label: "Description"
description: Please enter a description of the playback issue
validations:
required: true
- type: dropdown
id: experimental
attributes:
label: Experimental settings
description: Do you have experiment settings turn on?
options:
- "Yes"
- "No"
validations:
required: true
- type: dropdown
id: player
attributes:
label: Player backend
description: Which player(s) cause the issue?
multiple: true
options:
- ExoPlayer (default)
- MPV
- External App (please specify)
validations:
required: true
- type: textarea
id: mediainfo
attributes:
label: "Media info"
description: Please enter details about what media you are trying to play (video or audio codecs, container, etc)
placeholder: Use mediainfo or "Send media info to server" in the app to gather this
validations:
required: true
- type: input
id: app-version
attributes:
label: "App Version"
description: What version of the app?
placeholder: "v0.5.1"
validations:
required: true
- type: input
id: server
attributes:
label: "Server Version"
description: What version of the server?
placeholder: "10.11.6"
validations:
required: true
- type: input
id: device
attributes:
label: "Device"
description: What device are you using?
placeholder: "NVIDIA Shield TV Pro 2019"
validations:
required: false
- type: textarea
id: logs
attributes:
label: "Logs"
description: Please [gather any logs or crash reports](https://github.com/damontecres/Wholphin/wiki/Gathering-app-logs) and include them here.
placeholder: Review logs for personal information before sharing them here publicly!
render: bash
validations:
required: false

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: New feature / UI suggestion
url: https://github.com/damontecres/Wholphin/discussions/new?category=ideas
about: Please request new features here
- name: Questions
url: https://github.com/damontecres/Wholphin/discussions/new?category=q-a
about: Please ask questions here

View file

@ -1,15 +0,0 @@
name: "Question"
description: Ask a question
title: "[QST] - <title>"
labels: [
"question"
]
body:
- type: textarea
id: description
attributes:
label: "Question"
description: Please enter your question
placeholder: "How do I...?"
validations:
required: true

31
.github/actions/setup/action.yml vendored Normal file
View file

@ -0,0 +1,31 @@
name: Setup
description: "Setup the environment"
runs:
using: "composite"
steps:
# Setup the SDKs
- name: Set up Python
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7
with:
python-version: '3.14'
- name: Setup JDK
uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6
with:
distribution: zulu
java-version: '21'
cache: 'gradle'
- name: Set versions
shell: bash
run: |
echo "BUILD_TOOLS_VERSION=36.0.0" >> $GITHUB_ENV
echo "NDK_VERSION=29.0.14206865" >> $GITHUB_ENV
- name: Setup Android SDK
uses: android-actions/setup-android@be39fa834029ff78f1a44aa3bb0819b8fc2bd8fd # v4
with:
packages: "platform-tools build-tools;${{ env.BUILD_TOOLS_VERSION }} ndk;${{ env.NDK_VERSION }}"
- name: Add NDK to path
shell: bash
run: |
echo "Adding to path: ${{ env.ANDROID_SDK_ROOT }}/ndk/${{ env.NDK_VERSION }}:${{ env.ANDROID_SDK_ROOT }}/ndk/${{ env.NDK_VERSION }}/toolchains/llvm/prebuilt/linux-x86_64/bin"
echo "${{ env.ANDROID_SDK_ROOT }}/ndk/${{ env.NDK_VERSION }}:${{ env.ANDROID_SDK_ROOT }}/ndk/${{ env.NDK_VERSION }}/toolchains/llvm/prebuilt/linux-x86_64/bin" >> $GITHUB_PATH

17
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,17 @@
<!-- By submitting this pull request, you acknowledge that you have read the [contributing guide](https://github.com/damontecres/Wholphin/blob/main/CONTRIBUTING.md, including the AI/LLM policy, and [developer's guide](https://github.com/damontecres/Wholphin/blob/main/DEVELOPMENT.md) -->
## Description
<!-- Describe the changes in detail -->
### Related issues
<!-- If this is a new feature or a change, there must be a discussion in an issue first, reference the issue here -->
<!-- If fixing a bug, reference the bug issue here, or describe the bug, including steps to reproduce -->
### Testing
<!-- Describe how this change was tested and on what device(s) -->
## Screenshots
<!-- Please include screenshots if the PR alters any UI elements -->
## AI or LLM usage
<!-- If you used any AI or LLM assistance, please list where in the code and how you tested it -->

View file

@ -15,10 +15,8 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
env: env:
APK_SHORT_NAME: Wholphin-release.apk
APK_DEBUG_SHORT_NAME: Wholphin-debug.apk
BRANCH_NAME: ${{ github.ref_name }} BRANCH_NAME: ${{ github.ref_name }}
BUILD_TOOLS_VERSION: 36.0.0 GRADLE_OPTS: "-Dorg.gradle.jvmargs='-Xmx12g -XX:MaxMetaspaceSize=1024m'"
jobs: jobs:
build: build:
@ -27,20 +25,11 @@ jobs:
contents: write contents: write
steps: steps:
- name: Checkout the code - name: Checkout the code
uses: actions/checkout@v4 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with: with:
fetch-depth: 0 # Need the tags to build fetch-depth: 0 # Need the tags to build
# Setup the SDKs - name: Setup
- name: Setup JDK uses: ./.github/actions/setup
uses: actions/setup-java@v4
with:
distribution: zulu
java-version: '21'
cache: 'gradle'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
with:
packages: "tools platform-tools build-tools;${{ env.BUILD_TOOLS_VERSION }}"
- name: Get version names - name: Get version names
run: | run: |
@ -62,67 +51,58 @@ jobs:
KEY_PASSWORD: "${{ secrets.KEY_PASSWORD }}" KEY_PASSWORD: "${{ secrets.KEY_PASSWORD }}"
KEY_STORE_PASSWORD: "${{ secrets.KEY_STORE_PASSWORD }}" KEY_STORE_PASSWORD: "${{ secrets.KEY_STORE_PASSWORD }}"
SIGNING_KEY: "${{ secrets.SIGNING_KEY }}" SIGNING_KEY: "${{ secrets.SIGNING_KEY }}"
ORG_GRADLE_PROJECT_WholphinExtensionsUsername: "${{ secrets.EXTENSIONS_USERNAME }}"
ORG_GRADLE_PROJECT_WholphinExtensionsPassword: "${{ secrets.EXTENSIONS_PASSWORD }}"
run: | run: |
./gradlew clean assembleRelease assembleDebug ./gradlew clean assembleDefaultDebug
echo "apk=$(ls app/build/outputs/apk/release/Wholphin-release*.apk)" >> "$GITHUB_OUTPUT" ./gradlew assembleDefaultRelease
echo "apk_debug=$(ls app/build/outputs/apk/debug/Wholphin-debug*.apk)" >> "$GITHUB_OUTPUT"
- name: Verify signature - name: Verify signatures
run: | run: |
echo "Verify release APK" echo "Verify APK signatures"
${{env.ANDROID_SDK_ROOT}}/build-tools/${{ env.BUILD_TOOLS_VERSION }}/apksigner verify --verbose --print-certs "${{ steps.build.outputs.apk }}" find app/build/outputs/apk -name '*.apk'
echo "Verify debug APK" find app/build/outputs/apk -name '*.apk' -print0 | xargs -t -0 -n1 ${{env.ANDROID_SDK_ROOT}}/build-tools/${{ env.BUILD_TOOLS_VERSION }}/apksigner verify --verbose --print-certs
${{env.ANDROID_SDK_ROOT}}/build-tools/${{ env.BUILD_TOOLS_VERSION }}/apksigner verify --verbose --print-certs "${{ steps.build.outputs.apk_debug }}" - name: Copy APK to shorter names
- name: Copy APK to ${{ env.APK_SHORT_NAME }} id: apks
run: | run: |
cp "${{ steps.build.outputs.apk }}" "${{ env.APK_SHORT_NAME }}" find app/build/outputs/apk -name '*.apk' -print0 |
cp "${{ steps.build.outputs.apk_debug }}" "${{ env.APK_DEBUG_SHORT_NAME }}" while IFS= read -r -d '' line; do
# Wholphin-release-0.2.9-62-g5c12e03-16-arm64-v8a.apk => Wholphin-arm64-v8a.apk
short_name="$(echo $line | sed -E 's/-[0-9]+\.[0-9]+\.[0-9]+-[0-9]+-g[a-fA-F0-9]+-[0-9]+//' | sed -E 's/-default//')"
echo "$line => $short_name"
cp "$line" "$short_name"
done
- name: Checksums - name: Checksums
run: | run: |
echo "SHA256 checksums:" echo "SHA256 checksums:"
sha256sum "${{ steps.build.outputs.apk }}" "${{ env.APK_SHORT_NAME }}" find app/build/outputs/apk -name '*.apk' -print0 | xargs -0 sha256sum
sha256sum "${{ steps.build.outputs.apk_debug }}" "${{ env.APK_DEBUG_SHORT_NAME }}" - name: Upload mapping files
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: mapping
path: |
app/build/outputs/**/mapping.txt
compression-level: 6
- name: Delete ${{ env.TAG_NAME }} tag and release - name: Delete ${{ env.TAG_NAME }} tag and release
uses: dev-drprasad/delete-tag-and-release@v1.1 env:
with: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
tag_name: "${{ env.TAG_NAME }}" run: |
delete_release: true shopt -s globstar
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Advance ${{ env.TAG_NAME }} tag gh release delete "${{ env.TAG_NAME }}" --cleanup-tag -y || true
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
await github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: "refs/tags/${{ env.TAG_NAME }}",
sha: context.sha
})
- name: Create GitHub release
uses: ncipollo/release-action@v1
with:
allowUpdates: true
artifactErrorsFailBuild: true
artifacts: "${{ steps.build.outputs.apk_debug }},${{ steps.build.outputs.apk }},${{ env.APK_DEBUG_SHORT_NAME }},${{ env.APK_SHORT_NAME }}"
draft: false
generateReleaseNotes: false
makeLatest: false
name: "${{ env.VERSION_NAME }}"
prerelease: true
removeArtifacts: true
replacesArtifacts: true
updateOnlyUnreleased: true
tag: "${{ env.TAG_NAME }}"
body: | echo '### `${{ env.BRANCH_NAME }}` development build
### `${{ env.BRANCH_NAME }}` development build
This pre-release tracks the development debug build of Wholphin from the `${{ env.BRANCH_NAME }}` branch. This pre-release tracks the development build of Wholphin from the `${{ env.BRANCH_NAME }}` unstable branch.
See https://github.com/damontecres/Wholphin/releases/latest for the latest stable release. See https://github.com/damontecres/Wholphin/releases/latest for the latest stable release.
If you want to update to this version in-app, change Settings->Advanced->Update URL to https://api.github.com/repos/damontecres/Wholphin/releases/tags/${{ env.TAG_NAME }} (replace `latest` with `tags/${{ env.TAG_NAME }}`). If you want to update to this version in-app, change Settings->Advanced->Update URL to https://api.github.com/repos/damontecres/Wholphin/releases/tags/${{ env.TAG_NAME }} (replace `latest` with `tags/${{ env.TAG_NAME }}`).
[See changes since `${{ env.LATEST_TAG }}`](https://github.com/damontecres/Wholphin/compare/${{ env.LATEST_TAG }}...${{ env.TAG_NAME }}) [See changes since `${{ env.LATEST_TAG }}`](https://github.com/damontecres/Wholphin/compare/${{ env.LATEST_TAG }}...${{ env.TAG_NAME }})' > ci_release_note.txt
gh release create "${{ env.TAG_NAME }}" \
--latest=false --prerelease --title "${{ env.VERSION_NAME }}" --target "${{ env.BRANCH_NAME }}" -F ci_release_note.txt \
app/build/outputs/apk/**/*.apk

View file

@ -8,58 +8,37 @@ defaults:
shell: bash shell: bash
env: env:
BUILD_TOOLS_VERSION: 36.0.0
BUILD_DIRS_ARTIFACT: build-dirs BUILD_DIRS_ARTIFACT: build-dirs
GRADLE_OPTS: "-Dorg.gradle.jvmargs='-Xmx12g -XX:MaxMetaspaceSize=1024m'"
jobs: jobs:
pre-commit: pre-commit:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout the code - name: Checkout the code
uses: actions/checkout@v4 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with: with:
fetch-depth: 0 # Need the tags to build fetch-depth: 0 # Need the tags to build
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7
with: with:
python-version: '3.10' python-version: '3.14'
- name: Run pre-commit - name: Run pre-commit
uses: pre-commit/action@v3.0.1 uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: pre-commit needs: pre-commit
steps: steps:
- name: Checkout the code - name: Checkout the code
uses: actions/checkout@v4 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with: with:
fetch-depth: 0 # Need the tags to build fetch-depth: 0 # Need the tags to build
- name: Setup JDK - name: Setup
uses: actions/setup-java@v4 uses: ./.github/actions/setup
with:
distribution: zulu
java-version: '21'
cache: 'gradle'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
with:
packages: "tools platform-tools build-tools;${{ env.BUILD_TOOLS_VERSION }}"
- name: Build app - name: Build app
id: buildapp id: buildapp
run: | run: |
./gradlew clean build ./gradlew clean assembleDefaultDebug testDefaultDebugUnitTest --no-daemon
echo "apk=$(ls app/build/outputs/apk/debug/Wholphin-debug*.apk)" >> "$GITHUB_OUTPUT" apks=$(find app/build/outputs \( -name '*.apk' -or -name '*.aab' \) -print0 | tr '\0' ',' | sed 's/,$//')
- name: Tar build dirs echo "apks=$apks" >> "$GITHUB_OUTPUT"
run: |
tar -czf build.tgz */build/.
- uses: actions/upload-artifact@v4
id: upload-build-dirs
with:
name: "${{ env.BUILD_DIRS_ARTIFACT }}"
path: build.tgz
if-no-files-found: error
- uses: actions/upload-artifact@v4
with:
name: Wholphin-debug.apk
path: "${{ steps.buildapp.outputs.apk }}"
compression-level: 0

View file

@ -11,7 +11,8 @@ defaults:
env: env:
APK_SHORT_NAME: Wholphin.apk APK_SHORT_NAME: Wholphin.apk
BUILD_TOOLS_VERSION: 36.0.0 GRADLE_OPTS: "-Dorg.gradle.jvmargs='-Xmx12g -XX:MaxMetaspaceSize=1024m'"
TAG_NAME: ${{ github.ref_name }}
jobs: jobs:
publish: publish:
@ -20,23 +21,11 @@ jobs:
contents: write contents: write
steps: steps:
- name: Checkout the code - name: Checkout the code
uses: actions/checkout@v4 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with: with:
fetch-depth: 0 # Need the tags to build fetch-depth: 0 # Need the tags to build
- name: Setup Python - name: Setup
uses: actions/setup-python@v5 uses: ./.github/actions/setup
with:
python-version: '3.10'
- name: Setup JDK
uses: actions/setup-java@v4
with:
distribution: zulu
java-version: '21'
cache: 'gradle'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
with:
packages: "tools platform-tools build-tools;${{ env.BUILD_TOOLS_VERSION }}"
- name: Build app - name: Build app
id: buildapp id: buildapp
env: env:
@ -44,23 +33,51 @@ jobs:
KEY_PASSWORD: "${{ secrets.KEY_PASSWORD }}" KEY_PASSWORD: "${{ secrets.KEY_PASSWORD }}"
KEY_STORE_PASSWORD: "${{ secrets.KEY_STORE_PASSWORD }}" KEY_STORE_PASSWORD: "${{ secrets.KEY_STORE_PASSWORD }}"
SIGNING_KEY: "${{ secrets.SIGNING_KEY }}" SIGNING_KEY: "${{ secrets.SIGNING_KEY }}"
ORG_GRADLE_PROJECT_WholphinExtensionsUsername: "${{ secrets.EXTENSIONS_USERNAME }}"
ORG_GRADLE_PROJECT_WholphinExtensionsPassword: "${{ secrets.EXTENSIONS_PASSWORD }}"
run: | run: |
./gradlew clean assembleRelease ./gradlew clean bundleAppstoreRelease
echo "apk=$(ls app/build/outputs/apk/release/Wholphin-release*.apk)" >> "$GITHUB_OUTPUT" ./gradlew bundleFiretvRelease
- name: Verify signature ./gradlew assembleDefaultRelease
- name: Verify signatures
run: | run: |
${{env.ANDROID_SDK_ROOT}}/build-tools/${{ env.BUILD_TOOLS_VERSION }}/apksigner verify --verbose --print-certs "${{ steps.buildapp.outputs.apk }}" echo "Verify APK signatures"
- name: Copy APK to ${{ env.APK_SHORT_NAME }} find app/build/outputs \( -name '*.apk' -or -name '*.aab' \)
find app/build/outputs \( -name '*.apk' \) -print0 | xargs -0 -n1 ${{env.ANDROID_SDK_ROOT}}/build-tools/${{ env.BUILD_TOOLS_VERSION }}/apksigner verify --verbose --print-certs
- name: Copy APK to shorter names
id: apks
run: | run: |
cp "${{ steps.buildapp.outputs.apk }}" "${{ env.APK_SHORT_NAME }}" find app/build/outputs/apk -name '*.apk' -print0 |
while IFS= read -r -d '' line; do
# Wholphin-default-release-0.2.9-62-g5c12e03-16-arm64-v8a.apk => Wholphin-arm64-v8a.apk
short_name="$(echo $line | sed -E 's/-default-release-[0-9]+\.[0-9]+\.[0-9]+-[0-9]+-g[a-fA-F0-9]+-[0-9]+//')"
echo "$line => $short_name"
cp "$line" "$short_name"
done
- name: Checksums - name: Checksums
run: | run: |
echo "SHA256 checksums:" echo "SHA256 checksums:"
sha256sum "${{ steps.buildapp.outputs.apk }}" "${{ env.APK_SHORT_NAME }}" find app/build/outputs \( -name '*.apk' -or -name '*.aab' \) -print0 | xargs -0 sha256sum
- name: Create GitHub release - name: Upload AAB
uses: ncipollo/release-action@v1 uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with: with:
artifacts: "${{ steps.buildapp.outputs.apk }},${{ env.APK_SHORT_NAME }}" name: AAB
makeLatest: true path: |
prerelease: false app/build/outputs/bundle/**/*.aab
generateReleaseNotes: true compression-level: 0
- name: Upload mapping files
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
with:
name: mapping
path: |
app/build/outputs/**/mapping.txt
compression-level: 6
- name: Create GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
shopt -s globstar
gh release create "${{ env.TAG_NAME }}" \
--latest --title "${{ env.TAG_NAME }}" --verify-tag -n "" --draft \
app/build/outputs/apk/**/*.apk

16
.gitignore vendored
View file

@ -48,3 +48,19 @@ google-services.json
.externalNativeBuild .externalNativeBuild
.cxx .cxx
local.properties local.properties
local_scripts/
app/libs/
ffmpeg_decoder/
app/release/
app/debug/
.vscode
.kotlin
docs/
# mpv
app/src/main/obj/
app/src/main/libs/
app/src/main/jnilibs
app/src/main/jnilibs/
scripts/mpv/deps/
scripts/mpv/prefix/

View file

@ -1,16 +1,16 @@
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0 rev: v6.0.0
hooks: hooks:
- id: check-xml - id: check-xml
- id: check-yaml - id: check-yaml
- id: end-of-file-fixer - id: end-of-file-fixer
- id: trailing-whitespace - id: trailing-whitespace
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.12.0 rev: v2.15.0
hooks: hooks:
- id: pretty-format-kotlin - id: pretty-format-kotlin
args: [ --autofix, --ktlint-version=1.7.1 ] args: [ --autofix, --ktlint-version=1.8.0 ]
- repo: local - repo: local
hooks: hooks:
- id: check-debug-enabled - id: check-debug-enabled
@ -18,3 +18,8 @@ repos:
entry: val DEBUG = true entry: val DEBUG = true
language: pygrep language: pygrep
files: '.+\.kt' files: '.+\.kt'
- id: check-debug-logging-enabled
name: Check debug enabled
entry: debugLogging = true
language: pygrep
files: '.+\.kt'

View file

@ -12,6 +12,7 @@ You can contribute in several ways:
* File bug reports * File bug reports
* Request new features * Request new features
* Propose code changes via pull request * Propose code changes via pull request
* Submit translations to https://translate.codeberg.org/engage/wholphin/
In all cases, be sure to fully explain the issue. In all cases, be sure to fully explain the issue.
@ -22,3 +23,10 @@ You acknowledge that, if applicable, submitting and subsequent acceptance of any
Please be thoughtful when contributing code and consider the long term maintenance/support aspect for any new features. Please be thoughtful when contributing code and consider the long term maintenance/support aspect for any new features.
See the [developer's guide](./DEVELOPMENT.md) for more information. See the [developer's guide](./DEVELOPMENT.md) for more information.
#### Policy for AI or LLMs
Wholphin has adopted the [Jellyfin LLM/"AI" Development Policy](https://jellyfin.org/docs/general/contributing/llm-policies/). AI/LLM assisted code contributions are permitted under the policy, but the author must undertand and be able to explain the code changes in their own words.
At a minimum, you must disclose the usage any of AI/LLM assistance and you must write the pull request description _without_ AI assistance.

View file

@ -14,6 +14,7 @@ The app uses:
* [Room](https://developer.android.com/training/data-storage/room) & [DataStore](https://developer.android.com/topic/libraries/architecture/datastore) for local data storage * [Room](https://developer.android.com/training/data-storage/room) & [DataStore](https://developer.android.com/topic/libraries/architecture/datastore) for local data storage
* [Hilt](https://developer.android.com/training/dependency-injection/hilt-android) for dependency injection * [Hilt](https://developer.android.com/training/dependency-injection/hilt-android) for dependency injection
* [Media3/ExoPlayer](https://developer.android.com/media/media3/exoplayer) for media playback * [Media3/ExoPlayer](https://developer.android.com/media/media3/exoplayer) for media playback
* [MPV/libmpv](https://github.com/mpv-player/mpv) for media playback
* [Coil](https://coil-kt.github.io/coil/) for image loading * [Coil](https://coil-kt.github.io/coil/) for image loading
* [OkHttp](https://square.github.io/okhttp/) for HTTP requests * [OkHttp](https://square.github.io/okhttp/) for HTTP requests
@ -23,4 +24,82 @@ We follow GitHub's fork & pull request model for contributions.
After forking and cloning your fork, you can import the project into Android Studio. After forking and cloning your fork, you can import the project into Android Studio.
You need a compatible Android Studio version for the configured AGP. This is generally `Narwhal 3 Feature Drop | 2025.1.3` or newer. See https://developer.android.com/build/releases/gradle-plugin and [`libs.versions.toml](./gradle/libs.versions.toml). ### Development environment
It is recommended to use a recent version of [Android Studio](https://developer.android.com/studio). Make sure the [version is compatible](https://developer.android.com/build/releases/gradle-plugin#android_gradle_plugin_and_android_studio_compatibility) with Wholphin's AGP version.
Code formatting should follow [ktlint's](https://github.com/pinterest/ktlint) rules. Find the `ktlint` version in [`.pre-commit-config.yaml`](./.pre-commit-config.yaml). Optionally, install the [ktlint plugin](https://plugins.jetbrains.com/plugin/15057-ktlint) in Android Studio to run automatically. Configure the version in `Settings->Tools->KtLint->Ruleset Version`.
Also, it's recommend to add an extra ruleset jar for Compose-specific KtLint: https://mrmans0n.github.io/compose-rules/ktlint/#using-with-ktlint-cli-or-the-ktlint-intellij-plugin
Also setup [pre-commit](https://github.com/pre-commit/pre-commit) which will run `ktlint` as well on each commit, plus check for other common issues.
#### Extensions
Wholphin uses several native components for extra playback compatibility. This includes Media3 ffmpeg/av1 decoders and `libmpv`. These extensions are not required to build the app, but without them some functionality will not work.
If you want to include these in a local build, see the [instructions here](https://github.com/damontecres/wholphin-extensions?tab=readme-ov-file#usage) for configuring the repository.
You can also build the extensions locally from https://github.com/damontecres/wholphin-extensions and include them in `app/libs`. The gradle build dependency resolution prefers these local files over fetching from the remote maven registry.
Finally, if no `wholphin-mpv` implementation is found, `wholphin-mpv-stub` will be used. This allows the app to compile, but any runtime usage of MPV will throw an exception.
## Code organization
Code is split into several packages:
- `data` - app-specific data models and services
- `preferences` - Non-UI related code for user settings and preferences
- `services` - hilt injectable services often used by ViewModels for API calls
- `ui` - User interface code and ViewModels
- `util` - Utility classes and functions
### Settings
There are a few different ways user settings are stored:
1. `AppPreferences` via DataStore
2. Room database
3. Key-Value DataStore
#### App preferences
These are generally settings that apply across the whole app regardless of the currently active user.
The `AppPreferences` object can be retrieved from the `UserPreferencesService` or directly via injecting `DataStore<AppPreferences>`.
The `AppPreference` (note the `s` differences) objects are used to create the UI for configuring settings using the composable functions in `com.github.damontecres.wholphin.ui.preferences`.
##### How to add a new app setting
1. Add entry in `WholphinDataStore.proto` & build to generate classes
2. Add new `AppPreference` object in `AppPreference.kt`
3. Add new object to a `PreferenceGroup` (listed in `AppPreference.kt`)
4. Update `AppPreferencesSerializer` to set the default value for new installs
5. If needed, update `AppUpgradeHandler` to set the default value for app upgrades
- Since preferences use proto3, the [default values](https://protobuf.dev/programming-guides/proto3/#default) are zero, false, or the first enum, so only need this step if the default value is different
#### Room settings
These are settings were generally are applied per user. They are stored in several different tables. The entities are defined in `com.github.damontecres.wholphin.data.model`.
Additionally, all server and user info for both Jellyfin and Seerr are stored in Room tables.
Some examples are:
- `JellyfinServer`
- `JellyfinUser`
- `SeerrServer`
- `LibraryDisplayInfo`
- `ItemPlayback`
Some of the models, such as `GetItemsFilter` or `ViewOptions`, are stored as JSON in a column instead of defined entities. This should be used sparingly.
#### Key-value settings
These settings are similar to Room ones and are usually stored per user. These can be accessed via `KeyValueService`. The values are `@Serializable` objects saved as JSON.
This should be used sparingly.
#### Experimental
Some settings are considered "experimental". These may cause instabilities or may need further testing. They may be changed or removed any time.
These are stored in the `ExperimentalPreferences` object in `AppPreferences`. Accessing them should always be gated with `ExperimentalPreferences.enabled`. There are a few helper functions for this purpose.

168
Intents.md Normal file
View file

@ -0,0 +1,168 @@
# Intents
> [!IMPORTANT]
> This feature is experimental and the exact behaviors and/or parameters may change at any time
Wholphin accepts [intents](https://developer.android.com/guide/components/intents-filters) to load or play specified media.
Parameters can be specified as "extras" (eg `--es query value`) or as query parameters to the data URI (eg `-d wholphin://search?query=value`).
UUID parameters can be specified with or without hyphens.
If using data URI query parameters on the command line, you need to escape ampersands: `itemId=...\&userId=...`
## Server/user switching
You can choose a server and user to switch to with the `serverId` and `userId` UUID parameters. If the profile is not protected, the app will switch to it.
If not specified, the app will use the current user if auto sign in is enabled.
## Search
Action: `android.intent.action.SEARCH`
Shorthand action: `search`
Parameters:
- `query` - Optional, prepopulate the search field
### Examples
Full format
```bash
adb shell am start \
-a android.intent.action.SEARCH \
-n 'org.justinvisser.wolfskin.tv/com.github.damontecres.wholphin.MainActivity' \
--es query "Avengers"
```
URI based
```bash
adb shell am start -d 'wholphin://search?query=Avengers'
```
Multi-word query:
```bash
# Whole shell command is wrapped in double quotes
adb shell "am start \
-a android.intent.action.SEARCH \
-n 'org.justinvisser.wolfskin.tv/com.github.damontecres.wholphin.MainActivity' \
--es query 'Avengers Endgame'"
adb shell am start -d 'wholphin://search?query=Avengers+Endgame'
adb shell am start -d 'wholphin://search?query=Avengers%20Endgame'
```
## View
This opens the app to the specified item such as an episode or movie
Action: `android.intent.action.VIEW`
Shorthand action: `view`
Parameters:
- `itemId` - Optional, the UUID of the media item, if not set, goes to home screen
### Examples
Full format
```bash
adb shell am start \
-a android.intent.action.VIEW \
-n 'org.justinvisser.wolfskin.tv/com.github.damontecres.wholphin.MainActivity' \
--es itemId "5cf8f8e7-2a5f-4aa9-8c12-ddf63d42ee6d"
```
URI based
```bash
adb shell am start -d 'wholphin://view?itemId=5cf8f8e7-2a5f-4aa9-8c12-ddf63d42ee6d'
```
## Playback
Action: `com.github.damontecres.wholphin.PLAYBACK`
Shorthand action: `play`
Parameters:
- `itemId` - Required, the UUID of the media item
- `position` - Optional, the start position for playback in milliseconds
- `shuffle` - Optional, whether to shuffle the resulting items
### Examples
Full format
```bash
adb shell am start \
-a com.github.damontecres.wholphin.PLAYBACK \
-n 'org.justinvisser.wolfskin.tv/com.github.damontecres.wholphin.MainActivity' \
--es itemId "5cf8f8e7-2a5f-4aa9-8c12-ddf63d42ee6d"
```
URI based
```bash
adb shell am start -d 'wholphin://play?itemId=5cf8f8e7-2a5f-4aa9-8c12-ddf63d42ee6d'
```
Start at 4m30s:
```bash
adb shell am start \
-a com.github.damontecres.wholphin.PLAYBACK \
-n 'org.justinvisser.wolfskin.tv/com.github.damontecres.wholphin.MainActivity' \
--es itemId "5cf8f8e7-2a5f-4aa9-8c12-ddf63d42ee6d" \
--el position 270000
adb shell am start -d 'wholphin://play?itemId=5cf8f8e7-2a5f-4aa9-8c12-ddf63d42ee6d\&position=270000'
```
Shuffle a series (`itemId` is the Series ID):
```bash
adb shell am start \
-a com.github.damontecres.wholphin.PLAYBACK \
-n 'org.justinvisser.wolfskin.tv/com.github.damontecres.wholphin.MainActivity' \
--es itemId "5cf8f8e7-2a5f-4aa9-8c12-ddf63d42ee6d" \
--ez shuffle true
adb shell am start -d 'wholphin://play?itemId=5cf8f8e7-2a5f-4aa9-8c12-ddf63d42ee6d\&shuffle=true'
```
## More examples
### Switch user
Full format
```bash
# Switch user, go to item
adb shell am start \
-a android.intent.action.VIEW \
-n 'org.justinvisser.wolfskin.tv/com.github.damontecres.wholphin.MainActivity' \
--es itemId "5cf8f8e7-2a5f-4aa9-8c12-ddf63d42ee6d" \
--es serverId "341880f9-ca88-4038-8718-f6b2407950b1" \
--es userId "5c8575e0-468d-44fb-8c70-00aa79160587"
# Switch user, go to home screen
adb shell am start \
-a android.intent.action.VIEW \
-n 'org.justinvisser.wolfskin.tv/com.github.damontecres.wholphin.MainActivity' \
--es serverId "341880f9-ca88-4038-8718-f6b2407950b1" \
--es userId "5c8575e0-468d-44fb-8c70-00aa79160587"
```
URI based
```bash
adb shell am start -d 'wholphin://view?itemId=66c80aaac9c17d3761782d2205520229\&serverId=341880f9-ca88-4038-8718-f6b2407950b1\&userId=5c8575e0-468d-44fb-8c70-00aa79160587'
```
### Debug build
Debug builds have a different package, so you must specify the component by its full name:
```bash
adb shell am start \
-a android.intent.action.VIEW \
-n 'org.justinvisser.wolfskin.tv.debug/com.github.damontecres.wholphin.MainActivity' \
--es itemId "5cf8f8e7-2a5f-4aa9-8c12-ddf63d42ee6d"
```

21
Makefile Normal file
View file

@ -0,0 +1,21 @@
.DEFAULT_GOAL := apk
WOLFSKIN_BUILD_IMAGE ?= wolfskin-build:sdk37-jdk21
WOLFSKIN_GRADLE_VOLUME ?= wolfskin-gradle
WOLFSKIN_SECRETS_DIR ?= /opt/stacks/ventoy/machine-parity/manifest-tank/secrets/wolfskin
export WOLFSKIN_BUILD_IMAGE WOLFSKIN_GRADLE_VOLUME WOLFSKIN_SECRETS_DIR
.PHONY: build-image apk apk-debug test release-tag
build-image:
docker build --platform linux/amd64 --tag "$(WOLFSKIN_BUILD_IMAGE)" docker
apk: build-image
@bash docker/gradle.sh release clean assembleDefaultRelease
apk-debug: build-image
@bash docker/gradle.sh debug assembleDefaultDebug
test: build-image
@bash docker/gradle.sh debug testDefaultDebugUnitTest
release-tag:
@bash docker/release-tag.sh

106
README.md
View file

@ -1,25 +1,83 @@
# Wolfskin
Wolfskin is a personal fork of [Wholphin](https://github.com/damontecres/Wholphin) by damontecres.
Licensed under GPL-2.0, the same licence as upstream (see [LICENSE](LICENSE)).
Wolfskin is an Android TV client for one household. It is released through Forgejo releases on this repo, and it tracks upstream by merging each upstream release tag.
The app uses Wolfskin launcher artwork and a lilac-on-`#07070a` theme by default on a fresh install. Existing theme choices and all upstream colour themes remain available under Settings → App theme. Settings → About credits and links to Wholphin by damontecres.
---
# Wholphin - an OSS Android TV client for Jellyfin # Wholphin - an OSS Android TV client for Jellyfin
> "Never half-phin two jellies. Always wholphin one jelly." > "Never half-phin two jellies. Always wholphin one jelly."
Wholphin is an open-source Android TV client for Jellyfin. It aims to provide a different app UI that's inspired by Plex for users interested in migrating to Jellyfin. Wholphin is an open-source Android TV client for Jellyfin. It aims to provide a different app UI that's inspired by Plex for users interested in migrating to Jellyfin.
This is not a fork of the [official client](https://github.com/jellyfin/jellyfin-androidtv). Wholphin's user interface and controls have been written completely from scratch. Wholphin uses the same media player (media3/ExoPlayer) as the official client. This is not a fork of the [official client](https://github.com/jellyfin/jellyfin-androidtv). Wholphin's user interface and controls have been written completely from scratch. Wholphin supports playing media using either ExoPlayer or MPV.
<img width="1280" height="771" alt="0 1 0_01_home" src="https://github.com/user-attachments/assets/599d7f06-d3b5-4553-b37f-19a9726f058f" /> <p align="center">
<a href="https://github.com/damontecres/Wholphin/releases">
<img alt="Current Release" src="https://img.shields.io/github/release/damontecres/wholphin.svg"/>
</a>
<a href="https://translate.codeberg.org/engage/wholphin/">
<img src="https://translate.codeberg.org/widget/wholphin/wholphin/svg-badge.svg" alt="Translation status" />
</a>
<br/>
<a href="https://play.google.com/store/apps/details?id=com.github.damontecres.wholphin">
<img width="180" alt="Get Wholphin on Google Play" src="https://github.com/user-attachments/assets/2550a4cb-ce46-47a1-ae24-f33a169234b7"/>
</a>
<a href="https://www.amazon.com/gp/product/B0G8RQQR9T/ref=mas_pm_wholphin">
<img width="180" alt="Get Wholphin on Amazon AppStore" src="https://github.com/user-attachments/assets/1f3a3b26-4b4f-44b1-9741-f4c895c8a53b"/>
</a>
</p>
![v0_5_1_home](https://github.com/user-attachments/assets/62bb1703-abdf-4154-9054-e00b6ceb57b5)
## Features ## Features
- A navigation drawer for quick access to libraries, search, and settings from almost anywhere in the app ### User interface
- Show Movie/TV Show titles when browsing libraries
- Play TV Show theme music, if available - Customize the home page to see the content you are interested in
- Plex inspired playback controls, such as: - Use poster or thumb images, show/hide titles, add/remove/re-order different types of rows!
- Option to combine the Continue Watching & Next Up into a single row
- Pin collections, playlists, favorites, genres, and studios
- Remove series from next up
- A navigation drawer for quick access to libraries, favorites, search, and settings from almost anywhere in the app
- Integration with [Seerr](https://github.com/seerr-team/seerr) to discover new movies and TV shows
- Note: only available when installed from [GitHub](https://github.com/damontecres/Wholphin/releases/latest) or the [Play store](https://play.google.com/store/apps/details?id=com.github.damontecres.wholphin)
- Customize library display
- Option to show Movie/TV Show titles
- Choose image types & size
- Choose from grid or list layouts
- Play theme music, if available
- Customize subtitle style for plain text subtitles with separate HDR settings
- Search & download subtitles (requires compatible server plugin such as [OpenSubtitles](https://github.com/jellyfin/jellyfin-plugin-opensubtitles))
- Multiple app color themes
- Protect user profile switches with PIN code or require server login
- Change the user interface language per user
- In-app & Android OS screensaver support
### Playback
- Different media playback engines:
- **ExoPlayer** w/ optional extra audio, SSA/ASS, & AV1 software decoding
- **MPV** for direct playing anything with great SSA/ASS subtitle support
- Plex inspired playback controls:
- Using D-Pad left/right for seeking during playback - Using D-Pad left/right for seeking during playback
- Quickly access video chapters during playback - Quickly access video chapters & queue during playback
- Optionally skip back a few seconds when resuming playback - Optionally skip back a few seconds when resuming playback
- Other (subjective) enhancements: - Live TV & DVR support
- Subtly show playback position along the bottom of the screen while seeking w/ D-Pad - Music & music video support
- Force Continue Watching & Next Up TV episodes to use their Series posters - Cinema mode/pre-roll intro support (requires compatible server plugin such as [Intros](https://github.com/jellyfin/jellyfin-plugin-intros))
- Auto play next episodes with pass out protection
- Option for automatic refresh rate & resolution switching on supported displays
- Trickplay (video preview) support
- Subtly show playback position along the bottom of the screen while seeking w/ D-Pad
### Roadmap ### Roadmap
@ -27,6 +85,8 @@ See [here for the roadmap](https://github.com/damontecres/Wholphin/wiki#roadmap)
## Installation ## Installation
Using [Google Play](https://play.google.com/store/apps/details?id=com.github.damontecres.wholphin) or [Amazon appstore](https://www.amazon.com/gp/product/B0G8RQQR9T/ref=mas_pm_wholphin) are the fastest way to install. But you can follow these instructions to install without needing an app store
Downloader Code: `8668671` Downloader Code: `8668671`
1. Enable side-loading "unknown" apps 1. Enable side-loading "unknown" apps
@ -34,7 +94,7 @@ Downloader Code: `8668671`
- https://www.xda-developers.com/how-to-sideload-apps-android-tv/ - https://www.xda-developers.com/how-to-sideload-apps-android-tv/
- https://developer.android.com/distribute/marketing-tools/alternative-distribution#unknown-sources - https://developer.android.com/distribute/marketing-tools/alternative-distribution#unknown-sources
- https://www.aftvnews.com/how-to-enable-apps-from-unknown-sources-on-an-amazon-fire-tv-or-fire-tv-stick/ - https://www.aftvnews.com/how-to-enable-apps-from-unknown-sources-on-an-amazon-fire-tv-or-fire-tv-stick/
1. Install the APK on your Android TV device with one of these options: 2. Install the APK on your Android TV device with one of these options:
- Install a browser program such as [Downloader](https://www.aftvnews.com/downloader/), use it to get the latest apk with short code `8668671` or URL: http://aftv.news/8668671 - Install a browser program such as [Downloader](https://www.aftvnews.com/downloader/), use it to get the latest apk with short code `8668671` or URL: http://aftv.news/8668671
- Download the latest APK release from the [releases page](https://github.com/damontecres/Wholphin/releases/latest) or http://aftv.news/8668671 - Download the latest APK release from the [releases page](https://github.com/damontecres/Wholphin/releases/latest) or http://aftv.news/8668671
- Put the APK on an SD Card/USB stick/network share and use a file manager app from the Google Play Store / Amazon AppStore (e.g. `FX File Explorer`). Android's preinstalled file manager probably will not work! - Put the APK on an SD Card/USB stick/network share and use a file manager app from the Google Play Store / Amazon AppStore (e.g. `FX File Explorer`). Android's preinstalled file manager probably will not work!
@ -47,18 +107,24 @@ After the initial install above, the app will automatically check for updates. T
The first time you attempt an update, the OS should guide you through enabling the required additional permissions for the app to install updates. The first time you attempt an update, the OS should guide you through enabling the required additional permissions for the app to install updates.
Note: if installed via an app store, the app store will handle updates.
## Compatibility ## Compatibility
Requires Android 7.1+ (or Fire TV OS 6+) and Jellyfin server `10.10.x` (tested on primarily `10.10.7`). Requires Android 6+ (or Fire TV OS 6+) and Jellyfin server `10.10.x` or `10.11.x` (tested on primarily `10.11`).
The app is tested on a variety of Android TV/Fire TV OS devices, but if you encounter issues, please file an issue! The app is tested on a variety of Android TV/Fire TV OS devices, but if you encounter issues, please file an issue!
Seerr integration is generally tested with the latest stable version. Older versions may not work as expected. The Seerr server should configured with integration to your Jellyfin server for full compatibility.
## Contributions ## Contributions
Issues and pull requests are always welcome! Please check before submitting that your issue or pull request is not a duplicate. Issues and pull requests are always welcome! Please check before submitting that your issue or pull request is not a duplicate.
If you plan to contribute, please read the [contributing guide](CONTRIBUTING.md)! If you plan to contribute, please read the [contributing guide](CONTRIBUTING.md)!
You can [help translate Wholphin](https://translate.codeberg.org/engage/wholphin/)!
## Acknowledgements ## Acknowledgements
- Thanks to the Jellyfin team for creating and maintaining such a great open-source media server - Thanks to the Jellyfin team for creating and maintaining such a great open-source media server
@ -68,11 +134,21 @@ If you plan to contribute, please read the [contributing guide](CONTRIBUTING.md)
## Additional screenshots ## Additional screenshots
### Customized home page
![customize_home_example](https://github.com/user-attachments/assets/9a4f04b7-9604-4ea7-b352-50f2b15dc2f1)
### Movie library browsing ### Movie library browsing
<img width="1280" height="771" alt="0 1 0_02_movies" src="https://github.com/user-attachments/assets/0ea18f12-4b0a-49bd-b3c6-df45f97511f3" /> ![v0_5_1_library](https://github.com/user-attachments/assets/fad0424b-0631-4438-a8bc-d4fbb95a5bf3)
### Movie page
![v0_5_1_movie](https://github.com/user-attachments/assets/849aad34-49d5-4864-8de7-005bbcb68ac6)
### Series page ### Series page
<img width="1280" height="771" alt="0 1 0_03_series" src="https://github.com/user-attachments/assets/dc47cd0e-2cd2-4090-8f8e-3f776f67f465" /> ![v0_5_1_series](https://github.com/user-attachments/assets/655389e1-6a6f-43bc-85e1-e2feffb20429)
### Genres in library
![v0_5_1_genres](https://github.com/user-attachments/assets/5bbcbeb6-edc9-42c7-a1d8-d92fa432a498)
### Playlist ### Playlist
<img width="1280" height="771" alt="0 1 0_04_playlist" src="https://github.com/user-attachments/assets/d33451cd-2be0-46c0-a7de-2064e2615d84" /> ![v0_5_1_playlist](https://github.com/user-attachments/assets/98268f7d-479d-41c6-b47b-3e67bbe661bc)

View file

@ -1,13 +1,17 @@
import com.android.build.api.artifact.SingleArtifact
import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.dsl.ProductFlavor
import com.android.build.api.variant.FilterConfiguration
import com.google.protobuf.gradle.id import com.google.protobuf.gradle.id
import com.mikepenz.aboutlibraries.plugin.DuplicateMode import com.mikepenz.aboutlibraries.plugin.DuplicateMode
import com.mikepenz.aboutlibraries.plugin.DuplicateRule import com.mikepenz.aboutlibraries.plugin.DuplicateRule
import java.io.ByteArrayOutputStream import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import java.io.File
import java.util.Base64 import java.util.Base64
import java.util.Properties import java.util.Properties
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.ksp) alias(libs.plugins.ksp)
alias(libs.plugins.kotlin.compose) alias(libs.plugins.kotlin.compose)
alias(libs.plugins.hilt) alias(libs.plugins.hilt)
@ -15,81 +19,74 @@ plugins {
alias(libs.plugins.protobuf) alias(libs.plugins.protobuf)
alias(libs.plugins.kotlin.plugin.serialization) alias(libs.plugins.kotlin.plugin.serialization)
alias(libs.plugins.aboutLibraries) alias(libs.plugins.aboutLibraries)
alias(libs.plugins.openapi.generator)
} }
val isCI = if (System.getenv("CI") != null) System.getenv("CI").toBoolean() else false val isCI = providers.environmentVariable("CI").orElse("false").map { it.toBoolean() }
val shouldSign = isCI && System.getenv("KEY_ALIAS") != null val shouldSign =
isCI.zip(
fun getVersionCode(): Int { providers.environmentVariable("KEY_ALIAS").orElse("").map { it.isNotBlank() },
val stdout = ByteArrayOutputStream() ) { isCI, hasKey ->
exec { isCI && hasKey
commandLine = listOf("git", "tag", "--list", "v*")
standardOutput = stdout
} }
return stdout val ffmpegModuleExists =
.toString() providers.provider { project.file("libs/lib-decoder-ffmpeg-release.aar").exists() }
.trim() val av1ModuleExists =
.lines() providers.provider { project.file("libs/lib-decoder-av1-release.aar").exists() }
.size val mpvModuleExists =
} providers.provider { project.file("libs/wholphin-mpv-release.aar").exists() }
val extensionsRepoActive =
providers.provider { project.hasProperty("WholphinExtensionsUsername") }
fun getAppVersion(): String { // See https://issuetracker.google.com/issues/402800800
val stdout = ByteArrayOutputStream() val isBuildingBundle =
exec { providers.provider {
commandLine = listOf("git", "describe", "--tags", "--long", "--match=v*") gradle.startParameter.taskNames.any { it.lowercase().contains("bundle") }
standardOutput = stdout }
val gitTags =
providers
.exec { commandLine("git", "tag", "--list", "v*", "p*") }
.standardOutput.asText
.get()
val gitDescribe =
providers
.exec { commandLine("git", "describe", "--tags", "--long", "--match=v*") }
.standardOutput.asText
.getOrElse("v0.0.0")
kotlin {
compilerOptions {
languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_3
jvmTarget = JvmTarget.JVM_11
javaParameters = true
// Workaround for https://github.com/google/ksp/issues/2964
moduleName = "${project.group}_${project.name}"
} }
return stdout
.toString()
.trim()
.removePrefix("v")
.ifBlank { "0.0.0" }
} }
android { private fun Provider<String>.getInt() = get().toInt()
configure<ApplicationExtension> {
namespace = "com.github.damontecres.wholphin" namespace = "com.github.damontecres.wholphin"
compileSdk = 36 compileSdk = libs.versions.compileSdk.getInt()
defaultConfig { defaultConfig {
applicationId = "com.github.damontecres.wholphin" applicationId = "org.justinvisser.wolfskin.tv"
minSdk = 25 minSdk = libs.versions.minSdk.getInt()
targetSdk = 36 targetSdk = libs.versions.targetSdk.getInt()
versionCode = getVersionCode() versionCode = gitTags.trim().lines().size
versionName = getAppVersion() versionName = gitDescribe.trim().removePrefix("v").ifBlank { "0.0.0" }
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "com.github.damontecres.wholphin.test.WholphinTestRunner"
buildConfigField("long", "BUILD_TIME", System.currentTimeMillis().toString())
buildConfigField("String", "CONDUCTARR_BASE_URL", "\"https://wolfskin-tv.justinvisser.org\"")
} }
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
isDebuggable = false
}
debug {
isMinifyEnabled = false
isDebuggable = true
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
isCoreLibraryDesugaringEnabled = true
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
buildConfig = true
compose = true
}
room {
schemaDirectory("$projectDir/schemas")
}
signingConfigs { signingConfigs {
if (shouldSign) { if (shouldSign.get()) {
create("ci") { create("ci") {
file("ci.keystore").writeBytes( file("ci.keystore").writeBytes(
Base64.getDecoder().decode(System.getenv("SIGNING_KEY")), Base64.getDecoder().decode(System.getenv("SIGNING_KEY")),
@ -105,15 +102,17 @@ android {
} }
} }
} }
buildTypes { buildTypes {
release { release {
isMinifyEnabled = false isMinifyEnabled = true
isShrinkResources = true
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro", "proguard-rules.pro",
) )
if (shouldSign) { isDebuggable = false
if (shouldSign.get()) {
signingConfig = signingConfigs.getByName("ci") signingConfig = signingConfigs.getByName("ci")
} else { } else {
val localPropertiesFile = project.rootProject.file("local.properties") val localPropertiesFile = project.rootProject.file("local.properties")
@ -127,23 +126,170 @@ android {
} }
} }
} }
debug { debug {
if (shouldSign) { isMinifyEnabled = false
signingConfig = signingConfigs.getByName("ci") isShrinkResources = false
} isDebuggable = true
applicationIdSuffix = ".debug"
} }
applicationVariants.all { create("optimized") {
val variant = this initWith(getByName("release"))
variant.outputs signingConfig = signingConfigs.getByName("debug")
.map { it as com.android.build.gradle.internal.api.BaseVariantOutputImpl } applicationIdSuffix = ".opt"
.forEach { output -> matchingFallbacks += "release"
val outputFileName =
"Wholphin-${variant.baseName}-${variant.versionName}-${variant.versionCode}.apk"
output.outputFileName = outputFileName
}
} }
} }
flavorDimensions += "version"
productFlavors {
val featureLeanback = "leanback"
val featureUpdate = "UPDATING_ENABLED"
val featureDiscover = "DISCOVER_ENABLED"
fun ProductFlavor.setFeatureFlag(
name: String,
enabled: Boolean,
) {
this.buildConfigField("boolean", name, "Boolean.parseBoolean(\"${enabled}\")")
}
create("default") {
dimension = "version"
isDefault = true
manifestPlaceholders += mapOf(featureLeanback to false)
setFeatureFlag(featureUpdate, true)
setFeatureFlag(featureDiscover, true)
}
create("appstore") {
dimension = "version"
manifestPlaceholders += mapOf(featureLeanback to true)
setFeatureFlag(featureUpdate, false)
setFeatureFlag(featureDiscover, true)
}
create("firetv") {
dimension = "version"
manifestPlaceholders += mapOf(featureLeanback to true)
setFeatureFlag(featureUpdate, false)
setFeatureFlag(featureDiscover, false)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
isCoreLibraryDesugaringEnabled = true
}
buildFeatures {
buildConfig = true
compose = true
}
splits {
abi {
// Disable split abis when building bundles
isEnable = !isBuildingBundle.get()
reset()
include("armeabi-v7a", "arm64-v8a", "x86_64")
isUniversalApk = true
}
}
packaging {
jniLibs {
// Work around because libass-android & wholphin-mpv both (incorrectly) package libc++_shared.so
pickFirsts += "lib/*/libc++_shared.so"
}
}
sourceSets {
getByName("main") {
kotlin.directories += "$buildDir/generated/seerr_api/src/main/kotlin"
kotlin.directories += "src/wolfskin/java"
}
getByName("optimized") {
kotlin.directories += "src/release/java"
baselineProfiles.directories += "src/optimized/baselineProfiles"
}
}
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
lint {
disable.add("MissingTranslation")
}
androidResources {
generateLocaleConfig = true
}
}
androidComponents {
onVariants(selector().all()) { variant ->
variant.outputs
.map { it as com.android.build.api.variant.impl.VariantOutputImpl }
.forEach { output ->
val abi =
output
.getFilter(FilterConfiguration.FilterType.ABI)
.let { if (it != null) "-${it.identifier}" else "" }
val outputFileName =
"Wholphin-${variant.flavorName}-${variant.buildType}-${output.versionName.get()}-${output.versionCode.get()}$abi.apk"
output.outputFileName = outputFileName
}
if (variant.name == "defaultOptimized") {
tasks.withType<Test>().configureEach {
systemProperty("wolfskin.optimizedVersionName", variant.outputs.first().versionName.get())
}
}
if (variant.name == "defaultDebug") {
val packagedApks = variant.artifacts.get(SingleArtifact.APK)
val artifactLoader = variant.artifacts.getBuiltArtifactsLoader()
val aapt2 = sdkComponents.aapt2.flatMap { it.executable }
val checkWolfskinBranding = tasks.register("checkWolfskinBranding") {
inputs.dir(packagedApks)
inputs.file(aapt2)
doLast {
val artifacts = checkNotNull(artifactLoader.load(packagedApks.get())) {
"Packaged default/debug APK metadata was not found; branding is unverified"
}.elements
check(artifacts.isNotEmpty()) { "No default/debug APKs were found; branding is unverified" }
val header = Regex("""^\s*resource 0x[\da-fA-F]+ (?:[^\s:]+:)?([^\s/]+)/([^\s]+)""")
val visibleTypes = setOf("string", "plurals", "array")
// Upstream legal attribution and its project URL must survive the branding cutover.
val legalNames = setOf("wolfskin_upstream_credit", "wolfskin_upstream_url")
val violations = sortedSetOf<String>()
artifacts.forEach { artifact ->
val dump = providers.exec {
commandLine(aapt2.get().asFile, "dump", "resources", artifact.outputFile)
}.standardOutput.asText.get()
var currentName: String? = null
var inspected = 0
dump.lineSequence().forEach { line ->
val resource = header.find(line)
if (resource != null) {
currentName = resource.groupValues[2].takeIf { resource.groupValues[1] in visibleTypes }
if (currentName != null) inspected++
} else if (currentName != null && currentName !in legalNames && line.contains("Wholphin", ignoreCase = true)) {
violations += "${File(artifact.outputFile).name}:$currentName"
}
}
check(inspected > 0) { "No packaged string resources were decoded; branding is unverified" }
logger.lifecycle("Inspected $inspected string/plural/array resources in ${File(artifact.outputFile).name}")
}
check(violations.isEmpty()) { "User-visible upstream branding remains: ${violations.joinToString()}" }
logger.lifecycle("Wolfskin branding check passed (${artifacts.size} packaged debug APKs; legal attribution preserved)")
}
}
tasks.matching { it.name == "assembleDefaultDebug" }.configureEach { dependsOn(checkWolfskinBranding) }
}
}
}
room {
schemaDirectory("$projectDir/schemas")
} }
protobuf { protobuf {
@ -174,7 +320,37 @@ aboutLibraries {
duplicationRule = DuplicateRule.SIMPLE duplicationRule = DuplicateRule.SIMPLE
} }
} }
openApiGenerate {
generatorName.set("kotlin")
inputSpec.set("$projectDir/src/main/seerr/seerr-api.yml")
templateDir.set("$projectDir/src/main/seerr/templates")
outputDir.set("$buildDir/generated/seerr_api")
apiPackage.set("com.github.damontecres.wholphin.api.seerr")
modelPackage.set("com.github.damontecres.wholphin.api.seerr.model")
groupId.set("com.github.damontecres.wholphin.api.seerr")
id.set("seerr-api")
packageName.set("com.github.damontecres.wholphin.api.seerr")
additionalProperties.apply {
put("serializationLibrary", "kotlinx_serialization")
put("sortModelPropertiesByRequiredFlag", true)
put("sortParamsByRequiredFlag", true)
put("useCoroutines", true)
put("enumPropertyNaming", "UPPERCASE")
put("modelMutable", false)
// Note: this is only for downloading files, so it's not necessary to enable
put("supportAndroidApiLevel25AndBelow", false)
}
}
tasks.named("preBuild") {
dependsOn.add(tasks.named("openApiGenerate"))
}
dependencies { dependencies {
implementation(project(":wolfskin-design"))
implementation(project(":conductarr-client"))
implementation(libs.androidx.core.ktx) implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat) implementation(libs.androidx.appcompat)
implementation(platform(libs.androidx.compose.bom)) implementation(platform(libs.androidx.compose.bom))
@ -182,19 +358,27 @@ dependencies {
implementation(libs.androidx.compose.ui.graphics) implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview) implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.runtime) implementation(libs.androidx.compose.runtime)
implementation(libs.androidx.compose.runtime.livedata)
implementation(libs.androidx.tv.foundation) implementation(libs.androidx.tv.foundation)
implementation(libs.androidx.tv.material) implementation(libs.androidx.tv.material)
implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose) implementation(libs.androidx.activity.compose)
implementation(libs.androidx.datastore) implementation(libs.androidx.datastore)
implementation(libs.androidx.datastore.preferences)
implementation(libs.protobuf.kotlin.lite) implementation(libs.protobuf.kotlin.lite)
implementation(libs.androidx.tvprovider)
implementation(libs.androidx.work.runtime.ktx)
implementation(libs.androidx.hilt.work)
implementation(libs.androidx.profileinstaller)
implementation(libs.androidx.media3.exoplayer) implementation(libs.androidx.media3.exoplayer)
implementation(libs.androidx.media3.session)
implementation(libs.androidx.media3.datasource.okhttp) implementation(libs.androidx.media3.datasource.okhttp)
implementation(libs.androidx.media3.exoplayer.hls) implementation(libs.androidx.media3.exoplayer.hls)
implementation(libs.androidx.media3.exoplayer.dash)
implementation(libs.androidx.media3.ui) implementation(libs.androidx.media3.ui)
implementation(libs.androidx.media3.ui.compose) implementation(libs.androidx.media3.ui.compose)
implementation(libs.ass.media)
implementation(libs.exoplayerhdrutils)
implementation(libs.coil.core) implementation(libs.coil.core)
implementation(libs.coil.compose) implementation(libs.coil.compose)
@ -218,14 +402,18 @@ dependencies {
implementation(libs.androidx.room.common.jvm) implementation(libs.androidx.room.common.jvm)
implementation(libs.androidx.room.ktx) implementation(libs.androidx.room.ktx)
implementation(libs.androidx.compose.material3) implementation(libs.androidx.compose.material3)
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
implementation(libs.androidx.hilt.navigation.compose) implementation(libs.androidx.hilt.navigation.compose)
implementation(libs.androidx.preference.ktx) implementation(libs.androidx.preference.ktx)
implementation(libs.androidx.room.testing) implementation(libs.androidx.room.testing)
implementation(libs.androidx.palette.ktx)
implementation(libs.androidx.media3.effect)
implementation(libs.androidx.runner)
ksp(libs.androidx.room.compiler) ksp(libs.androidx.room.compiler)
ksp(libs.hilt.android.compiler) ksp(libs.hilt.android.compiler)
ksp(libs.androidx.hilt.compiler)
implementation(libs.timber) implementation(libs.timber)
implementation(libs.slf4j2.timber)
implementation(libs.aboutlibraries.core) implementation(libs.aboutlibraries.core)
implementation(libs.aboutlibraries.compose.m3) implementation(libs.aboutlibraries.compose.m3)
implementation(libs.multiplatform.markdown.renderer) implementation(libs.multiplatform.markdown.renderer)
@ -236,10 +424,64 @@ dependencies {
implementation(libs.acra.limiter) implementation(libs.acra.limiter)
compileOnly(libs.auto.service.annotations) compileOnly(libs.auto.service.annotations)
ksp(libs.auto.service.ksp) ksp(libs.auto.service.ksp)
implementation(platform(libs.okhttp.bom))
implementation(libs.okhttp)
implementation(libs.kache)
implementation(libs.kache.file)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
coreLibraryDesugaring(libs.desugar.jdk.libs) coreLibraryDesugaring(libs.desugar.jdk.libs)
if (ffmpegModuleExists.get()) {
logger.info("Using local ffmpeg decoder")
implementation(files("libs/lib-decoder-ffmpeg-release.aar"))
} else if (extensionsRepoActive.get()) {
logger.info("Using prebuilt ffmpeg decoder")
implementation(libs.wholphin.extensions.ffmpeg)
} else {
logger.warn("Media3 ffmpeg decoder was NOT found")
}
if (av1ModuleExists.get()) {
logger.info("Using local av1 decoder")
implementation(files("libs/lib-decoder-av1-release.aar"))
} else if (extensionsRepoActive.get()) {
logger.info("Using prebuilt av1 decoder")
implementation(libs.wholphin.extensions.av1)
} else {
logger.warn("Media3 av1 decoder was NOT found")
}
if (mpvModuleExists.get()) {
logger.info("Using local libMPV build")
implementation(files("libs/wholphin-mpv-release.aar"))
} else if (extensionsRepoActive.get()) {
logger.info("Using prebuilt libMPV")
implementation(libs.wholphin.extensions.mpv)
} else {
logger.warn("libMPV was NOT found, using stub library")
implementation(project(":wholphin-mpv-stub"))
}
debugImplementation(libs.androidx.compose.ui.test.manifest)
debugImplementation(libs.androidx.compose.ui.tooling)
testImplementation(libs.mockk.android)
testImplementation(libs.mockk.agent)
testImplementation(libs.kotlinx.coroutines.test)
testImplementation("com.squareup.okhttp3:mockwebserver3")
testImplementation(libs.androidx.core.testing)
testImplementation(libs.robolectric)
testImplementation(libs.hilt.android.testing)
testImplementation(libs.androidx.compose.ui.test.junit4)
testImplementation("io.github.takahirom.roborazzi:roborazzi:1.77.0")
testImplementation("io.github.takahirom.roborazzi:roborazzi-compose:1.77.0")
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.mockk.android)
androidTestImplementation(libs.hilt.android.testing)
androidTestImplementation(libs.androidx.compose.ui.test.manifest)
}
tasks.withType<Test>().configureEach {
dependsOn(":wolfskin-design:testDebugUnitTest")
systemProperty("robolectric.dependency.dir", project(":wolfskin-design").layout.buildDirectory.dir("robolectric-sdk").get().asFile.absolutePath)
systemProperty("robolectric.offline", "true")
systemProperty("roborazzi.test.record", "true")
systemProperty("wolfskin.captureDirectory", rootProject.layout.buildDirectory.dir("wolfskin-captures").get().asFile.absolutePath)
} }

View file

@ -0,0 +1,11 @@
{
"uniqueId": "com.github.videolan.dav1d",
"developers": [],
"artifactVersion": "master",
"description": "dav1d is an AV1 cross-platform decoder, open-source, and focused on speed and correctness",
"name": "dav1d",
"licenses": [
"dav1d"
],
"website": "https://github.com/videolan/dav1d"
}

View file

@ -0,0 +1,11 @@
{
"uniqueId": "org.ffmpeg",
"developers": [],
"artifactVersion": "6.0",
"description": "A complete, cross-platform solution to record, convert and stream audio and video",
"name": "FFmpeg",
"licenses": [
"lgpl-2.1"
],
"website": "https://www.ffmpeg.org"
}

View file

@ -0,0 +1,11 @@
{
"uniqueId": "com.github.fribidi.fribidi",
"developers": [],
"artifactVersion": "1.0.16",
"description": "The Free Implementation of the Unicode Bidirectional Algorithm",
"name": "fribidi",
"licenses": [
"lgpl-2.1"
],
"website": "https://github.com/fribidi/fribidi"
}

View file

@ -0,0 +1,11 @@
{
"uniqueId": "com.github.libass.libass",
"developers": [],
"artifactVersion": "master",
"description": "libass is a portable subtitle renderer for the ASS/SSA (Advanced Substation Alpha/Substation Alpha) subtitle format",
"name": "libass",
"licenses": [
"libass"
],
"website": "https://github.com/libass/libass"
}

View file

@ -0,0 +1,11 @@
{
"uniqueId": "com.github.haasn.libplacebo",
"developers": [],
"artifactVersion": "master",
"description": "libplacebo is, in a nutshell, the core rendering algorithms and ideas of mpv rewritten as an independent library",
"name": "libplacebo",
"licenses": [
"lgpl-2.1"
],
"website": "https://github.com/haasn/libplacebo"
}

View file

@ -0,0 +1,11 @@
{
"uniqueId": "com.github.adah1972.libunibreak",
"developers": [],
"artifactVersion": "6.1",
"description": "an implementation of the line breaking and word/grapheme breaking algorithms as described in Unicode Standard Annex 14 (UAX #14) and Unicode Standard Annex 29 (UAX #29)",
"name": "libunibreak",
"licenses": [
"libunibreak"
],
"website": "https://github.com/adah1972/libunibreak"
}

View file

@ -0,0 +1,11 @@
{
"uniqueId": "com.github.Mbed-TLS.mbedtls",
"developers": [],
"artifactVersion": "v3.6.4",
"description": "Mbed TLS is a C library that implements X.509 certificate manipulation and the TLS and DTLS protocols",
"name": "mbedtls",
"licenses": [
"gpl-2.0"
],
"website": "https://github.com/Mbed-TLS/mbedtls"
}

View file

@ -0,0 +1,11 @@
{
"uniqueId": "com.github.mpv-player.mpv",
"developers": [],
"artifactVersion": "master",
"description": "mpv is a free (as in freedom) media player for the command line",
"name": "mpv",
"licenses": [
"gpl-2.0"
],
"website": "https://github.com/mpv-player/mpv"
}

View file

@ -0,0 +1,11 @@
{
"uniqueId": "com.github.mpv-android.mpv-android",
"developers": [],
"artifactVersion": "master",
"description": "mpv-android is a video player for Android based on libmpv",
"name": "mpv for Android",
"licenses": [
"mpv-android"
],
"website": "https://github.com/mpv-android/mpv-android"
}

View file

@ -0,0 +1,11 @@
{
"uniqueId": "org.freetype",
"developers": [],
"artifactVersion": "2.14.1",
"description": "FreeType is a freely available software library to render fonts",
"name": "freetype",
"licenses": [
"gpl-2.0"
],
"website": "https://freetype.org"
}

View file

@ -0,0 +1,28 @@
{
"content": "Copyright © 2018-2025, VideoLAN and dav1d authors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"hash": "dav1d",
"url": "https://github.com/videolan/dav1d?tab=BSD-2-Clause-1-ov-file",
"name": "BSD-2-Clause"
}

View file

@ -0,0 +1,507 @@
{
"content": "GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the \"Lesser\" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
\"work based on the library\" and a \"work that uses the library\". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called \"this License\").
Each licensee is addressed as \"you\".
A \"library\" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The \"Library\", below, refers to any such software library or work
which has been distributed under these terms. A \"work based on the
Library\" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term \"modification\".)
\"Source code\" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a \"work that uses the Library\". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a \"work that uses the Library\" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a \"work that uses the
library\". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a \"work that uses the Library\" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a \"work that uses the Library\" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable \"work that
uses the Library\", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the \"work that uses the
Library\" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
\"any later version\", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY \"AS IS\" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
\"copyright\" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a \"copyright disclaimer\" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!",
"hash": "lgpl-2.1",
"url": "https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html",
"name": "GNU Lesser General Public License, version 2.1"
}

View file

@ -0,0 +1,20 @@
{
"content": "ISC License
Copyright (C) 2006-2016 libass contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.",
"hash": "libass",
"url": "https://github.com/libass/libass?tab=ISC-1-ov-file",
"name": "ISC"
}

View file

@ -0,0 +1,26 @@
{
"content": "Copyright (C) Wu Yongwei <wuyongwei at gmail dot com>
Copyright (C) Tom Hacohen <tom at stosb dot com>
Copyright (C) Petr Filipsky <philodej at gmail dot com>
Copyright (C) Andreas Röver <roever at users dot sf dot net>
This software is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgement in the product documentation would
be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.",
"hash": "libunibreak",
"url": "https://github.com/adah1972/libunibreak?tab=Zlib-1-ov-file",
"name": "Zlib"
}

View file

@ -0,0 +1,13 @@
{
"content": "Copyright (c) 2016 Ilya Zhuravlev
Copyright (c) 2016 sfan5 <sfan5@live.de>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
"hash": "mpv-android",
"url": "https://github.com/mpv-android/mpv-android?tab=MIT-1-ov-file",
"name": "MIT"
}

View file

@ -1,21 +1,27 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
-keepattributes SourceFile,LineNumberTable -keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to -keep class com.github.damontecres.wholphin.mpv.MPVLib { *; }
# hide the original source file name.
#-renamesourcefileattribute SourceFile -keep class * extends com.google.protobuf.GeneratedMessageLite { *; }
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite {
<fields>;
<methods>;
}
-dontwarn com.google.protobuf.**
# TODO investigate using smaller scope
-keep class com.google.common.cache.** { *; }
# Media3SubtitleOverride uses reflection to access/modify these fields
-keep class androidx.media3.ui.SubtitleView {
private androidx.media3.ui.SubtitleView$Output output;
}
-keep class androidx.media3.ui.CanvasSubtitleOutput {
private final java.util.List painters;
}
-keep class androidx.media3.ui.SubtitlePainter {
private final float outlineWidth;
}
# Keep external/native functions
-keep class com.suyashbelekar.exoplayerhdrutils.libdovi.LibDovi { *; }

View file

@ -0,0 +1,325 @@
{
"formatVersion": 1,
"database": {
"version": 10,
"identityHash": "addc8fcbddd84a88e20fbda34849cf26",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, `pin` TEXT, FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
},
{
"fieldPath": "pin",
"columnName": "pin",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', `viewOptions` TEXT, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
},
{
"fieldPath": "viewOptions",
"columnName": "viewOptions",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'addc8fcbddd84a88e20fbda34849cf26')"
]
}
}

View file

@ -0,0 +1,383 @@
{
"formatVersion": 1,
"database": {
"version": 11,
"identityHash": "26bbe361923e3aac064c08c52e65264e",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, `pin` TEXT, FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
},
{
"fieldPath": "pin",
"columnName": "pin",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', `viewOptions` TEXT, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
},
{
"fieldPath": "viewOptions",
"columnName": "viewOptions",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "PlaybackLanguageChoice",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `seriesId` TEXT NOT NULL, `itemId` TEXT, `audioLanguage` TEXT, `subtitleLanguage` TEXT, `subtitlesDisabled` INTEGER, PRIMARY KEY(`userId`, `seriesId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "seriesId",
"columnName": "seriesId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT"
},
{
"fieldPath": "audioLanguage",
"columnName": "audioLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitleLanguage",
"columnName": "subtitleLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitlesDisabled",
"columnName": "subtitlesDisabled",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"seriesId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '26bbe361923e3aac064c08c52e65264e')"
]
}
}

View file

@ -0,0 +1,434 @@
{
"formatVersion": 1,
"database": {
"version": 12,
"identityHash": "e9bcae45448f995d1f4ebf291e45c510",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, `pin` TEXT, FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
},
{
"fieldPath": "pin",
"columnName": "pin",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', `viewOptions` TEXT, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
},
{
"fieldPath": "viewOptions",
"columnName": "viewOptions",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "PlaybackLanguageChoice",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `seriesId` TEXT NOT NULL, `itemId` TEXT, `audioLanguage` TEXT, `subtitleLanguage` TEXT, `subtitlesDisabled` INTEGER, PRIMARY KEY(`userId`, `seriesId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "seriesId",
"columnName": "seriesId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT"
},
{
"fieldPath": "audioLanguage",
"columnName": "audioLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitleLanguage",
"columnName": "subtitleLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitlesDisabled",
"columnName": "subtitlesDisabled",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"seriesId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "ItemTrackModification",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `trackIndex` INTEGER NOT NULL, `delayMs` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`, `trackIndex`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "trackIndex",
"columnName": "trackIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "delayMs",
"columnName": "delayMs",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId",
"trackIndex"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e9bcae45448f995d1f4ebf291e45c510')"
]
}
}

View file

@ -0,0 +1,549 @@
{
"formatVersion": 1,
"database": {
"version": 20,
"identityHash": "dea51adcc724179afa0174d775f97480",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, `pin` TEXT, FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
},
{
"fieldPath": "pin",
"columnName": "pin",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', `viewOptions` TEXT, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
},
{
"fieldPath": "viewOptions",
"columnName": "viewOptions",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "PlaybackLanguageChoice",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `seriesId` TEXT NOT NULL, `itemId` TEXT, `audioLanguage` TEXT, `subtitleLanguage` TEXT, `subtitlesDisabled` INTEGER, PRIMARY KEY(`userId`, `seriesId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "seriesId",
"columnName": "seriesId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT"
},
{
"fieldPath": "audioLanguage",
"columnName": "audioLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitleLanguage",
"columnName": "subtitleLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitlesDisabled",
"columnName": "subtitlesDisabled",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"seriesId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "ItemTrackModification",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `trackIndex` INTEGER NOT NULL, `delayMs` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`, `trackIndex`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "trackIndex",
"columnName": "trackIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "delayMs",
"columnName": "delayMs",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId",
"trackIndex"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "seerr_servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `name` TEXT, `version` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_seerr_servers_url",
"unique": true,
"columnNames": [
"url"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_seerr_servers_url` ON `${TABLE_NAME}` (`url`)"
}
]
},
{
"tableName": "seerr_users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `serverId` INTEGER NOT NULL, `authMethod` TEXT NOT NULL, `username` TEXT, `password` TEXT, `credential` TEXT, PRIMARY KEY(`jellyfinUserRowId`, `serverId`), FOREIGN KEY(`serverId`) REFERENCES `seerr_servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`jellyfinUserRowId`) REFERENCES `users`(`rowId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "authMethod",
"columnName": "authMethod",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT"
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT"
},
{
"fieldPath": "credential",
"columnName": "credential",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"serverId"
]
},
"foreignKeys": [
{
"table": "seerr_servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"jellyfinUserRowId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'dea51adcc724179afa0174d775f97480')"
]
}
}

View file

@ -0,0 +1,635 @@
{
"formatVersion": 1,
"database": {
"version": 30,
"identityHash": "f9b86b73e972aa002238d43ab2c8dcc2",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, `pin` TEXT, FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
},
{
"fieldPath": "pin",
"columnName": "pin",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', `viewOptions` TEXT, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
},
{
"fieldPath": "viewOptions",
"columnName": "viewOptions",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "playback_effects",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `rotation` INTEGER NOT NULL, `brightness` INTEGER NOT NULL, `contrast` INTEGER NOT NULL, `saturation` INTEGER NOT NULL, `hue` INTEGER NOT NULL, `red` INTEGER NOT NULL, `green` INTEGER NOT NULL, `blue` INTEGER NOT NULL, `blur` INTEGER NOT NULL, PRIMARY KEY(`jellyfinUserRowId`, `itemId`, `type`))",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoFilter.rotation",
"columnName": "rotation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.brightness",
"columnName": "brightness",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.contrast",
"columnName": "contrast",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.saturation",
"columnName": "saturation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.hue",
"columnName": "hue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.red",
"columnName": "red",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.green",
"columnName": "green",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blue",
"columnName": "blue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blur",
"columnName": "blur",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"itemId",
"type"
]
}
},
{
"tableName": "PlaybackLanguageChoice",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `seriesId` TEXT NOT NULL, `itemId` TEXT, `audioLanguage` TEXT, `subtitleLanguage` TEXT, `subtitlesDisabled` INTEGER, PRIMARY KEY(`userId`, `seriesId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "seriesId",
"columnName": "seriesId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT"
},
{
"fieldPath": "audioLanguage",
"columnName": "audioLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitleLanguage",
"columnName": "subtitleLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitlesDisabled",
"columnName": "subtitlesDisabled",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"seriesId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "ItemTrackModification",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `trackIndex` INTEGER NOT NULL, `delayMs` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`, `trackIndex`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "trackIndex",
"columnName": "trackIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "delayMs",
"columnName": "delayMs",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId",
"trackIndex"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "seerr_servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `name` TEXT, `version` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_seerr_servers_url",
"unique": true,
"columnNames": [
"url"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_seerr_servers_url` ON `${TABLE_NAME}` (`url`)"
}
]
},
{
"tableName": "seerr_users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `serverId` INTEGER NOT NULL, `authMethod` TEXT NOT NULL, `username` TEXT, `password` TEXT, `credential` TEXT, PRIMARY KEY(`jellyfinUserRowId`, `serverId`), FOREIGN KEY(`serverId`) REFERENCES `seerr_servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`jellyfinUserRowId`) REFERENCES `users`(`rowId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "authMethod",
"columnName": "authMethod",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT"
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT"
},
{
"fieldPath": "credential",
"columnName": "credential",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"serverId"
]
},
"foreignKeys": [
{
"table": "seerr_servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"jellyfinUserRowId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f9b86b73e972aa002238d43ab2c8dcc2')"
]
}
}

View file

@ -0,0 +1,642 @@
{
"formatVersion": 1,
"database": {
"version": 31,
"identityHash": "c6829d764ec85321ab3be9905d6c0e3a",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, `pin` TEXT, FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
},
{
"fieldPath": "pin",
"columnName": "pin",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `order` INTEGER NOT NULL DEFAULT -1, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "-1"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', `viewOptions` TEXT, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
},
{
"fieldPath": "viewOptions",
"columnName": "viewOptions",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "playback_effects",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `rotation` INTEGER NOT NULL, `brightness` INTEGER NOT NULL, `contrast` INTEGER NOT NULL, `saturation` INTEGER NOT NULL, `hue` INTEGER NOT NULL, `red` INTEGER NOT NULL, `green` INTEGER NOT NULL, `blue` INTEGER NOT NULL, `blur` INTEGER NOT NULL, PRIMARY KEY(`jellyfinUserRowId`, `itemId`, `type`))",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoFilter.rotation",
"columnName": "rotation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.brightness",
"columnName": "brightness",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.contrast",
"columnName": "contrast",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.saturation",
"columnName": "saturation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.hue",
"columnName": "hue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.red",
"columnName": "red",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.green",
"columnName": "green",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blue",
"columnName": "blue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blur",
"columnName": "blur",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"itemId",
"type"
]
}
},
{
"tableName": "PlaybackLanguageChoice",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `seriesId` TEXT NOT NULL, `itemId` TEXT, `audioLanguage` TEXT, `subtitleLanguage` TEXT, `subtitlesDisabled` INTEGER, PRIMARY KEY(`userId`, `seriesId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "seriesId",
"columnName": "seriesId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT"
},
{
"fieldPath": "audioLanguage",
"columnName": "audioLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitleLanguage",
"columnName": "subtitleLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitlesDisabled",
"columnName": "subtitlesDisabled",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"seriesId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "ItemTrackModification",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `trackIndex` INTEGER NOT NULL, `delayMs` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`, `trackIndex`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "trackIndex",
"columnName": "trackIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "delayMs",
"columnName": "delayMs",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId",
"trackIndex"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "seerr_servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `name` TEXT, `version` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_seerr_servers_url",
"unique": true,
"columnNames": [
"url"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_seerr_servers_url` ON `${TABLE_NAME}` (`url`)"
}
]
},
{
"tableName": "seerr_users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `serverId` INTEGER NOT NULL, `authMethod` TEXT NOT NULL, `username` TEXT, `password` TEXT, `credential` TEXT, PRIMARY KEY(`jellyfinUserRowId`, `serverId`), FOREIGN KEY(`serverId`) REFERENCES `seerr_servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`jellyfinUserRowId`) REFERENCES `users`(`rowId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "authMethod",
"columnName": "authMethod",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT"
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT"
},
{
"fieldPath": "credential",
"columnName": "credential",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"serverId"
]
},
"foreignKeys": [
{
"table": "seerr_servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"jellyfinUserRowId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c6829d764ec85321ab3be9905d6c0e3a')"
]
}
}

View file

@ -0,0 +1,659 @@
{
"formatVersion": 1,
"database": {
"version": 32,
"identityHash": "934c38e7f4eb68c9a223ddb5c7e3e35d",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, `lastUsed` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
},
{
"fieldPath": "lastUsed",
"columnName": "lastUsed",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, `pin` TEXT, `requireLogin` INTEGER NOT NULL DEFAULT false, `lastUsed` TEXT, FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
},
{
"fieldPath": "pin",
"columnName": "pin",
"affinity": "TEXT"
},
{
"fieldPath": "requireLogin",
"columnName": "requireLogin",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "false"
},
{
"fieldPath": "lastUsed",
"columnName": "lastUsed",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `order` INTEGER NOT NULL DEFAULT -1, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "-1"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', `viewOptions` TEXT, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
},
{
"fieldPath": "viewOptions",
"columnName": "viewOptions",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "playback_effects",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `rotation` INTEGER NOT NULL, `brightness` INTEGER NOT NULL, `contrast` INTEGER NOT NULL, `saturation` INTEGER NOT NULL, `hue` INTEGER NOT NULL, `red` INTEGER NOT NULL, `green` INTEGER NOT NULL, `blue` INTEGER NOT NULL, `blur` INTEGER NOT NULL, PRIMARY KEY(`jellyfinUserRowId`, `itemId`, `type`))",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoFilter.rotation",
"columnName": "rotation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.brightness",
"columnName": "brightness",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.contrast",
"columnName": "contrast",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.saturation",
"columnName": "saturation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.hue",
"columnName": "hue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.red",
"columnName": "red",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.green",
"columnName": "green",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blue",
"columnName": "blue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blur",
"columnName": "blur",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"itemId",
"type"
]
}
},
{
"tableName": "PlaybackLanguageChoice",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `seriesId` TEXT NOT NULL, `itemId` TEXT, `audioLanguage` TEXT, `subtitleLanguage` TEXT, `subtitlesDisabled` INTEGER, PRIMARY KEY(`userId`, `seriesId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "seriesId",
"columnName": "seriesId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT"
},
{
"fieldPath": "audioLanguage",
"columnName": "audioLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitleLanguage",
"columnName": "subtitleLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitlesDisabled",
"columnName": "subtitlesDisabled",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"seriesId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "ItemTrackModification",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `trackIndex` INTEGER NOT NULL, `delayMs` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`, `trackIndex`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "trackIndex",
"columnName": "trackIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "delayMs",
"columnName": "delayMs",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId",
"trackIndex"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "seerr_servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `name` TEXT, `version` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_seerr_servers_url",
"unique": true,
"columnNames": [
"url"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_seerr_servers_url` ON `${TABLE_NAME}` (`url`)"
}
]
},
{
"tableName": "seerr_users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `serverId` INTEGER NOT NULL, `authMethod` TEXT NOT NULL, `username` TEXT, `password` TEXT, `credential` TEXT, PRIMARY KEY(`jellyfinUserRowId`, `serverId`), FOREIGN KEY(`serverId`) REFERENCES `seerr_servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`jellyfinUserRowId`) REFERENCES `users`(`rowId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "authMethod",
"columnName": "authMethod",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT"
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT"
},
{
"fieldPath": "credential",
"columnName": "credential",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"serverId"
]
},
"foreignKeys": [
{
"table": "seerr_servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"jellyfinUserRowId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '934c38e7f4eb68c9a223ddb5c7e3e35d')"
]
}
}

View file

@ -0,0 +1,664 @@
{
"formatVersion": 1,
"database": {
"version": 33,
"identityHash": "a65eec85c027fac13948a0dd6f99d3cb",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, `lastUsed` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
},
{
"fieldPath": "lastUsed",
"columnName": "lastUsed",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, `pin` TEXT, `requireLogin` INTEGER NOT NULL DEFAULT false, `lastUsed` TEXT, `uiLanguage` TEXT, FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
},
{
"fieldPath": "pin",
"columnName": "pin",
"affinity": "TEXT"
},
{
"fieldPath": "requireLogin",
"columnName": "requireLogin",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "false"
},
{
"fieldPath": "lastUsed",
"columnName": "lastUsed",
"affinity": "TEXT"
},
{
"fieldPath": "uiLanguage",
"columnName": "uiLanguage",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `order` INTEGER NOT NULL DEFAULT -1, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "-1"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', `viewOptions` TEXT, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
},
{
"fieldPath": "viewOptions",
"columnName": "viewOptions",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "playback_effects",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `rotation` INTEGER NOT NULL, `brightness` INTEGER NOT NULL, `contrast` INTEGER NOT NULL, `saturation` INTEGER NOT NULL, `hue` INTEGER NOT NULL, `red` INTEGER NOT NULL, `green` INTEGER NOT NULL, `blue` INTEGER NOT NULL, `blur` INTEGER NOT NULL, PRIMARY KEY(`jellyfinUserRowId`, `itemId`, `type`))",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoFilter.rotation",
"columnName": "rotation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.brightness",
"columnName": "brightness",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.contrast",
"columnName": "contrast",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.saturation",
"columnName": "saturation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.hue",
"columnName": "hue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.red",
"columnName": "red",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.green",
"columnName": "green",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blue",
"columnName": "blue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blur",
"columnName": "blur",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"itemId",
"type"
]
}
},
{
"tableName": "PlaybackLanguageChoice",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `seriesId` TEXT NOT NULL, `itemId` TEXT, `audioLanguage` TEXT, `subtitleLanguage` TEXT, `subtitlesDisabled` INTEGER, PRIMARY KEY(`userId`, `seriesId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "seriesId",
"columnName": "seriesId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT"
},
{
"fieldPath": "audioLanguage",
"columnName": "audioLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitleLanguage",
"columnName": "subtitleLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitlesDisabled",
"columnName": "subtitlesDisabled",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"seriesId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "ItemTrackModification",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `trackIndex` INTEGER NOT NULL, `delayMs` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`, `trackIndex`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "trackIndex",
"columnName": "trackIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "delayMs",
"columnName": "delayMs",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId",
"trackIndex"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "seerr_servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `name` TEXT, `version` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_seerr_servers_url",
"unique": true,
"columnNames": [
"url"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_seerr_servers_url` ON `${TABLE_NAME}` (`url`)"
}
]
},
{
"tableName": "seerr_users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `serverId` INTEGER NOT NULL, `authMethod` TEXT NOT NULL, `username` TEXT, `password` TEXT, `credential` TEXT, PRIMARY KEY(`jellyfinUserRowId`, `serverId`), FOREIGN KEY(`serverId`) REFERENCES `seerr_servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`jellyfinUserRowId`) REFERENCES `users`(`rowId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "authMethod",
"columnName": "authMethod",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT"
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT"
},
{
"fieldPath": "credential",
"columnName": "credential",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"serverId"
]
},
"foreignKeys": [
{
"table": "seerr_servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"jellyfinUserRowId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a65eec85c027fac13948a0dd6f99d3cb')"
]
}
}

View file

@ -0,0 +1,720 @@
{
"formatVersion": 1,
"database": {
"version": 34,
"identityHash": "278e485064fc9f184a2181599f0f8efb",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, `lastUsed` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
},
{
"fieldPath": "lastUsed",
"columnName": "lastUsed",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, `pin` TEXT, `requireLogin` INTEGER NOT NULL DEFAULT false, `lastUsed` TEXT, `uiLanguage` TEXT, FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
},
{
"fieldPath": "pin",
"columnName": "pin",
"affinity": "TEXT"
},
{
"fieldPath": "requireLogin",
"columnName": "requireLogin",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "false"
},
{
"fieldPath": "lastUsed",
"columnName": "lastUsed",
"affinity": "TEXT"
},
{
"fieldPath": "uiLanguage",
"columnName": "uiLanguage",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `order` INTEGER NOT NULL DEFAULT -1, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "-1"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', `viewOptions` TEXT, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
},
{
"fieldPath": "viewOptions",
"columnName": "viewOptions",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "playback_effects",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `rotation` INTEGER NOT NULL, `brightness` INTEGER NOT NULL, `contrast` INTEGER NOT NULL, `saturation` INTEGER NOT NULL, `hue` INTEGER NOT NULL, `red` INTEGER NOT NULL, `green` INTEGER NOT NULL, `blue` INTEGER NOT NULL, `blur` INTEGER NOT NULL, PRIMARY KEY(`jellyfinUserRowId`, `itemId`, `type`))",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoFilter.rotation",
"columnName": "rotation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.brightness",
"columnName": "brightness",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.contrast",
"columnName": "contrast",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.saturation",
"columnName": "saturation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.hue",
"columnName": "hue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.red",
"columnName": "red",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.green",
"columnName": "green",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blue",
"columnName": "blue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blur",
"columnName": "blur",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"itemId",
"type"
]
}
},
{
"tableName": "PlaybackLanguageChoice",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `seriesId` TEXT NOT NULL, `itemId` TEXT, `audioLanguage` TEXT, `subtitleLanguage` TEXT, `subtitlesDisabled` INTEGER, PRIMARY KEY(`userId`, `seriesId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "seriesId",
"columnName": "seriesId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT"
},
{
"fieldPath": "audioLanguage",
"columnName": "audioLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitleLanguage",
"columnName": "subtitleLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitlesDisabled",
"columnName": "subtitlesDisabled",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"seriesId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "ItemTrackModification",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `trackIndex` INTEGER NOT NULL, `delayMs` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`, `trackIndex`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "trackIndex",
"columnName": "trackIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "delayMs",
"columnName": "delayMs",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId",
"trackIndex"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "seerr_servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `name` TEXT, `version` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_seerr_servers_url",
"unique": true,
"columnNames": [
"url"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_seerr_servers_url` ON `${TABLE_NAME}` (`url`)"
}
]
},
{
"tableName": "seerr_users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `serverId` INTEGER NOT NULL, `authMethod` TEXT NOT NULL, `username` TEXT, `password` TEXT, `credential` TEXT, PRIMARY KEY(`jellyfinUserRowId`, `serverId`), FOREIGN KEY(`serverId`) REFERENCES `seerr_servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`jellyfinUserRowId`) REFERENCES `users`(`rowId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "authMethod",
"columnName": "authMethod",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT"
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT"
},
{
"fieldPath": "credential",
"columnName": "credential",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"serverId"
]
},
"foreignKeys": [
{
"table": "seerr_servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"jellyfinUserRowId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "RememberedTab",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `index` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "index",
"columnName": "index",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_RememberedTab_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_RememberedTab_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '278e485064fc9f184a2181599f0f8efb')"
]
}
}

View file

@ -0,0 +1,741 @@
{
"formatVersion": 1,
"database": {
"version": 35,
"identityHash": "ff9ade58d4ac39e36335ac0df1405a5f",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, `lastUsed` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
},
{
"fieldPath": "lastUsed",
"columnName": "lastUsed",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, `pin` TEXT, `requireLogin` INTEGER NOT NULL DEFAULT false, `lastUsed` TEXT, `uiLanguage` TEXT, `preferredAudioLanguage` TEXT NOT NULL DEFAULT '', `preferredSubtitleLanguage` TEXT NOT NULL DEFAULT '', `subtitleMode` TEXT NOT NULL DEFAULT 'USE_USER_PROFILE', FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
},
{
"fieldPath": "pin",
"columnName": "pin",
"affinity": "TEXT"
},
{
"fieldPath": "requireLogin",
"columnName": "requireLogin",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "false"
},
{
"fieldPath": "lastUsed",
"columnName": "lastUsed",
"affinity": "TEXT"
},
{
"fieldPath": "uiLanguage",
"columnName": "uiLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "appPreferences.preferredAudioLanguage",
"columnName": "preferredAudioLanguage",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "appPreferences.preferredSubtitleLanguage",
"columnName": "preferredSubtitleLanguage",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "appPreferences.subtitleMode",
"columnName": "subtitleMode",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'USE_USER_PROFILE'"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `order` INTEGER NOT NULL DEFAULT -1, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "-1"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', `viewOptions` TEXT, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
},
{
"fieldPath": "viewOptions",
"columnName": "viewOptions",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "playback_effects",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `rotation` INTEGER NOT NULL, `brightness` INTEGER NOT NULL, `contrast` INTEGER NOT NULL, `saturation` INTEGER NOT NULL, `hue` INTEGER NOT NULL, `red` INTEGER NOT NULL, `green` INTEGER NOT NULL, `blue` INTEGER NOT NULL, `blur` INTEGER NOT NULL, PRIMARY KEY(`jellyfinUserRowId`, `itemId`, `type`))",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoFilter.rotation",
"columnName": "rotation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.brightness",
"columnName": "brightness",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.contrast",
"columnName": "contrast",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.saturation",
"columnName": "saturation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.hue",
"columnName": "hue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.red",
"columnName": "red",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.green",
"columnName": "green",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blue",
"columnName": "blue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blur",
"columnName": "blur",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"itemId",
"type"
]
}
},
{
"tableName": "PlaybackLanguageChoice",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `seriesId` TEXT NOT NULL, `itemId` TEXT, `audioLanguage` TEXT, `subtitleLanguage` TEXT, `subtitlesDisabled` INTEGER, PRIMARY KEY(`userId`, `seriesId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "seriesId",
"columnName": "seriesId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT"
},
{
"fieldPath": "audioLanguage",
"columnName": "audioLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitleLanguage",
"columnName": "subtitleLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitlesDisabled",
"columnName": "subtitlesDisabled",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"seriesId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "ItemTrackModification",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `trackIndex` INTEGER NOT NULL, `delayMs` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`, `trackIndex`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "trackIndex",
"columnName": "trackIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "delayMs",
"columnName": "delayMs",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId",
"trackIndex"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "seerr_servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `name` TEXT, `version` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_seerr_servers_url",
"unique": true,
"columnNames": [
"url"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_seerr_servers_url` ON `${TABLE_NAME}` (`url`)"
}
]
},
{
"tableName": "seerr_users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `serverId` INTEGER NOT NULL, `authMethod` TEXT NOT NULL, `username` TEXT, `password` TEXT, `credential` TEXT, PRIMARY KEY(`jellyfinUserRowId`, `serverId`), FOREIGN KEY(`serverId`) REFERENCES `seerr_servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`jellyfinUserRowId`) REFERENCES `users`(`rowId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "authMethod",
"columnName": "authMethod",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT"
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT"
},
{
"fieldPath": "credential",
"columnName": "credential",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"serverId"
]
},
"foreignKeys": [
{
"table": "seerr_servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"jellyfinUserRowId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "RememberedTab",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `index` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "index",
"columnName": "index",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_RememberedTab_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_RememberedTab_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ff9ade58d4ac39e36335ac0df1405a5f')"
]
}
}

View file

@ -0,0 +1,834 @@
{
"formatVersion": 1,
"database": {
"version": 40,
"identityHash": "b297c087485321c3ee8282ad1ff675a0",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, `lastUsed` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
},
{
"fieldPath": "lastUsed",
"columnName": "lastUsed",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, `pin` TEXT, `requireLogin` INTEGER NOT NULL DEFAULT false, `lastUsed` TEXT, `uiLanguage` TEXT, `preferredAudioLanguage` TEXT NOT NULL DEFAULT '', `preferredSubtitleLanguage` TEXT NOT NULL DEFAULT '', `subtitleMode` TEXT NOT NULL DEFAULT 'USE_USER_PROFILE', FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
},
{
"fieldPath": "pin",
"columnName": "pin",
"affinity": "TEXT"
},
{
"fieldPath": "requireLogin",
"columnName": "requireLogin",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "false"
},
{
"fieldPath": "lastUsed",
"columnName": "lastUsed",
"affinity": "TEXT"
},
{
"fieldPath": "uiLanguage",
"columnName": "uiLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "appPreferences.preferredAudioLanguage",
"columnName": "preferredAudioLanguage",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "appPreferences.preferredSubtitleLanguage",
"columnName": "preferredSubtitleLanguage",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "appPreferences.subtitleMode",
"columnName": "subtitleMode",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'USE_USER_PROFILE'"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `order` INTEGER NOT NULL DEFAULT -1, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "-1"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', `viewOptions` TEXT, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
},
{
"fieldPath": "viewOptions",
"columnName": "viewOptions",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "playback_effects",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `rotation` INTEGER NOT NULL, `brightness` INTEGER NOT NULL, `contrast` INTEGER NOT NULL, `saturation` INTEGER NOT NULL, `hue` INTEGER NOT NULL, `red` INTEGER NOT NULL, `green` INTEGER NOT NULL, `blue` INTEGER NOT NULL, `blur` INTEGER NOT NULL, PRIMARY KEY(`jellyfinUserRowId`, `itemId`, `type`))",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoFilter.rotation",
"columnName": "rotation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.brightness",
"columnName": "brightness",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.contrast",
"columnName": "contrast",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.saturation",
"columnName": "saturation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.hue",
"columnName": "hue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.red",
"columnName": "red",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.green",
"columnName": "green",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blue",
"columnName": "blue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blur",
"columnName": "blur",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"itemId",
"type"
]
}
},
{
"tableName": "PlaybackLanguageChoice",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `seriesId` TEXT NOT NULL, `itemId` TEXT, `audioLanguage` TEXT, `subtitleLanguage` TEXT, `subtitlesDisabled` INTEGER, PRIMARY KEY(`userId`, `seriesId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "seriesId",
"columnName": "seriesId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT"
},
{
"fieldPath": "audioLanguage",
"columnName": "audioLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitleLanguage",
"columnName": "subtitleLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitlesDisabled",
"columnName": "subtitlesDisabled",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"seriesId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "ItemTrackModification",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `trackIndex` INTEGER NOT NULL, `delayMs` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`, `trackIndex`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "trackIndex",
"columnName": "trackIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "delayMs",
"columnName": "delayMs",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId",
"trackIndex"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "seerr_servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `name` TEXT, `version` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_seerr_servers_url",
"unique": true,
"columnNames": [
"url"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_seerr_servers_url` ON `${TABLE_NAME}` (`url`)"
}
]
},
{
"tableName": "seerr_users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `serverId` INTEGER NOT NULL, `authMethod` TEXT NOT NULL, `username` TEXT, `password` TEXT, `credential` TEXT, PRIMARY KEY(`jellyfinUserRowId`, `serverId`), FOREIGN KEY(`serverId`) REFERENCES `seerr_servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`jellyfinUserRowId`) REFERENCES `users`(`rowId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "authMethod",
"columnName": "authMethod",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT"
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT"
},
{
"fieldPath": "credential",
"columnName": "credential",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"serverId"
]
},
"foreignKeys": [
{
"table": "seerr_servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"jellyfinUserRowId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "RememberedTab",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `index` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "index",
"columnName": "index",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_RememberedTab_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_RememberedTab_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "SeriesTrackChoice",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `parentId` TEXT NOT NULL, `type` TEXT NOT NULL, `parentType` TEXT NOT NULL, `itemId` TEXT, `language` TEXT, `activation` TEXT NOT NULL, `trackFlags` INTEGER NOT NULL, `codec` TEXT, `relativeTrackIndex` INTEGER, `title` TEXT, `audioChannels` INTEGER, PRIMARY KEY(`userId`, `parentId`, `type`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "parentId",
"columnName": "parentId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "parentType",
"columnName": "parentType",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT"
},
{
"fieldPath": "language",
"columnName": "language",
"affinity": "TEXT"
},
{
"fieldPath": "activation",
"columnName": "activation",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "trackFlags",
"columnName": "trackFlags",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "codec",
"columnName": "codec",
"affinity": "TEXT"
},
{
"fieldPath": "relativeTrackIndex",
"columnName": "relativeTrackIndex",
"affinity": "INTEGER"
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT"
},
{
"fieldPath": "audioChannels",
"columnName": "audioChannels",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"parentId",
"type"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b297c087485321c3ee8282ad1ff675a0')"
]
}
}

View file

@ -0,0 +1,841 @@
{
"formatVersion": 1,
"database": {
"version": 41,
"identityHash": "1f2fe7912f9e3ab83b77029e07488e84",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, `lastUsed` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
},
{
"fieldPath": "lastUsed",
"columnName": "lastUsed",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, `pin` TEXT, `requireLogin` INTEGER NOT NULL DEFAULT false, `lastUsed` TEXT, `uiLanguage` TEXT, `preferredAudioLanguage` TEXT NOT NULL DEFAULT '', `preferredSubtitleLanguage` TEXT NOT NULL DEFAULT '', `subtitleMode` TEXT NOT NULL DEFAULT 'USE_USER_PROFILE', `c_homeSettingsSource` TEXT NOT NULL DEFAULT 'UNSET', FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
},
{
"fieldPath": "pin",
"columnName": "pin",
"affinity": "TEXT"
},
{
"fieldPath": "requireLogin",
"columnName": "requireLogin",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "false"
},
{
"fieldPath": "lastUsed",
"columnName": "lastUsed",
"affinity": "TEXT"
},
{
"fieldPath": "uiLanguage",
"columnName": "uiLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "appPreferences.preferredAudioLanguage",
"columnName": "preferredAudioLanguage",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "appPreferences.preferredSubtitleLanguage",
"columnName": "preferredSubtitleLanguage",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "appPreferences.subtitleMode",
"columnName": "subtitleMode",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'USE_USER_PROFILE'"
},
{
"fieldPath": "config.homeSettingsSource",
"columnName": "c_homeSettingsSource",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'UNSET'"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `order` INTEGER NOT NULL DEFAULT -1, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "-1"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', `viewOptions` TEXT, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
},
{
"fieldPath": "viewOptions",
"columnName": "viewOptions",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "playback_effects",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, `rotation` INTEGER NOT NULL, `brightness` INTEGER NOT NULL, `contrast` INTEGER NOT NULL, `saturation` INTEGER NOT NULL, `hue` INTEGER NOT NULL, `red` INTEGER NOT NULL, `green` INTEGER NOT NULL, `blue` INTEGER NOT NULL, `blur` INTEGER NOT NULL, PRIMARY KEY(`jellyfinUserRowId`, `itemId`, `type`))",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoFilter.rotation",
"columnName": "rotation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.brightness",
"columnName": "brightness",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.contrast",
"columnName": "contrast",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.saturation",
"columnName": "saturation",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.hue",
"columnName": "hue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.red",
"columnName": "red",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.green",
"columnName": "green",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blue",
"columnName": "blue",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "videoFilter.blur",
"columnName": "blur",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"itemId",
"type"
]
}
},
{
"tableName": "PlaybackLanguageChoice",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `seriesId` TEXT NOT NULL, `itemId` TEXT, `audioLanguage` TEXT, `subtitleLanguage` TEXT, `subtitlesDisabled` INTEGER, PRIMARY KEY(`userId`, `seriesId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "seriesId",
"columnName": "seriesId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT"
},
{
"fieldPath": "audioLanguage",
"columnName": "audioLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitleLanguage",
"columnName": "subtitleLanguage",
"affinity": "TEXT"
},
{
"fieldPath": "subtitlesDisabled",
"columnName": "subtitlesDisabled",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"seriesId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "ItemTrackModification",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `trackIndex` INTEGER NOT NULL, `delayMs` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`, `trackIndex`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "trackIndex",
"columnName": "trackIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "delayMs",
"columnName": "delayMs",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId",
"trackIndex"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "seerr_servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `name` TEXT, `version` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_seerr_servers_url",
"unique": true,
"columnNames": [
"url"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_seerr_servers_url` ON `${TABLE_NAME}` (`url`)"
}
]
},
{
"tableName": "seerr_users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`jellyfinUserRowId` INTEGER NOT NULL, `serverId` INTEGER NOT NULL, `authMethod` TEXT NOT NULL, `username` TEXT, `password` TEXT, `credential` TEXT, PRIMARY KEY(`jellyfinUserRowId`, `serverId`), FOREIGN KEY(`serverId`) REFERENCES `seerr_servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`jellyfinUserRowId`) REFERENCES `users`(`rowId`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "jellyfinUserRowId",
"columnName": "jellyfinUserRowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "authMethod",
"columnName": "authMethod",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT"
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT"
},
{
"fieldPath": "credential",
"columnName": "credential",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"jellyfinUserRowId",
"serverId"
]
},
"foreignKeys": [
{
"table": "seerr_servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"jellyfinUserRowId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "RememberedTab",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `index` INTEGER NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "index",
"columnName": "index",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_RememberedTab_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_RememberedTab_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "SeriesTrackChoice",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `parentId` TEXT NOT NULL, `type` TEXT NOT NULL, `parentType` TEXT NOT NULL, `itemId` TEXT, `language` TEXT, `activation` TEXT NOT NULL, `trackFlags` INTEGER NOT NULL, `codec` TEXT, `relativeTrackIndex` INTEGER, `title` TEXT, `audioChannels` INTEGER, PRIMARY KEY(`userId`, `parentId`, `type`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "parentId",
"columnName": "parentId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "parentType",
"columnName": "parentType",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT"
},
{
"fieldPath": "language",
"columnName": "language",
"affinity": "TEXT"
},
{
"fieldPath": "activation",
"columnName": "activation",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "trackFlags",
"columnName": "trackFlags",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "codec",
"columnName": "codec",
"affinity": "TEXT"
},
{
"fieldPath": "relativeTrackIndex",
"columnName": "relativeTrackIndex",
"affinity": "INTEGER"
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT"
},
{
"fieldPath": "audioChannels",
"columnName": "audioChannels",
"affinity": "INTEGER"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"parentId",
"type"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1f2fe7912f9e3ab83b77029e07488e84')"
]
}
}

View file

@ -0,0 +1,303 @@
{
"formatVersion": 1,
"database": {
"version": 6,
"identityHash": "24390d64259f19e0e67ecfb827b974fb",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '24390d64259f19e0e67ecfb827b974fb')"
]
}
}

View file

@ -0,0 +1,308 @@
{
"formatVersion": 1,
"database": {
"version": 7,
"identityHash": "a4fd9e29c8cbcd13c24e8644f62e8551",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a4fd9e29c8cbcd13c24e8644f62e8551')"
]
}
}

View file

@ -0,0 +1,315 @@
{
"formatVersion": 1,
"database": {
"version": 8,
"identityHash": "d4874262e325932fe00de49471288d82",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd4874262e325932fe00de49471288d82')"
]
}
}

View file

@ -0,0 +1,320 @@
{
"formatVersion": 1,
"database": {
"version": 9,
"identityHash": "4255556f160da7bb269b114400da58da",
"entities": [
{
"tableName": "servers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `url` TEXT NOT NULL, `version` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "version",
"columnName": "version",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
}
},
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `id` TEXT NOT NULL, `name` TEXT, `serverId` TEXT NOT NULL, `accessToken` TEXT, FOREIGN KEY(`serverId`) REFERENCES `servers`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT"
},
{
"fieldPath": "serverId",
"columnName": "serverId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "accessToken",
"columnName": "accessToken",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_users_id_serverId",
"unique": true,
"columnNames": [
"id",
"serverId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_users_id_serverId` ON `${TABLE_NAME}` (`id`, `serverId`)"
},
{
"name": "index_users_id",
"unique": false,
"columnNames": [
"id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_id` ON `${TABLE_NAME}` (`id`)"
},
{
"name": "index_users_serverId",
"unique": false,
"columnNames": [
"serverId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_users_serverId` ON `${TABLE_NAME}` (`serverId`)"
}
],
"foreignKeys": [
{
"table": "servers",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"serverId"
],
"referencedColumns": [
"id"
]
}
]
},
{
"tableName": "ItemPlayback",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`rowId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sourceId` TEXT, `audioIndex` INTEGER NOT NULL, `subtitleIndex` INTEGER NOT NULL, FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "rowId",
"columnName": "rowId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourceId",
"columnName": "sourceId",
"affinity": "TEXT"
},
{
"fieldPath": "audioIndex",
"columnName": "audioIndex",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subtitleIndex",
"columnName": "subtitleIndex",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"rowId"
]
},
"indices": [
{
"name": "index_ItemPlayback_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemPlayback_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "NavDrawerPinnedItem",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
},
{
"tableName": "LibraryDisplayInfo",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` INTEGER NOT NULL, `itemId` TEXT NOT NULL, `sort` TEXT NOT NULL, `direction` TEXT NOT NULL, `filter` TEXT NOT NULL DEFAULT '{}', `viewOptions` TEXT, PRIMARY KEY(`userId`, `itemId`), FOREIGN KEY(`userId`) REFERENCES `users`(`rowId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sort",
"columnName": "sort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "direction",
"columnName": "direction",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "filter",
"columnName": "filter",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'{}'"
},
{
"fieldPath": "viewOptions",
"columnName": "viewOptions",
"affinity": "TEXT"
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"userId",
"itemId"
]
},
"indices": [
{
"name": "index_LibraryDisplayInfo_userId_itemId",
"unique": true,
"columnNames": [
"userId",
"itemId"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_LibraryDisplayInfo_userId_itemId` ON `${TABLE_NAME}` (`userId`, `itemId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"userId"
],
"referencedColumns": [
"rowId"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4255556f160da7bb269b114400da58da')"
]
}
}

View file

@ -0,0 +1,78 @@
package com.github.damontecres.wholphin.test
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performKeyInput
import androidx.compose.ui.test.pressKey
import androidx.navigation3.runtime.NavBackStack
import com.github.damontecres.wholphin.MainContent
import com.github.damontecres.wholphin.services.NavigationManager
import com.github.damontecres.wholphin.services.ScreensaverService
import com.github.damontecres.wholphin.services.ScreensaverState
import com.github.damontecres.wholphin.services.SetupDestination
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@HiltAndroidTest
class InstrumentedBasicUiTests {
@get:Rule(order = 0)
var hiltRule = HiltAndroidRule(this)
@get:Rule(order = 1)
val composeTestRule = createAndroidComposeRule<TestActivity>()
lateinit var screensaverService: ScreensaverService
@Before
fun setup() {
screensaverService = mockk(relaxed = true)
every { screensaverService.state } returns MutableStateFlow(ScreensaverState(false, false, false, false))
}
@OptIn(ExperimentalTestApi::class)
@Test
fun myTest() {
val navigationManager = NavigationManager()
navigationManager.backStack = NavBackStack(Destination.Home())
// Start the app
composeTestRule.setContent {
WholphinTheme {
MainContent(
backStack = mutableListOf(SetupDestination.ServerList),
navigationManager = navigationManager,
appPreferences = mockk(relaxed = true),
backdropService = mockk(relaxed = true),
screensaverService = screensaverService,
modifier = Modifier,
)
}
}
composeTestRule.onNodeWithText("Add Server").assertExists()
composeTestRule.onNodeWithTag("add_server").performKeyInput {
pressKey(Key.DirectionDown) // TODO
}
composeTestRule.onNodeWithTag("add_server").performClickEnter()
composeTestRule.onNodeWithText("Discovered Servers").assertExists()
}
}
@OptIn(ExperimentalTestApi::class)
fun SemanticsNodeInteraction.performClickEnter() =
performKeyInput {
pressKey(Key.DirectionCenter)
}

View file

@ -0,0 +1,7 @@
package com.github.damontecres.wholphin.test
import androidx.activity.ComponentActivity
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class TestActivity : ComponentActivity()

View file

@ -0,0 +1,14 @@
package com.github.damontecres.wholphin.test
import android.app.Application
import android.content.Context
import androidx.test.runner.AndroidJUnitRunner
import dagger.hilt.android.testing.HiltTestApplication
class WholphinTestRunner : AndroidJUnitRunner() {
override fun newApplication(
cl: ClassLoader?,
name: String?,
context: Context?,
): Application = super.newApplication(cl, HiltTestApplication::class.java.name, context)
}

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" tools:node="remove" />
<uses-permission
android:name="com.amazon.permission.media.session.voicecommandcontrol"
tools:node="remove" />
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Some files were not shown because too many files have changed in this diff Show more