Commit Graph

103 Commits

Author SHA1 Message Date
a4b28b1dc7 fix: prevent popping the last item from backStack if it's the only item 2026-02-18 12:07:30 +01:00
f215c93608 fix: restore subtitles after backward seek
Switch seek parameters from CLOSEST_SYNC to PREVIOUS_SYNC so seeks always
land at or before the requested position, preventing subtitle cues from
being skipped. On backward seek discontinuity, briefly disable and re-enable
the text track to flush TextRenderer state so the current cue is re-delivered.
2026-02-17 21:23:34 +01:00
19273d2cb9 feat: Added purefin logo 2026-02-17 21:07:08 +01:00
5b285ed677 fix: prevent ContinueWatching LazyRow from starting scrolled to middle
Removing the item key disables Compose's scroll anchoring, which was
shifting the scroll offset rightward whenever a refresh prepended new
items, making the list appear to start in the middle.
2026-02-17 20:50:51 +01:00
f97cc54e2a feat: Added SearchField reusable component and used in HomeTopBar 2026-02-17 20:41:33 +01:00
be9f37955b feat: Add Space between HomeContents for more separated sections 2026-02-17 20:25:30 +01:00
be456d4d6c feat: add movie download support using Media3 DownloadService
Implement media downloading with foreground notification showing progress
percentage and speed. Uses Media3's DownloadManager + SimpleCache with
OkHttp datasource for authenticated Jellyfin downloads. Downloaded movies
are saved to the offline database and the player reads from cache
automatically via CacheDataSource.
2026-02-17 20:20:50 +01:00
9c83c3629b fix: resolve gesture conflicts by unifying drag handlers
Replaced competing pointerInput modifiers with a single unified gesture handler that determines drag direction early. This eliminates conflicts between horizontal seeking and vertical brightness/volume gestures, making swipe interactions more reliable and predictable.
2026-02-16 20:30:33 +01:00
733a8b651f fix: correct subtitle display after seeking by preferring embedded delivery
Subtitles were not showing the active cue after rewinding/seeking because
they were delivered externally and Media3 only picks up forward-starting
cues. Changed device profile to prefer EMBED delivery so subtitles stay
in the container where Media3's extraction-time parser handles them
correctly. Also added support for attaching external subtitle tracks to
MediaItem when embedding isn't possible.
2026-02-16 20:30:24 +01:00
43307229cb feat: Decrease gesture zone, so it does conflict with system gestures. 2026-02-16 19:56:19 +01:00
ebf70343ed feat: do not show controls when using gesture for seeking. 2026-02-16 19:28:25 +01:00
98042b97ed feat: add online/offline mode toggle to HomeScreen
Implements a toggle switch in the HomeScreen top bar (next to Menu) that allows users to switch between online mode (fetching from Jellyfin server) and offline mode (using local database only). The preference persists across app restarts via Proto DataStore.

Key changes:
- Added ActiveMediaRepository that delegates to online/offline repositories based on user preference
- Extended MediaRepository interface with continueWatching, nextUp, and latestLibraryContent
- Added isOfflineMode state to UserSession with reactive Flow
- Added Cloud/CloudOff icon toggle button to HomeTopBar
- Updated ViewModels to use MediaRepository interface for better abstraction
2026-02-16 16:02:19 +01:00
c2a0eb60c8 feat: keep screen on during playback 2026-02-16 10:36:50 +01:00
fe97ff5e73 feat: add offline media repository and update dependency injection for media sources 2026-02-16 09:03:20 +01:00
bcaabc6da7 fix: do not include resumable in next up episodes 2026-02-15 22:25:44 +01:00
74b1c19c0c feat: add FFmpeg decoder support for unsupported audio codecs
Add support for audio codecs like DTS-HD that aren't natively supported
by Android's MediaCodec by integrating Jellyfin's FFmpeg decoder extension.

Changes:
- Add media3-ffmpeg-decoder dependency for software audio decoding
- Create AndroidDeviceProfile to detect and report device codec capabilities
- Configure ExoPlayer with extension renderer mode and decoder fallback
- Update playback URL selection to use transcoding when direct play unsupported
- Add CodecDebugHelper for debugging available device codecs

This fixes playback failures when encountering premium audio formats
by falling back to FFmpeg software decoding when hardware decoding fails.
2026-02-15 21:44:21 +01:00
798e95da0d feat: implement track preference storage and auto-selection
Add persistent storage for audio and subtitle track preferences with automatic selection on playback. Track preferences are stored by matching semantic properties (language, channel count, forced flag) rather than track IDs.

Key features:
- Movies remember individual audio/subtitle selections
- Series share preferences across all episodes (by series ID)
- Property-based matching with scoring algorithm
- DataStore persistence with kotlinx.serialization
- Graceful fallback to Media3 defaults when no match found

