Commit Graph

605 Commits

Author SHA1 Message Date
8d194f2baf chore(build): increment app versionCode to 1000033 2026-06-26 18:40:24 +02:00
c836c809fc feat(series): add confirmation dialog before marking series as watched 2026-06-26 16:39:37 +00:00
b6fc10df57 chore(build): increment app-tv versionCode to 2000017 2026-06-26 18:34:43 +02:00
18e68c6051 feat(series): add long-click context menu to episode cards
Add a ModalBottomSheet with Mark as watched/unwatched actions on long-click of episode cards.

- Replace clickable with combinedClickable to support long-click gestures
- Add markEpisodeAsWatched function to SeriesViewModel
- Add MediaAction model usage for bottom sheet actions
- Add test tag for the dialog component
2026-06-26 16:32:53 +00:00
e8224849cc fix(player): prevent hidden seek timeline from showing when controls are visible 2026-06-26 18:29:05 +02:00
c2cc4e9e65 revert(data): remove ETag caching and head-fetch home refresh
Reverts 5491c38 (ETag-based conditional HTTP caching for home refresh) and 1ec9ff9 (head-fetch rows and count-only libraries). Restores the pre-ETag home refresh path that fully loads library content per library and fetches each home row in a single request.

Preserves unrelated later improvements: SingleFlight deduplication (86f44ac), nullable toLibrary skip for unsupported library types (80b1bd8), and upsert-before-publish ordering to prevent empty intermediate states (f0a7828).
2026-06-26 15:44:05 +00:00
13bc9410b4 chore(build): increment app-tv versionCode to 2000016 2026-06-25 21:17:10 +02:00
422642ad19 chore(build): increment app versionCode to 1000032 2026-06-25 21:14:33 +02:00
59315b701d fix(repository): merge existing seasons when updating series with empty seasons 2026-06-25 21:09:43 +02:00
3c46650a63 fix(repository): enhance series upsert logic to preserve existing seasons 2026-06-25 20:47:38 +02:00
f0a7828613 refactor(repository): reorder state updates to prevent empty intermediate states 2026-06-22 20:30:31 +02:00
8c758e8262 feat(tv-player): add live clock overlay to player controls 2026-06-22 18:27:09 +00:00
9872f401f8 refactor(data): remove redundant dedup guards and improve loading robustness 2026-06-22 18:09:50 +00:00
86f44ac9ce refactor(data): deduplicate concurrent data fetches with SingleFlight 2026-06-22 17:26:50 +00:00
d80b283ded fix(repository): streamline season loading and update series state 2026-06-22 19:20:20 +02:00
1ac649e8ae fix(tv-player): remove unused seek live edge functionality and related UI elements 2026-06-22 18:41:23 +02:00
b23db4bab3 fix(player): keep seek bar thumb at target on release
Previously, the Slider's value was switched from the local sliderPosition to
the positionMs prop the instant onValueChangeFinished cleared isScrubbing.
Because the prop had not yet caught up with the committed seek, the thumb
visibly snapped back to the pre-seek position before jumping to the target.

