Make LocalMediaRepository extend MediaMetadataUpdater directly instead of the
now-removed LocalMediaUpdater, eliminating the redundant intermediate interface.
Add no-op updatePlaybackPosition stubs to all LocalMediaRepository implementations
since server-side position tracking belongs in the remote layer of the composite.
JellyfinMediaMetadataUpdaterImpl continues to act as the composite that combines
LocalMediaRepository (local cache) with JellyfinApiClient (remote server) under
the single MediaMetadataUpdater contract.
Replace the disparate JellyfinMediaMetadataUpdater, LocalMediaUpdater,
and direct API/repository calls for watched status and progress updates
with a single unified MediaMetadataUpdater interface and implementation.
The new implementation coordinates both local cache (in-memory/Room)
and remote server updates, so callers have a single point of entry
for all metadata mutations. markAsWatched now updates both server
and local state, fixing a gap where the UI would not reflect
watched changes until a full refresh.
When offline, getNextUpPlayableMedias() only tried the Jellyfin API which silently returns an empty list on failure. This broke the queue population for offline playback, meaning no playlist was built and autoplay-next could never fire.
Add an offline fallback path that queries the Room database for the current episode's series, finds the next episodes ordered by (seasonIndex, index), and resolves them as downloaded PlayableMedia via getPlayableMedia().
Offline downloaded media always started playback from the beginning because getOfflineDownloadedPlayableMedia() hardcoded resumePositionMs to 0L, ignoring the progress percentage stored in the Room database.
Added calculateOfflineResumePosition() that parses the runtime string and converts the stored progress percentage (0-100) to milliseconds, applying the same 5%-95% threshold as the online path.
When playback position reaches 80% of runtime, send the full runtimeTicks to the server so items appear completely watched, matching the played flag already being set at this threshold.
Add a home refresh side effect that compares offline progress with Jellyfin user data and syncs the furthest playback position.
Use Jellyfin playbackPositionTicks for server updates while keeping percent-based writes for local offline and in-memory repositories.
Move playback-related media state into shared model types and make the player consume a single PlayableMedia object instead of separately loading MediaItems, track preferences, resume positions, and media segments.
This updates PlayableMediaRepository to return playable media bundles, seeds the player playlist with the current item, keeps next-up entries in the same model, restores resume/segment handling, and fixes current queue highlighting.
The change also moves track preference models into core-model, centralizes Jellyfin BaseItemDto conversion, removes placeholder movie audio/subtitle fields, bumps the offline Room schema, and updates mobile/TV queue UI code for the playlist model.
Verification:
./gradlew --no-daemon :core-model:compileDebugKotlin :core:compileDebugKotlin :data:compileDebugKotlin :app:compileDebugKotlin