Commit Graph

180 Commits

Author SHA1 Message Date
781d36cc99 refactor: replace Room backing store in InMemoryMediaRepository with MutableStateFlows
- Remove RoomMediaLocalDataSource dependency from InMemoryMediaRepository;
  libraries, movies, series, and episodes are now held in MutableStateFlows
- observeSeriesWithContent derives from _series flow via map {}
- updateWatchProgress mutates _movies / _episodes in-memory
- ensureSeriesContentLoaded checks and updates _series in-memory
- Reorganize Room package: move entities/DAOs from local/room to room/entity and room/dao
2026-02-22 11:05:28 +01:00
c72283c566 refactor: replace NavigationDrawer with NavigationBar on HomeScreen
Converts the mobile home screen from a ModalNavigationDrawer to a
bottom NavigationBar with three tabs: Home, Libraries, and Downloads.
2026-02-22 11:05:27 +01:00
6f34190ed0 feat: add player screen to TV app via compose navigation
- Add PlayerRoute to the Route sealed interface
- Refactor PlayerViewModel to expose loadMedia() for external callers
- Add onPlay() to EpisodeScreenViewModel and MovieScreenViewModel
- Wire play/resume buttons in TV episode and movie screens
- Create TvPlayerScreen with TV-optimized controls: seek bar, playback
  buttons, track selection panels, queue panel, and state cards
- Register PlayerRoute in TvRouteEntryBuilder and TvNavigationModule
- Add media3-ui dependency to app-tv module
2026-02-22 11:05:27 +01:00
f85ecc04c7 feat: add content screens to TV app (movies, series, episodes)
- Created MovieScreen, MovieComponents for movie detail view
- Created SeriesScreen, SeriesComponents for series detail with season selection
- Created EpisodeScreen, EpisodeComponents for episode detail view
- Copied all required common UI components from mobile app
- Updated TvNavigationModule with route entry builders
- Updated TvRouteEntryBuilder with new screen entries
- Added missing launcher resources for TV app build
- All screens reuse ViewModels from :feature:shared module
- TV app now supports browsing movies, series, and episodes
2026-02-22 11:05:27 +01:00
75f3313aa9 feat: add :app-tv module as Android TV starting point
Creates a new :app-tv application module that reuses shared business
logic from :core:model, :core:data, :core:player, and :feature:shared,
with its own TV-specific UI layer.

- TvApplication + TvActivity with full Hilt + Navigation3 wiring
- TvHomePage reusing HomePageViewModel from :feature:shared
- Copied common UI components (PosterCard, WaitingScreen, etc.)
- Copied login screen and form components
- TV navigation module with Home + Login route entry builders
- Material3 theme copied from mobile (ready for TV customization)
- AndroidManifest with LAUNCHER + LEANBACK_LAUNCHER intent filters
- Excludes :feature:download (downloads are mobile-only)
2026-02-22 11:05:27 +01:00
7333781f83 refactor: modularize app into multi-module architecture 2026-02-22 11:05:26 +01:00
8601ef0236 Added README.md 2026-02-22 11:05:26 +01:00
feb2bdd9e9 fix: do not show next up section header if no content available 2026-02-22 11:05:26 +01:00
455d8957fd fix: remove left in background for gesture testing 2026-02-22 11:05:26 +01:00
72982a0f48 fix: fix gesture layout 2026-02-22 11:05:26 +01:00
969f6dc5fd revert: restore separate ContinueWatching and NextUp sections
Manually reverts c5b613e which combined the two sections. Restores
the NextUp UI section, data flow, and dedicated API call.
2026-02-22 11:05:15 +01:00
a86d496ff9 fix: consume vertical drag events to prevent overlay toggle
Vertical drag gestures (brightness/volume) were not consuming pointer
events, causing detectTapGestures to fire onTap and toggle the player
overlay when the gesture ended.
2026-02-22 11:05:05 +01:00
6191098158 feat: use MediaResumeButton with progress in Movie and Episode screens
Replace MediaPlayButton with MediaResumeButton in both MovieComponents
and EpisodeComponents, converting Double progress (0-100) to Float (0-1).
2026-02-22 11:04:25 +01:00
17ddc0b160 feat: update WatchStateIndicator and UnwatchedEpisodeIndicator sizes and styles 2026-02-19 17:51:22 +01:00
f3dd2a0783 feat: remove quick play button from the ContinueWatchingCard on Homepage 2026-02-19 11:03:41 +01:00
62606fc7a4 cleanup: remove unused component 2026-02-19 11:01:20 +01:00
90a66ef7e3 fix: Change SearchField bg color so it is can be seen easier 2026-02-19 10:57:27 +01:00
7d564c2470 feat: Generated MUI3 Color scheme 2026-02-19 10:56:37 +01:00
ed7d0f23d6 fix: Prevent rendering empty Continue Watching section 2026-02-18 20:13:02 +01:00
c5b613e301 refactor: combine ContinueWatching and NextUp sections. 2026-02-18 19:55:51 +01:00
82547a781c feat: Add HomeData caching for faster application loading 2026-02-18 19:05:53 +01:00
198e40d1e8 fix: Prevent duplicate API calls on app startup
ensureReady() had a race condition where concurrent callers from init
block and ViewModels would all run the loading logic simultaneously.
Added Mutex to ensure only the first caller loads; others await.

Also added a cooldown to refreshHomeData() since LifecycleResumeEffect
fires immediately on first composition, triggering a redundant reload
right after the initial load completes.
2026-02-18 18:52:05 +01:00
9a9cb9c2e7 refactor: Do not use JellyfinApiClient in viewModels. Use MediaRepository for consistency 2026-02-18 18:37:57 +01:00
1a46247da0 feat: Integrate OfflineRoomMediaLocalDataSource into InMemoryMediaRepository for library management 2026-02-18 18:24:26 +01:00
a0dbef3dc1 feat: Added library to the database scheme and to the MediaRepository. LibraryViewModel now uses it. 2026-02-18 17:56:29 +01:00
e7d2fa3d62 feat: Use fraction for the size of MediaHero component 2026-02-18 15:53:24 +01:00
88a8f6f90d fix: Add default index if season wihtout index received from server 2026-02-18 12:08:15 +01:00
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