Drive the slider from sliderPosition unconditionally, sync sliderPosition
from the prop via a LaunchedEffect only when not scrubbing, and pin
sliderPosition to the committed target before clearing isScrubbing in
onValueChangeFinished.
2026-06-21 11:17:16 +00:00
67a141e7d7 chore(build): increment app versionCode to 1000031 2026-06-21 10:25:20 +02:00
9f432edd27 chore(build): increment app-tv versionCode to 2000015 2026-06-20 21:50:31 +02:00
80b1bd8547 fix(data): gracefully skip unsupported library types during refresh
Previously, toLibrary() threw UnsupportedOperationException for
library types the app does not surface (e.g. boxsets, music, photos,
live TV), which aborted the entire home screen refresh. Now these
unsupported types are logged and skipped, preventing a single
unexpected view from leaving the user with an empty home screen.
2026-06-20 21:50:15 +02:00
00c5689195 fix(tv-player): Do not show hidden seek line when skip button is shown 2026-06-20 21:12:13 +02:00
bd2c1777a5 fix(tv-player): Only show paused feedback 2026-06-20 21:12:12 +02:00
a5d04998ac refactor(tv): extract hero backdrop and simplify gradient drawing
Split the hero backdrop (image + gradient overlays) into TvHomeHeroBackdrop, rendered behind the home content. Replace nested Box/background gradient approach with drawWithContent for better composability. Clean up unused imports across TvHomeScreen, TvFocusedItemHero, and TvHomeContent.
2026-06-20 18:52:09 +00:00
da89a8a661 fix(tv): adjust hero height fraction for better layout on TV screens 2026-06-20 20:22:49 +02:00
53a7d4ad23 chore(build): increment app versionCode to 1000030 2026-06-20 19:42:19 +02:00
8ac0fd0bd1 feat(series): show season/episode context on play button and truncate subtitle
Enhance the series play button to display season and episode number alongside the play label when a next-up episode is available. Also constrain the subtitle text to a single line with ellipsis overflow to prevent layout issues from long subtitle strings.
2026-06-20 17:42:03 +00:00
1910a0c144 refactor(player): improve EmptyValueTimedVisibility and ValueChangeTimedVisibility components 2026-06-20 19:26:59 +02:00
217ba5b6d9 refactor(player): use mutableFloatStateOf for volume and brightness states 2026-06-20 18:38:59 +02:00
fee0e703eb chore(build): increment app versionCode to 1000029 2026-06-20 18:18:49 +02:00
0fc4644fdb refactor(tvplayer): remove unused isPlaying parameter from key event handler 2026-06-20 18:04:28 +02:00
6e48b35741 refactor(data/jellyfin): reload media items on user data change events 2026-06-20 16:01:22 +00:00
8d5948438e feat(data/jellyfin): sync watch progress in real time via WebSocket
Add JellyfinWebSocketService that subscribes to UserDataChangedMessage events and propagates watch progress changes to the local media repository.

The WebSocket subscription is gated by login status and network connectivity (matching other background work), runs with automatic retry on transient failures, and follows the lifecycle of the Jellyfin SDK's ApiClient for automatic reconnection.

Inject the service into JellyfinSessionBootstrapper to ensure Hilt instantiates the singleton at app startup.
2026-06-20 13:27:10 +00:00
8a2702a7cb refactor(core/series): improve data loading reliability with concurrent initialization 2026-06-20 12:18:29 +00:00
5a98f3d2e7 refactor(core): separate data loading from repository reads
Move implicit loading side effects out of getX methods into explicit loadX methods, making data flow predictable. Remove seriesTitle StateFlow in favor of direct seriesName access. Add proper error handling to repository load operations.
2026-06-20 09:27:25 +00:00
80f25c6dea chore(build): increment app-tv versionCode to 2000014 2026-06-19 19:49:08 +02:00
b19d41abc6 chore(build): increment app versionCode to 1000028 2026-06-19 19:43:58 +02:00
831abfebb4 feat(tv): make hero responsive to screen height and enrich metadata 2026-06-19 17:04:55 +00:00
eb540a0fbb fix(tv): constrain media detail background gradients to image size 2026-06-19 16:16:52 +00:00
29c48a50b6 fix(player): disable swipe gestures when player controls are shown 2026-06-19 18:11:19 +02:00
05f58295ad fix(player): disable swipe gestures when player controls are shown 2026-06-19 18:11:13 +02:00
31f9fe5d4c refactor(ui): drop focus border from circular buttons
- Remove focusedBorderWidth/focusedBorderColor from CircularIconButton
- Add new CircularTextButton component
- Replace skip segment FilledTonalButton with CircularTextButton
- Clean up unused imports
2026-06-19 14:32:18 +00:00
1ec9ff9c95 perf(home): head-fetch rows and count-only libraries
Add a head-only fetch path for the home rows (Suggestions, Continue
Watching, Next Up, Latest per library). The first HEAD_LIMIT items
are fetched first; if the head matches the cached head, the full
request is skipped. Most home refreshes now only pay for the small
head request, not the full payload.

