Unified Season & Episode Experience with Smart Playback and PiP#3
Merged
Conversation
…een` for seasons
This commit refactors the app's navigation and data display logic by replacing the dedicated `EpisodeListScreen` with a more versatile `ItemDetailScreen` that can now handle `AfinitySeason` items. This streamlines the user experience and reduces code duplication.
Instead of navigating to a separate screen for episodes, clicking a season now navigates to the standard item detail view, which is enhanced to load and display episodes using paging.
### Key Changes:
* **Navigation:**
* The route for `Destination.EPISODE_LIST_ROUTE` now points to `ItemDetailScreen` instead of the now-removed `EpisodeListScreen`.
* `SeasonsSection` now navigates directly to the item detail route for the selected season.
* **`ItemDetailViewModel` & `ItemDetailScreen`:**
* `ItemDetailViewModel` can now load `AfinitySeason` items.
* When a season is loaded, it initializes an `EpisodesPagingSource` to load episodes asynchronously.
* `ItemDetailScreen` renders a `SeasonDetailContent` composable when the loaded item is a season.
* **Episode Interaction:**
* Introduced a reusable `EpisodeDetailOverlay` to show detailed information when an episode is clicked from any screen (e.g., "Continue Watching," "Next Up," or the new season detail view).
* Clicking an episode now triggers this overlay, from which the user can play, favorite, or mark it as watched.
* `HomeViewModel` and `ItemDetailViewModel` now share logic for selecting an episode and handling interactions within the overlay.
* **Code Cleanup:**
* The `EpisodeListScreen.kt`, `EpisodeListContent.kt`, and `EpisodeListViewModel.kt` files have been removed or commented out as they are now obsolete.
This commit introduces a new file, `SeasonDetailContent.kt`, which defines a dedicated composable for displaying the details of a season. The `SeasonDetailContent` composable arranges various sections vertically, including: * Tagline and Overview * External Links * A horizontal `LazyRow` for episodes, utilizing paging for efficient loading. * Special Features * Cast information The episode list is presented as a scrollable horizontal row with loading indicators for initial refresh and subsequent appends.
This commit introduces a new `EpisodeDetailOverlay` composable, which displays detailed information about a TV show episode in a modal bottom sheet.
The overlay shows the episode's image, title, season/episode number, air date, duration, and community rating. It also provides action buttons to play/resume, mark as watched, favorite, add to watchlist, and navigate to the series detail screen.
This new component is triggered from the home screen, and the `HomeScreen` has been updated to clear the selected episode state when navigating away to the series details.
**Key changes:**
* **New File:** `EpisodeDetailOverlay.kt` containing the `ModalBottomSheet`-based UI.
* **`HomeScreen.kt`:**
* Added logic to call `viewModel.clearSelectedEpisode()` when the user navigates from the episode overlay to the full series page, ensuring correct state management.
This commit enhances the user experience for the episode detail overlay and the season detail screen.
The episode detail overlay now animates out smoothly, and navigation to the series detail page is delayed until the exit animation completes, preventing jarring UI transitions. This also includes a fix where the episode data was being re-fetched unnecessarily, which could cause a flicker.
For season detail pages, this commit significantly improves the presentation by displaying the show's backdrop and logo instead of the season's specific artwork, creating a more consistent and polished look.
**Key Changes:**
* **`HomeScreen.kt`**:
* Wrapped the `EpisodeDetailOverlay` in an `AnimatedVisibility` block to provide a smooth slide-out and fade-out exit animation.
* Introduced a 300ms delay after the overlay is dismissed before navigating to the series detail page, allowing the animation to complete.
* **`ItemDetailScreen.kt` & `HeroSection.kt`**:
* Updated the logic to display the parent show's backdrop and logo on the `AfinitySeason` detail page, falling back to the season's own images if necessary.
* **`HomeViewModel.kt` & `ItemDetailViewModel.kt`**:
* Refactored `onEpisodeClick` to avoid re-fetching and replacing the `selectedEpisode` state with the full detail object. This prevents a brief flicker and ensures the overlay displays the initially selected episode data while full details load in the background.
* Added support for toggling favorite and watched status for `AfinitySeason` items.
* **`EpisodeDetailOverlay.kt`**:
* Wrapped image URL and blurhash lookups in `remember(episode.id)` to prevent re-composition.
* **`FieldSets.kt`**:
* Added `ItemFields.OVERVIEW` to the `CONTINUE_WATCHING` field set, ensuring the episode overview is available in the "Continue Watching" section without an extra API call.
…asons
This commit introduces support for adding and viewing seasons in both the "Favorites" and "Watchlist" sections of the application.
Previously, users could only add movies, shows, and episodes. This change extends that functionality to seasons, allowing them to be displayed in their respective UI sections.
**Key changes:**
* **API & Repositories:**
* Added `getFavoriteSeasons` to `JellyfinMediaRepository` to fetch favorite seasons from the Jellyfin API.
* Implemented `getWatchlistSeasons` in `WatchlistRepositoryImpl` to retrieve seasons from the local watchlist database.
* Updated the `JellyfinRepository` and `MediaRepository` interfaces to include the new `getFavoriteSeasons` method.
* **ViewModels & UI State:**
* `FavoritesViewModel` and `WatchlistViewModel` now fetch and manage a list of `AfinitySeason` objects.
* `FavoritesUiState` and `WatchlistUiState` have been updated to include a `seasons` list.
* **UI:**
* The "Favorites" and "Watchlist" screens now render a dedicated horizontal row for seasons if any are present.
* The empty-state logic on both screens has been adjusted to account for the new seasons list.
* **Item Detail:**
* The `ItemDetailViewModel` now correctly identifies the `itemType` as "SEASON" when adding a season to the watchlist.
This commit introduces a "Play" button on the season detail screen, which intelligently determines the correct episode to play.
The logic for selecting an episode prioritizes them in the following order:
1. **Continue Watching:** The most recent in-progress episode within that season.
2. **Next Up:** The next available episode from the Jellyfin "Next Up" list for that season.
3. **First Unwatched:** The first episode in the season that has not been played.
4. **First Episode:** If all episodes have been watched, it defaults to the first episode of the season for rewatching.
This feature enhances the user experience by allowing direct playback from the season view without needing to manually select an episode.
**Key changes:**
* **`JellyfinRepository`**: Added `getEpisodeToPlayForSeason` to the interface and implementation to contain the new playback logic.
* **`ItemDetailViewModel`**: Exposed the new repository method to the UI.
* **`ItemDetailScreen`**:
* Added a smart play button to the `AfinitySeason` detail view.
* The button's state changes to "Resume," "Rewatch," or "Play" based on the selected episode's watch progress.
* A loading indicator is displayed while the episode is being determined.
* The existing smart play button logic for TV Series was also refactored for consistency.
This commit significantly refactors and optimizes the logic for determining the next episode to play for both a series and a season, improving performance and reliability.
Previously, finding the next episode involved multiple, separate API calls: one for "continue watching," followed by a "next up" call, and finally a manual fallback that fetched all episodes to find the first unwatched one. Each of these steps could require an additional `getFullEpisodeDetails` call to get complete data.
The new implementation streamlines this process:
* **Optimized `NextUp` Call:** The logic now prioritizes a single, more efficient call to the `NextUp` API. It requests the `ITEM_DETAIL` field set directly, which includes all necessary information (like media sources), eliminating the need for a follow-up `getFullEpisodeDetails` call.
* **Removed Redundant Checks:** The initial, inefficient check against the "Continue Watching" list has been removed in favor of the more direct `NextUp` API.
* **Improved Fallback Logic:**
* In the fallback scenario for a series, it now fetches all episodes from all seasons at once before sorting, which is more efficient than iterating through each season individually.
* The fallback logic also now requests the `ITEM_DETAIL` field set when fetching episodes, avoiding extra API calls.
* **Type-Safe Sorting:** Episode sorting for series now correctly uses both `parentIndexNumber` (season) and `indexNumber` (episode) for more reliable ordering.
…de overlay
This commit enhances the episode detail overlay by accurately reflecting and managing an episode's watchlist status.
The `EpisodeDetailOverlay` now receives an `isInWatchlist` boolean to correctly display the bookmark icon. This status is fetched when an episode is selected and is now managed in both `HomeViewModel` and `ItemDetailViewModel`.
Toggling the watchlist now uses an optimistic update approach: the UI state is updated immediately, and then reverted only if the repository call fails. This provides a more responsive user experience.
**Key Changes:**
* **UI/UX:**
* `EpisodeDetailOverlay` now accepts and uses `isInWatchlist` to correctly render the watchlist icon state.
* `HomeScreen` and `ItemDetailScreen` now collect `selectedEpisodeWatchlistStatus` from their respective ViewModels and pass it to the overlay.
* The player loading and error screens now have a black background, and the error text is white for better visibility.
* **ViewModel & State Management:**
* `HomeViewModel` and `ItemDetailViewModel` now manage `selectedEpisodeWatchlistStatus` as part of their state.
* When an episode is selected, its watchlist status is fetched from the `watchlistRepository`.
* The `toggleEpisodeWatchlist` function in both ViewModels now performs optimistic updates for a snappier UI, with error handling to revert the state on failure.
* **Refactor (`ItemDetailScreen.kt`):**
* The logic for fetching the "next episode to play" for shows and seasons has been centralized in the `ItemDetailViewModel`.
* Removed the local `LaunchedEffect` and state (`episodeToPlay`, `isLoadingEpisode`) from the composable, simplifying the UI and relying on the `nextEpisode` state from the ViewModel.
This commit enhances the Item Detail screen by adding logic to check if a trailer exists for the current media item (`AfinityMovie`, `AfinityShow`, or `AfinityVideo`). The "Play Trailer" button is now disabled and visually greyed out if no trailer is found, preventing users from clicking it unnecessarily and providing clearer UI feedback. This check is implemented in both landscape and portrait layouts.
This commit introduces Picture-in-Picture (PiP) support for the video player, allowing users to continue watching in a floating window.
A new setting, "Picture-in-Picture Home Gesture," has been added to the Player settings screen. When enabled, using the home gesture or button during playback will trigger PiP mode. PiP can also be activated manually via a new icon in the player controls.
**Key Changes:**
* **Player & UI:**
* `PlayerActivity` now manages PiP lifecycle events, including entering and exiting PiP mode, and handling playback controls within the PiP window (Play/Pause).
* `PlayerState` is updated with `isInPictureInPictureMode` to track the PiP status.
* Player controls are now hidden when in PiP mode.
* Introduced new `ic_play.xml` and `ic_pause.xml` vector drawables for PiP controls.
* **Settings:**
* A new `SettingsSwitchItem` for enabling the PiP home gesture is added to the "Player" section.
* `PreferencesRepository` and `SettingsViewModel` are updated to store and manage the new PiP gesture preference.
* **Refactoring:**
* Removes optimistic UI updates from the watchlist toggle logic in `HomeViewModel` and `ItemDetailViewModel` for more reliable state handling.
* The `DisposableEffect` for `ON_PAUSE`/`ON_RESUME` in `PlayerScreen` has been removed, as the logic is now handled more robustly in `PlayerActivity` to accommodate the PiP lifecycle.
* Cleans up commented-out code and redundant comments in the Jellyfin repository implementations.
This commit removes the `EpisodeListScreen`, `EpisodeListViewModel`, and `EpisodeListContent` files, which are no longer in use. The functionality for displaying season and episode details has been integrated into other parts of the application, making these dedicated components obsolete. This cleanup simplifies the codebase by removing dead code and also updates related code comments in `FieldSets.kt` to reflect the removal.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
This PR refactors and enhances the handling of seasons and episodes across the app. It consolidates navigation, improves the user interface, introduces intelligent playback, and adds Picture-in-Picture (PiP) support, while also optimizing backend logic and cleaning up obsolete code. The result is a smoother, more intuitive experience for users interacting with shows, seasons, and episodes.
Key Changes
Unified Navigation and Season Detail
EpisodeListScreenwithItemDetailScreenfor seasons.SeasonDetailContentcomposable for detailed season pages with tagline, overview, episodes (with paging), special features, and cast.Episode Detail Overlay
EpisodeDetailOverlayas a modal bottom sheet showing episode details and actions: play/resume, favorite, mark watched, watchlist, and navigate to series.Favorites & Watchlist Support for Seasons
Smart Playback
UI and UX Improvements
Picture-in-Picture (PiP) Support
PlayerActivity, triggered by home gesture or player icon.PlayerState.Cleanup
EpisodeListScreen,EpisodeListViewModel, andEpisodeListContent.Impact
Testing Notes