Implementation:
- Created TrackPreferences data layer with DataStore serialization
- Added TrackMatcher for property-based track matching
- Enhanced TrackOption model with forced flag for subtitles
- Integrated auto-selection into PlayerManager on tracks available
- Save preferences on manual track selection
- PlayerViewModel determines media type and constructs preference key
2026-02-15 20:39:05 +01:00
9e4a9c64cc fix: prevent track selection overlay from auto-hiding on tap
Separated track selection UI from player controls overlay to prevent
the gesture layer from dismissing the track selection panel when
selecting options. Created PersistentOverlayContainer that manages
its own visibility state independent of player controls.
2026-02-15 16:56:24 +01:00
02393c868a feat: implement track selection buttons for quality, audio, and subtitles 2026-02-15 16:41:35 +01:00
f9e8775034 refactor: use HSL for Color palette 2026-02-15 15:25:36 +01:00
51198fe90e feature: Add next up section to homepage 2026-02-15 15:09:04 +01:00
7b870ccd3a refactor: remove mediaId parameter from getNextUpEpisodes function 2026-02-15 14:55:09 +01:00
8ca3f1a3cc fix: move all network I/O off the main thread to prevent UI freezes
Wrap all JellyfinApiClient suspend functions with withContext(Dispatchers.IO)
so callers (ViewModels on Main dispatcher) no longer block the UI thread.
Replace runBlocking in JellyfinAuthInterceptor with a reactive cached token
to avoid blocking OkHttp threads. Add IO dispatching to player MediaRepository
for DataStore reads.
2026-02-09 20:43:00 +01:00
c1f733d1f3 feat: add artwork thumbnails to player queue
- Inject UserSessionRepository into MediaRepository to access server URL
- Build artwork URLs using JellyfinImageHelper for both initial and next-up episodes
- Add artworkUrl parameter to MediaItem metadata via setArtworkUri()
- Fix PlayerQueuePanel thumbnail display with proper 4:3 aspect ratio
- Increase next-up queue count from 2 to 5 episodes
2026-02-09 20:36:50 +01:00
57a9f4f236 feat: Do not show all unwacthed episodes only 9+ 2026-02-09 20:09:28 +01:00
eff2e3a0e9 feat: enhance login flow with error handling and user feedback 2026-02-09 19:52:04 +01:00
cc9a82a4cf feat: refactor media retrieval to use combined flows for episodes and continue watching 2026-02-08 17:56:40 +01:00
f932507469 feat: enhance series data handling with reactive flows and content observation 2026-02-07 16:48:56 +01:00
e3b13f2ea7 feat: implement watch progress tracking and home page content refresh on apperance. 2026-02-07 16:13:07 +01:00
7951315048 feat: add unwatched episode count indicators to PosterItems and enhance media item data structure 2026-02-07 00:10:12 +01:00
4c7d6317c8 refactor: code cleanup 2026-02-05 20:14:33 +01:00
6977acc60f feat: enhance media item retrieval to include resume position calculation 2026-02-05 20:13:06 +01:00
3fed91aa27 feat: add playback reporting to Jellyfin server
Report playback start, progress (every 5s), and stop events to enable
session tracking on the Jellyfin dashboard and accurate resume positions.
2026-02-04 22:00:41 +01:00
08373eb878 refactor: extract itemFields list for cleaner API method calls in JellyfinApiClient 2026-02-04 20:07:36 +01:00
4300c8ce84 feat: add MediaProgressBar component and integrate into SeriesComponents for episode progress display 2026-02-04 14:20:34 +01:00
0ebd5c5afe feat: enhance WatchStateIndicator to include started state and update usage in SeriesComponents 2026-02-04 10:18:38 +01:00
d5027047ce feat: add WatchStateIndicator component to display episode watch status in SeriesScreen 2026-02-04 10:02:28 +01:00
88b34c4780 implement latest library content loading and refactor Home screen state
- Implement `loadLatestLibraryContent` in `InMemoryMediaRepository` to fetch and categorize latest media from Jellyfin libraries.
- Update `HomePageViewModel` to use `mapLatest` and `stateIn` for asynchronous, thread-safe loading of "Continue Watching" and "Latest" content.
- Refactor `HomePage` and its UI components (`HomeContent`, `HomeDrawer`, `HomeSections`) to pass data and callbacks as parameters instead of using `hiltViewModel()` internally.
- Enhance `PosterCard` and `ContinueWatchingCard` with explicit click listeners and image request optimization using `Coil`.
- Add `SeasonMedia` type to the `Media` sealed class to support more granular library item tracking.
- Standardize `UUID` usage for media selection callbacks across `LibraryViewModel` and common UI components.
- Improve UI styling by replacing shadows with subtle borders and consistent corner radii on media cards.
2026-02-01 07:56:46 +01:00
b643988ed4 update JellyfinApiClient methods and logging
- Rename `getNextUpEpisode` to `getNextUpEpisodes` and update it to return a list of episodes with expanded request fields.
- Rename `getMediaPlaybackInfo` to `getMediaPlaybackUrl` and update its usages in `MediaRepository`.
- Simplify logging format by removing curly brace placeholders across multiple API call methods.
- Reorder `getContinueWatching` and `getLibraryContent` for better class organization.
- Remove unused `episode` import in `MediaRepository`.
2026-01-31 21:39:42 +01:00
fa76517e12 implement Room local data source and refactor media repository
- Integrate Room database with entities for `Movie`, `Series`, `Season`, `Episode`, and `CastMember`.
- Implement `RoomMediaLocalDataSource` to handle persistent storage and retrieval of media data.
- Refactor `InMemoryMediaRepository` to use the local data source and synchronize with the Jellyfin API.
- Update `HomePageViewModel`, `SeriesViewModel`, and `EpisodeScreenViewModel` to leverage the new repository logic.
- Replace generic `ItemDto` with specific `MovieDto`, `SeriesDto`, and `EpisodeDto` for type-safe navigation.
- Refactor UI models and components in `SeriesScreen`, `EpisodeScreen`, and `HomeSections` to use domain models directly.
- Enhance `JellyfinApiClient` requests to include necessary fields like `CHILD_COUNT` and `PARENT_ID`.
- Update Gradle dependencies to include Room and KSP.
2026-01-31 21:30:12 +01:00
7cde4b357e Remove local database because it is not being used at this moment of the project. 2026-01-30 15:38:03 +01:00
32d0029379 feat: update MediaRepository and related models to support asynchronous data fetching and add CastMember data class 2026-01-30 15:35:01 +01:00
15190a657a feat: add Room database entities and DAOs for Episodes, Seasons, and Series 2026-01-29 18:16:11 +01:00
ceb1c141cf refactor: rename Route objects 2026-01-29 08:50:46 +01:00
d057bdaab0 feature: Create models for MediaRepository and MediaRepository interface. It will be used for abstract generalized MediaManagement. 2026-01-29 08:50:23 +01:00
88e9ca229e refactor: abstract player logic into PlayerManager and MediaRepository
- Introduce `PlayerManager` to encapsulate `Media3` player interactions, state management, and UI updates.
  - Manages playback state (playing, buffering, ended, error), progress, metadata, track selection, and the media queue.
  - Exposes state via `StateFlow` for reactive UI updates.
  - Handles player lifecycle and event listeners.