Replace per-library getLibraryContent calls in the home page with
getLibraryItemCount (limit=0, enableTotalRecordCount=true). The home
page only uses Library.size; the full movies/series lists are fetched
on demand by LibraryViewModel via the new loadLibraryContent method.
The per-row loaders now upsert full movie/series details into
onlineMediaRepository so the home viewmodel can still resolve them.
2026-06-19 13:46:21 +00:00
5491c381a3 feat(jellyfin): add ETag-based conditional HTTP caching for home refresh
Introduce an OkHttp interceptor that caches ETags from home-refresh
endpoints and throws NotModifiedException on 304, allowing callers to
reuse cached data. Wire it into the Jellyfin SDK via a dedicated
@JellyfinSdkClient OkHttpClient. Update API methods to return null on
304, and update InMemoryAppContentRepository to keep existing state
when null is returned. Persist dateLastMediaAdded from /UserViews in
HomeCache to short-circuit unchanged /Items/Latest calls.
2026-06-19 13:16:07 +00:00
34340c2bd4 feat(player): add controls visibility handling to PlayerGesturesLayer 2026-06-19 15:02:10 +02:00
f6c465656e perf(home): reduce redundant requests on home refresh
Drop a duplicate getLibraries call from loadLatestLibraryContent by
reusing the libraries already loaded earlier in the same refresh.

Add a 30s debounce to HomeRefreshCoordinator.onResumed so rapid
resume/tab-switch events coalesce into a single refresh; pull-to-refresh
bypasses the gate via onRefresh.

Rate-limit SyncPlaybackPositionsHomeRefreshSideEffect to once every
6h with a 25-item cap and a rotating window cursor, so users with many
downloads no longer fire one getItemInfo call per item on every resume.
2026-06-19 14:38:57 +02:00
54c895ff0f feat(player): track skippable segment type to gate intro and outro UI
Expose the active skippable segment type from the player state so the UI
can show the skip intro button only for intro segments and trigger the
next episode overlay when an outro segment is active.
2026-06-19 14:25:29 +02:00
a4267d85ce refactor(player): replace manual hidden UI visibility with ValueChangeTimedVisibility counters
- Replace hiddenSeekPreviewPositionMs with hiddenSeek counter for ValueChangeTimedVisibility
- Add resumeStopFeedbackCounter to decouple feedback visibility from isPlaying state
- Consolidate onPausePlayback/onResumePlayback into single onTogglePlayback
- Increase right D-pad seek from 10s to 30s
- Remove obsolete LaunchedEffect resetting hidden seek state
2026-06-19 11:42:16 +00:00
a07f9dd6e4 refactor(player): replace manual stop feedback visibility with ValueChangeTimedVisibility 2026-06-19 11:14:48 +00:00
50bbf5b81f fix(player): let focused overlay handle D-pad center instead of pausing
The root Box's onPreviewKeyEvent consumed KeyDown for D-pad
center/Enter in handleTvPlayerRootKeyEvent, pausing or resuming
playback and returning true before the event reached the focused
next-episode overlay. Pressing center on the overlay therefore
paused playback instead of starting the next episode.

Add a popupVisible flag so the root handler returns false for
center/Enter when a focusable overlay (skip intro / next episode) is
shown, letting the event propagate to the overlay's clickable. The
skip-intro action is preserved because the visible skip button now
handles activation through its own clickable.

Drop the now-unused onSkipSegment/hasSkippableSegment parameters and
the redundant focusable modifier on TvNextEpisodeOverlay (clickable
already makes it focusable).
2026-06-19 11:04:18 +00:00
1bd51d8a9f refactor(player): move controls auto-hide from ViewModel to composable layer 2026-06-18 20:05:35 +00:00