Commit Graph
732 Commits
Author SHA1 Message Date
edde746 fe8d405a56 fix(i18n): enforce complete localized coverage 2026-07-12 17:31:14 +02:00
edde746 7ecadffdb1 refactor(features): consolidate shared feature primitives 2026-07-12 17:31:13 +02:00
edde746 6506f36843 refactor(app): share client and presentation scaffolds 2026-07-12 17:31:12 +02:00
edde746 f78089edf8 refactor(input): share D-pad long press state 2026-07-12 08:42:24 +02:00
edde746 80344a630f feat(playlists): add picker filtering and TV focus 2026-07-12 08:42:24 +02:00
edde746 8a8b4f3a61 refactor: remove unused convenience APIs 2026-07-12 08:42:20 +02:00
edde746 cdec9d1514 refactor(player): centralize chapter traversal 2026-07-12 08:42:20 +02:00
edde746 18835d92dd refactor(navigation): reuse library icon mapping 2026-07-12 08:42:20 +02:00
edde746 967c25b11d refactor(player): share queue item labels 2026-07-12 08:42:20 +02:00
edde746 a63369c247 fix: close async lifecycle races 2026-07-12 08:42:19 +02:00
edde746 4a81246986 build(libass): bump fork to 0.18.3 2026-07-12 00:44:08 +02:00
edde746 a23c7f215c fix(player): tighten staged back navigation 2026-07-11 23:11:25 +02:00
edde746 4443b76145 fix(player): respect visible chrome on back 2026-07-11 17:38:22 +02:00
edde746 3a982c2a7a fix(player): prevent duplicate back actions 2026-07-11 12:59:57 +02:00
Ryan Ernest c86d40f8dd fix(player): preserve volume when toggling mute (#1532)
* fix(player): preserve volume when toggling mute

Muting now keeps the last volume level memorized. Unmuting restores that level, including amplified values allowed by the configured maximum.

* fix(player): preserve volume for companion remote mute

Route the companion remote through the shared mute transition so it no longer overwrites the persisted volume or restores to 100%. Also correct the Dartdoc formatting for the transition record fields.
2026-07-11 12:46:58 +02:00
edde746 d86e328169 fix(player): unify back navigation
close #1525
2026-07-11 03:23:40 +02:00
edde746 7509077d63 perf(tv): reduce animated semantics work 2026-07-10 12:07:19 +02:00
edde746 1acc83572e perf(tv): reduce navigation rebuilds and artwork fan-out 2026-07-10 12:07:19 +02:00
edde746 04daf89a20 feat(explore): Explore tab with catalog detail, search, and Seerr requests 2026-07-10 07:10:15 +02:00
edde746 f94b8c6586 feat(settings): Services hub, Seerr sign-in, and all-locale strings 2026-07-10 07:09:29 +02:00
edde746 fe5d6d8d96 fix(music): play audio playlists from context menus 2026-07-09 23:22:27 +02:00
edde746 ef75887504 style: apply analyzer lint fixes across lib
Initializing formals, unawaited() on fire-and-forget futures, unused
import removal, wildcard params, and mounted vs context.mounted.
2026-07-06 15:56:56 +02:00
edde746 49d4b433a7 fix(music): show now-playing rail item only on TV
Desktop already has the mini-player as the way back into the
now-playing screen; the rail item is redundant there.
2026-07-06 15:56:01 +02:00
edde746andClaude Fable 5 5f7925f350 fix(music): scope current-track highlight per server
TrackRow compared bare ids (server-scoped rating keys), so a track on
another server with the same id lit up as currently playing. Compare
globalKey (serverId:id) like the rest of the queue engine.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 15:32:26 +02:00
edde746 4de38f4b17 fix(music): hide album track numbers in queue rows
Album-relative numbers read as a sorting bug in a mixed-album queue;
the leading slot stays so titles align with the equalizer row.
2026-07-06 12:17:03 +02:00
edde746 adf732a1a1 fix(ui): app-bar tab chip strips scroll instead of overflowing
Shared TabChipStrip replaces the plain Rows in libraries/downloads/live
TV app bars (and live TV's hand-rolled scroll wrapper); chips center
themselves on focus so d-pad reaches off-screen tabs.
2026-07-06 12:17:03 +02:00
edde746 214a7c13dc feat(music): show full play queue with history, YouTube Music style
Queue sheet/panel now renders the whole playback order — dimmed played
tracks above the current one — instead of only up-next. Opens scrolled
to the current track and follows advancement while parked on it.
2026-07-06 11:30:23 +02:00
edde746 fcdef343eb fix(music): sheet safe-area, swipe-to-dismiss, hide mini-player under sheets 2026-07-06 10:51:19 +02:00
edde746 764345f021 feat(music): audio downloads and music home rows
Album/artist downloads expand to tracks with pinned parent metadata,
aggregate progress, container deletes with reference-counted album
covers, and a Music tab on the downloads screen with fully offline
album playback. Home rows include music libraries (fixes plex hub
items being filtered to video types) and audio playlists join
download/sync rules.
2026-07-05 21:51:37 +02:00
edde746 7d6a747aa7 feat(music): now-playing, mini-player, queue and lyrics UI
Persistent mini-player over the profile navigator (route-aware
suppression, bottom-bar inset), slide-up now-playing screen with
mobile/desktop/TV layouts (blurred-art background, pause shape morphs,
dpad seek chain), queue sheet with reorder/remove/jump, synced lyrics
view, TV rail now-playing item, sleep timer, and audio playlist
playback. Adds download affordances on music surfaces.
2026-07-05 21:50:11 +02:00
edde746 05a631415e feat(music): music browsing UI — square cards, library groupings, artist/album screens
Un-gates music libraries; adds CardShape.square plumbing (circular
artists), artists/albums/tracks browse groupings with per-grouping
sorts, audio playlists tab, artist + album detail screens with dpad
track rows, music context-menu actions, and a stub MusicPlaybackService
seam for the upcoming engine.
2026-07-05 17:20:28 +02:00
edde746 c87a9060f9 chore: tidy formatting and local ignores 2026-07-05 14:32:21 +02:00
edde746 ee3c75ba9e fix(playback): remember last played media version for continue watching
close #1492
2026-07-05 14:29:57 +02:00
edde746 90c4fe5458 feat(audio): add Kodi-style stereo downmix with center channel boost 2026-07-05 14:29:57 +02:00
edde746 f97ab03111 fix(images): bound artwork decodes on both axes and never fetch unsized originals
The decode clamp was height-only, so ultra-wide originals could decode
far past the display budget, and two small-slot fallbacks skipped
server-side resizing entirely — handing multi-megapixel originals to
the decoder behind tiny slots. The TV spotlight background's offline
branch had no decode bound at all. All artwork now flows through a
shared both-axes bound (ResizeImagePolicy.fit: aspect-preserving,
scale-down only), and the reduced tier gets matching thumb/poster
decode caps.

Ref #1349
2026-07-05 09:10:20 +02:00
edde746 3981fac0db feat(settings): surface Manage Libraries and raise the desktop sheet cap
Extract the manage/reorder-libraries sheet from the libraries screen into
a shared widget with a public launcher, and add a settings tile (top
group, gated on having libraries) that opens it. The libraries screen
keeps its side-nav refresh and re-select-on-hide behavior via launcher
hooks; the TV dialog path lives in the launcher so the entry works there
too.

Desktop overlay sheets defaulted to a fixed 400px max height, which left
the reorder sheet ~280px tall. Both default-constraint sites now use 75%
of window height, matching mobile.

close #1468
2026-07-05 09:08:29 +02:00
edde746 c4184c6148 fix(ci): restore sanity checks 2026-07-05 08:34:55 +02:00
edde746 aee48f6956 feat: extend Force TV mode to desktop for home-theater setups
Initialize TvDetectionService on every platform so the existing
force_tv_mode setting drives the 10-foot TV interface on Windows,
macOS, and Linux, and surface the toggle in appearance settings there.

Desktop keeps real-mouse behavior in TV mode: InputModeTracker still
flips between pointer and keyboard modes (cursor hidden while
keyboard-driven), segmented controls keep their hover affordance, and
the settings backup section stays available (only Android TV lacks a
document picker).

Adds PlatformDetector.debugSetIsDesktopOSOverride so TV-device
simulations in widget tests don't inherit the desktop test host's
platform.

close #1409
2026-07-05 07:08:40 +02:00
edde746 11f7fd766d perf(library): cut scroll jank in card grids and hub rows
Profile traces showed 100-370ms UI-thread frames while scrolling the
library screen, dominated by rebuilding and inflating media cards.

- Gate per-card focus/pointer chrome on input mode: FocusableWrapper
  skips the scale/border wrappers and creates its AnimationController
  lazily outside keyboard mode, and ClickableCursor plus the card tap
  region only build MouseRegion/InkWell machinery on desktop - TV and
  touch use a bare GestureDetector. Hub cards also drop their outer
  gesture wrapper outside keyboard mode; the card's own tap region
  always won the gesture arena anyway.

- Memoize sliver children (SliverChildMemo): browse/collections grids
  and hub rows return identical widget instances for unchanged items,
  so delegate swaps from pagination, watch-state, and deletion
  setStates no longer rebuild every realized card inside layout. The
  browse tab prunes the memo in lockstep with focus-node eviction so a
  cached card can never resurrect a disposed FocusNode.

- Budget fresh inflation (CardInflationBudget): while a scrollable is
  moving in pointer/touch mode at most one new card inflates per frame,
  the rest render as SkeletonMediaCard and upgrade on following frames.
  Hub rows also stop pre-inflating 250px of off-screen cards on entry.

Device traces: worst frame 373ms -> 103ms, per-card build 3.6ms ->
2.4ms median; remaining row-entry work is spread across frames.
2026-07-05 02:23:42 +02:00
edde746 d7c0af9ad0 fix(player): fit the performance overlay on short screens
Anchor the overlay to the screen and seekbar clearance, widen the card
on height-limited screens so sections pack side by side, and scale down
as a last resort instead of clipping the bottom sections.

close #1469
2026-07-04 23:53:50 +02:00
edde746 f9abcd44fe fix(playback): hold timeline preview until playback settles at the target
The preview cleared unconditionally 2s after a committed seek; on a device
still buffering, the live position hasn't caught up yet, so the next key
press re-based off the stale position and re-targeted the same spot -
discarding the seek exactly on the slow hardware the coalescing targets.
Re-arm the clear until the live position is near the target, with a 10s
ceiling backstop (same settle pattern as LiveSeekAccumulator).
2026-07-04 22:58:19 +02:00
edde746 3cad91736b fix(playback): raise timeline scrub debounce above the key-repeat delay
350ms sits below the typical KeyDown-to-first-repeat gap (~400-500ms on
Android/TV), so every hold committed an intermediate seek before repeats
even began. 800ms matches Jellyfin ATV (media3's DefaultTimeBar uses 1s);
key release still flushes synchronously, so taps are unaffected.
2026-07-04 22:57:27 +02:00
edde746 632d66f6ac fix(playback): clear timeline seek dedup state when a gesture finalizes
_lastDispatchedTimelineSeek was set by the drag throttle and never reset, so
with key-seeks now routed through onSeekEnd a coalesced flush targeting the
same position as a stale drag (realistic at the 0/duration clamp boundaries)
skipped the real seek while still reporting completion.
2026-07-04 22:57:00 +02:00
Christian Tismer ed843b4b89 fix: coalesce held timeline seeks on direct play
A held fast-forward fired a real seek on every key-repeat for direct/
progressive streams, flooding the source with overlapping range requests and
wedging low-power devices (e.g. Fire TV Stick) in BUFFERING until the player
was restarted. Route direct play through the same preview/debounce path the
transcode case already uses: move only the timeline preview while the key is
held and commit a single seek on release or pause.

close #1424
2026-07-04 22:56:43 +02:00
edde746 6af4289289 fix(tv): keep sidebar focused when Back returns to the home tab
Backing out to home selected the home tab but let focusActiveTabIfReady
steal real focus into the content while _isSidebarFocused stayed true,
leaving the rail stuck expanded over a shifted content box that clipped
the end of every hub row. Skip the content-focus grab while the sidebar
owns focus, move the sidebar focus ring to the Home item on back-to-home,
and self-heal the flag whenever the content scope actually gains focus.

close #1411
2026-07-04 20:56:04 +02:00
edde746 7632d4bea7 feat(jellyfin): favorites library filter + heart favorite in the rate sheet
close #1485
2026-07-04 18:33:58 +02:00
edde746 3f9c25fe5e fix(ui): hide Play Version while the item's server is unreachable
At most one version of an item can be downloaded, and plain Play now
targets it directly, so an offline Play Version picker is a no-op
detour offering versions that cannot play (#1440). Backend
capabilities are static, so the menu gate and the quality picker in
_handlePlayVersion both get a liveness check — a server dropping
between menu open and tap must not offer transcodes either.
2026-07-03 10:19:55 +02:00
edde746 6d95e35448 feat(tvos): AVPlayer Atmos sink wiring + Atmos output diagnostics
Re-enables Audio Passthrough on Apple TV against the MPVKit EAC3-JOC
AVPlayer sink (pin bump to v1.0.8 follows), adds an AVPlayer test
harness + diagnostics screen for #1300, and makes loudnorm yield while
passthrough is active (fixes a latent spdif conflict on desktop too).
2026-07-02 15:52:10 +02:00
edde746 5f49dddb4d feat(ui): M3E restyle for settings, auth, and profile screens 2026-07-02 11:47:43 +02:00
edde746 074f92d144 fix(tv): bleed the unfocused-rail dim under the side nav
The rail dim (and its top fade band) was clipped to the rail's footprint,
so it terminated in a vertical seam at the nav edge while the backdrop
continued behind the nav. Generalize _RailBackgroundBleed into
_RailBleedPositioned and render the dim through it, full-width like the
background gradient. Row stripes now apply only while the rail itself is
focused — under the full-width dim they'd re-introduce level seams at the
nav edge (and read as noise anyway).
2026-07-02 11:41:25 +02:00