- Create `MediaRepository` to fetch media items and upcoming episodes from the Jellyfin API.
  - Abstracts away the logic for retrieving media sources, playback URLs, and constructing `MediaItem` objects.
  - Includes a method to get the next episodes for auto-play, avoiding duplicates already in the queue.
- Implement `TrackMapper` to convert Media3 `Tracks` into a `TrackSelectionState` model for the UI.
- Refactor `PlayerViewModel` to delegate all player and data-fetching logic to `PlayerManager` and `MediaRepository`.
  - The ViewModel now observes state flows from the manager and orchestrates UI actions (e.g., auto-hiding controls).
  - Simplifies the ViewModel by removing direct player listener implementation, progress loops, and track parsing.
  - Improves error handling for invalid media IDs and data loading issues.
2026-01-28 21:08:33 +01:00
eae843312c feat: implement auto-play next episode and up-next queue
- Implement `getNextEpisodes` in `JellyfinApiClient` to fetch a list of upcoming episodes for a given series.
- In `PlayerViewModel`, automatically load the next few episodes into the media queue when a new item starts playing.
- Attach `MediaMetadata` (e.g., title) to `MediaItem` objects to ensure the correct title is displayed for queued items.
- Trigger loading of the next episodes in the `onMediaItemTransition` player callback.
- Refactor video playback and queueing methods (`playVideo`, `addVideoUri`) to accept and utilize `MediaMetadata`.
2026-01-28 20:24:50 +01:00
abf66c75e1 refactor: move HorizontalSeekGestureHelper to helper package 2026-01-28 17:43:23 +01:00
aeec3784be refactor(player): replace side sliders with centered adjustment indicators
- Replace the horizontal `PlayerSideSliders` with a new vertical `PlayerAdjustmentIndicator` for brightness and volume, displaying it in the center of the screen.
- Create `PlayerAdjustmentIndicator.kt`, a new composable that shows an icon, a vertical progress bar, and a percentage value within a semi-transparent black background.
- Update `PlayerScreen` to use the new centered indicator instead of the old side-aligned sliders when brightness or volume is adjusted via gestures.
- Pass a `modifier` to `ValueChangeTimedVisibility` to correctly position the new indicators.
- Change the background color of the "Seek to" toast message from a theme surface color to semi-transparent black for better consistency.
2026-01-27 20:23:18 +01:00
d24eff19cc refactor: improve player gesture feedback with timed visibility components
- Replace `LaunchedEffect` and manual state handling for showing/hiding player overlays (brightness, volume, seek feedback) with new reusable timed visibility composables.
- Introduce `ValueChangeTimedVisibility` to automatically show brightness and volume sliders for a short duration when their values change.
- Implement `EmptyValueTimedVisibility` to display the horizontal seek amount indicator and hide it after a delay.
- Remove redundant state variables (`brightnessOverlayVisible`, `volumeOverlayVisible`, `showFeedbackPreview`) and related logic from `PlayerScreen`.
- Clean up `PlayerGesturesLayer` by removing the `setFeedBackPreview` callback, simplifying its responsibility.
- Remove the auto-hide `LaunchedEffect` from `PlayerSideSliders` as this is now handled externally.
2026-01-27 19:16:23 +01:00