Skip to content

V12 support - #230

Merged
MakD merged 50 commits into
masterfrom
v12-support
Sep 15, 2026
Merged

MakD merged 50 commits into
masterfrom
v12-support

Conversation

@MakD

@MakD MakD commented Sep 15, 2026

Copy link
Copy Markdown
Owner

No description provided.

MakD added 30 commits August 30, 2026 09:45
This commit updates several project dependencies in the version catalog to their latest versions, including core networking, navigation, and UI libraries.

### Key Changes:

*   **Core SDKs & Networking**:
    *   Updated `jellyfinCore` from `1.7.1` to `1.9.0-beta.3`.
    *   Updated `okhttp` and `okhttp-logging` from `5.4.0` to `5.5.0`.

*   **UI & Navigation**:
    *   Updated AndroidX `navigation` from `2.9.8` to `2.10.0`.
    *   Updated `coilCompose` from `3.5.0` to `3.6.0`.

*   **Utilities**:
    *   Updated `aboutlibrariesCore` from `15.0.4` to `15.2.0`.
This commit adds the SLF4J API library to the project's dependencies to support standardized logging abstractions.

### Key Changes:

*   **Dependency Management**:
    *   Added `slf4j` version `2.0.18` to the version catalog in `libs.versions.toml`.
    *   Defined the `slf4j-api` library module within the version catalog.
*   **Build Configuration**:
    *   Added `libs.slf4j.api` as an implementation dependency in `app/build.gradle.kts`.
…logic

This commit refactors the data and repository layers to align with the latest Jellyfin SDK changes. It primarily involves updating deprecated or renamed API classes to their modern counterparts (e.g., transitioning from pluralized names like `ItemsApi` to `LibraryApi`) and refactoring playback state reporting to use structured info objects.

### Key Changes:

*   **Jellyfin SDK API Migration**:
    *   **Consolidated Library APIs**: Replaced usage of `ItemsApi`, `UserLibraryApi`, and `ItemRefreshApi` with the unified `LibraryApi`.
    *   **Singularized API Names**: Updated numerous API classes to their new naming convention, including `ArtistApi`, `GenreApi`, `LyricApi`, `PlaylistApi`, `PersonApi`, `ShowApi`, `StudioApi`, and `VideoApi`.
    *   **Auth & System Updates**: Migrated `QuickConnectApi` and `UserApi` calls to `AuthenticationApi`, and moved `TimeSyncApi` functionality into `SystemApi`.
    *   **Task Management**: Renamed `ScheduledTasksApi` to `ScheduledTaskApi`.

*   **Playback Reporting Refactor**:
    *   Updated `JellyfinPlaybackRepository` and `JellyfinUserDataRepository` to use structured data objects for session reporting.
    *   Implemented `PlaybackStartInfo`, `PlaybackProgressInfo`, and `PlaybackStopInfo` in place of multiple individual parameters when calling `SessionApi` (formerly `PlayStateApi`).

*   **Data Models & Mapping**:
    *   **Trickplay Updates**: Refactored `AfinityTrickplayInfo` and associated mapping logic in `AfinityMovie` and `AfinityEpisode` to handle `TrickplayInfoDto` and improved the selection of high-resolution tiles.
    *   **External IDs**: Simplified `ExternalIdProvider` by removing the `urlFormatString` property.
    *   **Mapping Robustness**: Added null-safety checks (using `orEmpty()`) in `JellyfinModelExtensions.kt` for trickplay metadata mapping.

*   **Infrastructure & Workers**:
    *   Updated `MediaDownloadWorker`, `TrickplayDownloadWorker`, `SubtitleDownloadWorker`, and `LyricsDownloadWorker` to utilize the renamed API classes for fetching item metadata and resources.
    *   **WebSockets**: Updated `JellyfinWebSocketManager` to subscribe to `SyncPlayGroupUpdateMessage` instead of the deprecated command message type.
This commit adds support for predictive back animations within the main navigation graph. By implementing `predictivePopEnterTransition` and `predictivePopExitTransition`, the app now provides a smoother visual transition when using gesture navigation to return to previous screens.

### Key Changes:

*   **Navigation Transitions**:
    *   Updated the root `NavHost` in `MainNavigation.kt` to include custom predictive pop transitions.
    *   Configured both enter and exit transitions to use a 700ms `fadeIn` and `fadeOut` animation, ensuring a more seamless and consistent user experience during back navigation gestures.
This commit integrates the `kotlin-logging` library into the project to support idiomatic, multiplatform-ready logging.

### Key Changes:

*   **Dependency Management**:
    *   Added `kotlin-logging` (version `8.0.4`) to the version catalog in `libs.versions.toml`.
*   **Build Configuration**:
    *   Added `libs.kotlin.logging` to the dependencies in `app/build.gradle.kts`.
This commit introduces a comprehensive in-app log viewer, replacing the previous static log export with an interactive, real-time console. This tool allows for live monitoring of app activity, improved debugging via log grouping, and more granular control over diagnostic data sharing.

### Key Changes:

*   **Core Logging Infrastructure**:
    *   **`RingBufferTree` Refactor**: Updated to store structured `LogEntry` objects instead of raw strings. It now exposes an `updates` flow to notify the UI of new logs and has an increased capacity of 2000 lines.
    *   **`SdkLogBridge`**: Added a bridge to intercept logs from external SDKs (via `kotlin-logging`) and redirect them into the app's `Timber` pipeline.
    *   **Structured Logging**: Introduced `LogEntry` and `LogLevel` to manage log metadata, including timestamps, tags, and stack trace frames.

*   **Log Processing & Intelligence**:
    *   **Log Grouping**: Implemented `LogSignatures` to identify variable patterns (UUIDs, URLs, hex strings) within log messages. This allows the UI to collapse repetitive logs into single groups with occurrence counts.
    *   **Gap Detection**: Added logic to visualize time gaps between log events, helping identify periods of inactivity or processing delays.

*   **Interactive UI (Compose)**:
    *   **`LogViewerScreen`**: A new high-level destination in Settings featuring a live-updating log stream with a "Back to live" following mode.
    *   **Adaptive Density**: Implemented two view modes: a "Compact" console-style view for quick scanning and a "Comfortable" timeline view for detailed inspection.
    *   **Filtering**: Added UI chips to filter logs by severity (All, Warnings, Errors) with real-time count updates.
    *   **Console Styling**: Created `LogLevelColors` to provide standardized, high-contrast syntax highlighting for different log levels.

*   **Privacy & Export**:
    *   **`LogSecretsCollector`**: Centralized the collection of sensitive information (access tokens, server addresses, usernames) from various repositories to ensure thorough redaction.
    *   **Enhanced Export**: Updated `LogExporter` to support exporting either the full buffer or a filtered subset of logs, with automatic redaction of sensitive data.

*   **Integration**:
    *   Registered the new `Logs` destination in `SettingsPaneDestination`.
    *   Updated `SettingsScreen` to navigate to the viewer instead of triggering a direct export.
…ayback

This commit introduces server storage monitoring, a minimum server version enforcement system, and character-level lyrics synchronization for music. It also unifies media stream metadata display and refactors the library filtering system to support language-based queries.

### Key Changes:

*   **Server Management & Compatibility**:
    *   **Storage Monitoring**: Introduced `ServerStorage` models and `ServerStorageDao` to track disk usage and folder paths. Added a storage overview card in the server management UI showing used/free space.
    *   **Version Enforcement**: Added `ServerVersionSupport` to validate that the connected Jellyfin server meets the app's minimum requirements. Introduced `UnsupportedServerDialog` to block access for outdated servers.
    *   **Expanded Stats**: Updated `JellyfinStats` to track more item types, including albums, songs, artists, books, and trailers.

*   **Music & Lyrics Improvements**:
    *   **Karaoke-style Lyrics**: Refactored `MusicLyricsView` and `AfinityLyricLine` to support character-level cues and "karaoke fill" animations during playback.
    *   **Normalization Gain**: Added support for `albumNormalizationGain` in the playback queue and audio service to provide consistent volume levels across single-album playback.
    *   **Lyrics Serialization**: Standardized lyrics encoding/decoding logic with a new helper utility.

*   **UI & Metadata Centralization**:
    *   **`MediaStreamBadges`**: Created a centralized component for displaying video resolution, HDR status (Dolby Vision, HDR10+, HLG), and audio codecs (Atmos, DTS, etc.).
    *   **Metadata Refactoring**: Replaced repetitive badge logic in `MetadataRow` and `EpisodeDetailOverlay` with the new unified component.
    *   **HDR Handling**: Improved detection and labeling of Dolby Vision and HDR10+ profiles from media streams.

*   **Library Filtering**:
    *   **Language Filters**: Added the ability to filter library items by audio and subtitle languages within the `LibraryFilterBottomSheet`.
    *   **Filter Logic**: Updated `ItemFilterCriteria` and `LibraryFilters` to handle language code sets.

*   **Data & Maintenance**:
    *   **BoxSet Refactor**: Removed the local `BoxSetCache` and its associated database tables, migrating to the server's `getItemCollections` API for more reliable relationship mapping.
    *   **Database Migrations**: Incremented database version to 77, adding migrations for normalization gain, extended server stats, and storage caching.
    *   **Localization**: Updated `NetworkModule` to pass the device's preferred locales to the Jellyfin client for localized metadata responses.
This commit refactors the lyrics progress drawing logic to explicitly capture the drawing context before entering clipping blocks. This ensures that `drawContent()` is called on the correct receiver, preventing potential scope ambiguity issues within the `clipRect` lambdas during the rendering of the active lyric line highlights.

### Key Changes:

*   **Rendering Logic**:
    *   Updated `MusicLyricsView.kt` to capture the current scope in a local `content` variable.
    *   Refactored `clipRect` blocks to use the explicit `content.drawContent()` call, ensuring the lyric text is correctly redrawn within the clipped bounds for both completed lines and the currently active line.
…logic

This commit centralizes user avatar rendering by introducing a dedicated `UserAvatar` component. It also refactors the `AfinityTopAppBar` to use a structured `AppBarProfile` data class, ensuring consistent profile display and fallback behavior (images, initials, or icons) across the entire application.

### Key Changes:

*   **UserAvatar Component**:
    *   Created a reusable `UserAvatar` composable that handles profile image loading via `AsyncImage`.
    *   Implemented fallback logic to display the user's first initial when an image is unavailable.
    *   Added a default vector icon fallback for cases where neither an image nor a name is provided.
    *   Included adaptive typography that scales the initial's text size based on the component's dimensions.

*   **Top App Bar Refactoring**:
    *   Introduced the `AppBarProfile` data class to encapsulate profile-related state (name, image URL) and click actions.
    *   Refactored `AfinityTopAppBar` to accept a single `AppBarProfile` parameter, replacing multiple individual profile-related arguments.
    *   Integrated the new `UserAvatar` into the top bar, simplifying its internal layout logic.

*   **UI Consolidation**:
    *   Replaced manual avatar rendering logic with the `UserAvatar` component in `AppNavigationDrawerContent`, `SessionSwitcherBottomSheet`, and `RequestCard`.
    *   Updated all screens (Home, Library, Watchlist, Favorites, Music, and Requests) to use the new `AppBarProfile` structure when configuring their respective top bars.

*   **State Management**:
    *   Updated `MusicLibraryViewModel` and `LibraryContentViewModel` to collect and expose the `userName` from `AppDataRepository`.
    *   Ensured that screens previously only tracking the profile image URL now also track the user's name to support the initials fallback.
This commit introduces the ability for users to "forget" a saved account from the login screen or session switcher. This action removes the account's authentication tokens, clears all user-specific data from the local database, and deletes any downloaded media associated with that account to free up storage.

### Key Changes:

*   **Data Management**:
    *   **`ForgetUserUseCase`**: Introduced a new use case to coordinate the removal of user data. It ensures that the currently active session cannot be forgotten and handles the deletion of both general and Audiobookshelf downloads.
    *   **`ServerDatabaseDao`**: Added a transactional `clearAllDataForUser` method. This performantly deletes rows across multiple tables—including music tracks, metadata caches, and library configurations—that are linked to a specific `userId` and `serverId`.
    *   **Secure Storage**: Updated logic to clear saved tokens for Jellyfin, Jellyseerr, and Audiobookshelf services.

*   **UI Components**:
    *   **`ForgetAccountDialog`**: Created a new reusable confirmation dialog that explains the consequences of forgetting an account (data removal).
    *   **Interaction Model**: Updated `UserAvatarItem` and `SessionItem` to use `combinedClickable`, allowing users to trigger the "forget" flow via a long-press gesture.

*   **ViewModel Integration**:
    *   **`LoginViewModel`**: Added support for forgetting a saved user and refreshing the login list upon success.
    *   **`SessionSwitcherViewModel`**: Implemented session removal logic with error handling and snackbar notifications.

*   **Resources**:
    *   Added localized strings for the forget account confirmation flow and error states.
This commit centralizes the logic for parsing server addresses and generating candidate URLs for Jellyfin, Seerr, and Audiobookshelf services. It also enhances the server management interface with visual port indicators and adds account management features to the navigation drawer.

### Key Changes:

*   **URL Handling & Candidate Generation**:
    *   **`AddressParts`**: Introduced a new data class and utility to robustly parse URLs into scheme, host, path, and port components, including support for IPv6 brackets and sub-paths.
    *   **`UrlCandidates`**: Refactored to use the new parsing logic, ensuring consistent URL probing across the app. It now correctly preserves sub-paths and prioritizes secure ports (e.g., 8920 for Jellyfin).
    *   **ViewModel Cleanup**: Removed redundant `generateCandidateUrls` implementations from `JellyseerrLoginViewModel`, `AudiobookshelfLoginViewModel`, and `JellyfinServerRepository` in favor of the centralized utility.

*   **UI & UX Enhancements**:
    *   **Server List Visuals**: Updated `SharedComponents` to include a `PortPill` for explicit port visibility and `addressAnnotated` to dim URL schemes, making server addresses easier to read.
    *   **Navigation Drawer**: Added a long-press action to server sessions in the sidebar, allowing users to forget an account via a new `ForgetAccountDialog`.
    *   **Localization**: Migrated hardcoded error messages in login flows (e.g., 401, 404, network timeouts) to standardized string resources in `strings.xml`.

*   **Repository & Logic Refinement**:
    *   **Audiobookshelf**: Streamlined the login process by removing the explicit `testConnection` phase and `setServerUrl` state, relying instead on the improved candidate probing logic.
This commit refactors the services hub UI to dynamically arrange service tiles and updates the episode detail bottom sheet to better handle system bars and display cutouts for a more robust edge-to-edge experience.

### Key Changes:

*   **Services Hub Layout**:
    *   **Dynamic Grid**: Refactored `ServicesHubScreen.kt` to use a `buildList` of composable tiles instead of hardcoded `Row` structures.
    *   **Automatic Chunking**: Implemented `tiles.chunked(2)` to automatically group service tiles into rows. This simplifies the logic for conditional elements like the ratings tile and ensures consistent spacing and alignment without manual `Spacer` management.

*   **Window Insets & Edge-to-Edge**:
    *   **`EpisodeDetailOverlay.kt`**: Applied `windowInsetsPadding` to the `ModalBottomSheet` using a union of `systemBars` and `displayCutout` for horizontal sides. This prevents UI elements from being obscured by notches or system navigation in landscape orientation.
    *   **Drag Handle**: Updated the bottom sheet drag handle to respect top window insets, ensuring it remains accessible and correctly positioned relative to the status bar.
This commit improves the music lyrics system by introducing "cue-awareness" to cached data and enhances the media repository's "Continue Watching" logic by filtering for specific resumable item types. Additionally, it improves UI legibility in the lyrics view by dynamically adjusting accent colors for better contrast.

### Key Changes:

*   **Lyrics System & Caching**:
    *   **`CachedLyrics`**: Introduced a new data model to wrap lyric lines with a `cueAware` flag, allowing the repository to distinguish between legacy and enhanced lyric formats.
    *   **Logic Refinement**: Updated `JellyfinMusicRepository` to fetch fresh lyrics from the API if the cached version is not cue-aware, while maintaining a fallback to cached content on network failure.
    *   **Serialization**: Updated `decodeLyricsJson` to detect different JSON structures and correctly assign the `cueAware` state.

*   **UI Legibility**:
    *   **`MusicLyricsView`**: Implemented a `readableOnDark()` extension on `Color` that uses luminance checks and linear interpolation (`lerp`) to ensure accent colors remain visible against dark backgrounds.
    *   **Theming**: Applied the new legibility logic to the active line and karaoke fill indicators.

*   **Media Repository**:
    *   **Resumable Filtering**: Added `RESUMABLE_ITEM_TYPES` (limited to `MOVIE` and `EPISODE`) to `getContinueWatching` and resume queries. This prevents unrelated item types from appearing in the video-centric continue watching carousels.

*   **Code Cleanup**:
    *   Refactored `JellyfinMusicRepository` to use a private `readCachedLyrics` helper for better separation of concerns between raw data retrieval and public API mapping.
…ic library

This commit separates artist browsing in the music library into two distinct views: "Album Artists" and "Artists" (including track artists). It updates the repository layer to support fetching all artists recursively via the Jellyfin Library API alongside the existing Album Artists endpoint, and exposes dedicated UI tabs, filtering options, and paging flows for each view.

### Key Changes:

*   **Data Layer**:
    *   Updated `MusicRepository` and `JellyfinMusicRepository` to accept an `albumArtistsOnly` parameter in `getArtists()`.
    *   Implemented repository fetching logic: queries `ArtistApi.getAlbumArtists` when `albumArtistsOnly` is true, and `LibraryApi.getItems` with `BaseItemKind.MUSIC_ARTIST` when false.
    *   Updated `MusicArtistsPagingSource` to forward the `albumArtistsOnly` flag to repository requests.

*   **UI & State Management**:
    *   Added `LibraryFilter.AlbumArtists` to distinguish between album artist and full artist browsing.
    *   Expanded `MusicLibraryViewModel` and `MusicBrowsePrefs` to maintain separate paging flows (`allArtistsPagingFlow`), letter filters (`allArtistLetterFilter`), and filter states (`allArtistFilters`) for all artists.
    *   Updated `MusicBrowseScreen` and `MusicLibraryScreen` to include dedicated UI grids, filter bottom sheets, FABs, and shortcuts for both artist filters.

*   **Paging Invalidation**:
    *   Updated `LibraryContentViewModel` to invalidate `currentLibraryPagingSource` when receiving admin change events if a paging source is active.

*   **Resources**:
    *   Added string resources for `music_tab_album_artists` and `music_nav_album_artists`.
This commit optimizes the item detail loading flow in `ItemDetailViewModel` by streamlining cache reads, throttling background server syncs, and avoiding unnecessary network requests. It introduces a dedicated `getItemDetail` repository method and conditionally fetches special features only when available.

### Key Changes:

*   **Repository Layer**:
    *   Added `getItemDetail(itemId)` to `MediaRepository` and implemented it in `JellyfinMediaRepository` to directly fetch item details using `LibraryApi`.

*   **Background Sync & Cache Optimization**:
    *   Added a server sync freshness check (`SERVER_SYNC_FRESHNESS_MS = 3_000L`) in `ItemDetailViewModel` to skip background sync if the item was already fetched moments prior.
    *   Updated `refreshFromCacheImmediate` to fetch directly via `loadItemFromDatabase()` and removed redundant background jobs fetching seasons and next episodes during cache refreshes.

*   **Conditional Network Requests**:
    *   Deferred calling `launchParallelFetches` until after the initial item details are loaded to extract `specialFeatureCount`.
    *   Gated `mediaRepository.getSpecialFeatures` calls across series, seasons, and movies behind a `specialFeatureCount > 0` check to reduce unnecessary API calls.
This commit improves loading performance in `ItemDetailViewModel` by parallelizing background data synchronization for shows and triggering the next up episode fetch earlier in the item loading lifecycle.

### Key Changes:

*   **Concurrent Background Sync**:
    *   Refactored the `AfinityShow` background refresh logic to execute `getEpisodeToPlay` and `getSeasons` concurrently in separate coroutines using `coroutineScope`.

*   **Eager Next Up Fetching**:
    *   Moved `fetchNextUp()` to trigger earlier in the item loading flow when online for both `SERIES` and `SEASON` item types.
    *   Removed redundant `fetchNextUp()` invocations from `launchParallelFetches`.
This commit introduces Jellyfin Quick Connect support for Jellyseerr authentication. Users with an active Jellyfin session can now sign in to Jellyseerr seamlessly without manually re-entering their password by authorizing a Quick Connect request directly through Jellyfin.

### Key Changes:

*   **Jellyseerr Quick Connect Flow**:
    *   **Data Models**: Added `QuickConnectInitiateResponse` and `QuickConnectAuthenticateRequest` DTOs.
    *   **Network Layer**: Added Quick Connect initiation and authentication endpoints to `JellyseerrApiService`.
    *   **Repository**: Implemented `initiateQuickConnect()` and `authenticateQuickConnect()` in `JellyseerrRepositoryImpl`, refactoring shared session storage and config creation into a private `persistSession()` helper method.

*   **Jellyfin Auth Repository Enhancements**:
    *   **`QuickConnectAuthorization` Enum**: Replaced basic `Boolean` authorization returns with an explicit `QuickConnectAuthorization` enum (`APPROVED`, `REFUSED`, `UNKNOWN_CODE`, `FAILED`) to accurately handle server responses such as 404 status codes.
    *   **Availability Check**: Added `isQuickConnectEnabled()` to `AuthRepository` to verify server capability before presenting the option.

*   **UI & State Management**:
    *   **`JellyseerrLoginViewModel`**: Implemented `loginWithQuickConnect()` and added reactive state tracking for `quickConnectAvailable` and `isQuickConnecting`.
    *   **`JellyseerrBottomSheet`**: Rendered a "Sign in with Quick Connect" button and visual divider when Jellyfin authentication and Quick Connect are supported.
    *   **String Resources**: Added localized strings and error messages for unsupported server versions, non-approved requests, and account setup failures.
This commit improves UI responsiveness and network efficiency by offloading heavy BlurHash decoding tasks, dynamically sizing backdrops, skipping server-side image resizing on local network connections, and reducing HTTP connection timeouts.

### Key Changes:

*   **Image Loading & Sizing**:
    *   **Local Connection Optimization**: Introduced `LocalSkipServerImageResize` via `CompositionLocal` to bypass server-side image resizing when connected via `ConnectionType.LOCAL`.
    *   **Dynamic Backdrop Dimensions**: Replaced fixed 1920x1080 backdrop target sizes in `ItemDetailScreen` and `SeerrMediaDetailScreen` with dynamic screen dimensions from `LocalWindowInfo`.
    *   **Compression Adjustments**: Updated `optimizedImageUrl` to use a standard image quality parameter of 80 (down from 90) and capped max bucket bounds.
    *   **`HeroCarousel`**: Updated backdrop preloading to respect local connection resizing settings and simplified next-item prefetching logic.

*   **Asynchronous BlurHash Decoding**:
    *   Offloaded `BlurHash` decoding from the main UI thread to a dedicated low-priority single-threaded background executor (`blurhash-decode`).
    *   Implemented a custom `BlurHashPainter` that updates asynchronously once bitmap decoding completes.

*   **Network Configuration**:
    *   Reduced default OkHttpClient connection timeouts and `JELLYFIN_HTTP_OPTIONS` from 15 seconds to 6 seconds for quicker fallback on unreachable servers.
This commit updates the `SdkLogBridge` initialization in `AfinityApplication` to dynamically set log verbosity based on the build type. Detailed debug logs are preserved for debug builds, while release builds are restricted to warning-level logs to reduce unnecessary logging overhead in production.

### Key Changes:

*   **Logging Configuration**:
    *   Updated `SdkLogBridge.install()` to pass `Level.DEBUG` when `BuildConfig.DEBUG` is true, and `Level.WARN` otherwise.
    *   Imported `io.github.oshai.kotlinlogging.Level` in `AfinityApplication.kt`.
This commit refactors the library filter option fetching to occur lazily on demand when the user opens the filter sheet, rather than during initial library initialization. It also introduces a loading indicator within the filter bottom sheet while options are being loaded and updates genre mapping logic in the repository layer.

### Key Changes:

*   **Lazy Options Fetching**:
    *   **`LibraryContentViewModel`**: Removed eager `loadFilterOptions()` call from library initialization. Added `ensureFilterOptionsLoaded()` to fetch options asynchronously on demand and updated `LibraryContentUiState` to include `isLoadingFilterOptions`.
    *   **`LibraryContentScreen`**: Updated the filter floating action button to invoke `ensureFilterOptionsLoaded()` prior to opening the filter sheet.

*   **UI Loading Feedback**:
    *   **`LibraryFilterBottomSheet`**: Added an `isLoadingOptions` parameter and introduced a centered `CircularProgressIndicator` when filter options are in a loading state.
    *   **`CustomSectionsScreen`**: Passed loading state to `LibraryFilterBottomSheet` and ensured the template selection dialog correctly dismisses upon selecting a preset.

*   **Repository Enhancements**:
    *   **`JellyfinMediaRepository`**: Refined `getFilterOptions` to prefer non-blank genres parsed from modern responses, using legacy genre lists as a fallback.
This commit introduces a preference allowing users to enable or disable the side sheet overlay for episode details in landscape and wide window layouts. When disabled, the interface falls back to using a bottom sheet layout.

### Key Changes:

*   **Preference Layer**:
    *   Added `SIDE_SHEET_ENABLED` key to `PreferencesRepository` with corresponding getters, setters, and `Flow` streams.
    *   Exposed `sideSheetEnabled` state flows in `SettingsViewModel` and `MainNavigationViewModel`.

*   **Settings UI & Resources**:
    *   Added `ic_sidesheet.xml` vector drawable.
    *   Added `pref_side_sheet_title` and `pref_side_sheet_summary` string resources.
    *   Added a new switch item in `AppearanceOptionsScreen` to control the preference.

*   **Composition & UI Logic**:
    *   Introduced `LocalSideSheetEnabled` composition local in `MainNavigation` to propagate the setting throughout the composition tree.
    *   Updated `EpisodeDetailOverlay` to respect `LocalSideSheetEnabled` when evaluating whether to display the wide side sheet layout.
This commit adds support for filtering music genres and artist queries by a target parent library ID (`parentId`). When a user has a single music library, queries are automatically scoped to that library to prevent mixed content results.

### Key Changes:

*   **Repository Layer**:
    *   **`MusicRepository` & `JellyfinMusicRepository`**: Extended `getMusicGenres`, `getArtistsByGenre`, `getFavoriteArtists`, `getTopArtists`, and `getRandomArtists` to accept an optional `parentId: UUID?` parameter.
    *   Updated underlying Jellyfin `GenreApi` and `ArtistApi` calls to pass `parentId` to the server endpoints.

*   **UI & ViewModels**:
    *   **`MusicLibraryViewModel`**: Updated `loadMusicHomeSections` to resolve the single music library ID (`musicParentId`) and pass it when fetching home screen sections (genres, top/favorite/random artists).
    *   **`MusicGenreViewModel`**: Scoped `getArtistsByGenre` calls using the single music library ID if present.
… library scoping

This commit enhances the music experience across the app by adding related album recommendations, external links support (e.g., MusicBrainz and AudioDB), and library-scoped content filtering. It also standardizes overview text components and adds interactive birthplace location links in person details.

### Key Changes:

*   **Music Feature Enhancements**:
    *   **Album Recommendations**: Added "More from [Artist]" and "More like this" carousels on the album detail screen using a new `AlbumRelatedSection` composable.
    *   **Repository & ViewModels**: Updated `MusicAlbumViewModel` and `JellyfinMusicRepository` to fetch similar albums and artist discographies with parameters to exclude current items.

*   **External Links Integration**:
    *   **Data Models & API**: Added `externalUrls` to `AfinityAlbum` and `AfinityArtist` models, and updated `FieldSets.MUSIC_ALBUM` to include external URLs.
    *   **`ExternalLinksSection`**: Refactored to accept external URL lists directly, adding support and vector drawables for MusicBrainz and AudioDB alongside URL precedence filtering.

*   **Library Scoping & Navigation**:
    *   Updated `MusicRepository` methods and ViewModels (`MusicLibraryViewModel`, `MusicGenreViewModel`) to pass `parentId`, ensuring genre, artist, and track requests are correctly scoped to the current music library.
    *   Updated navigation destinations (`Destination.createMusicGenreRoute`) to preserve and carry the active `libraryId`.

*   **UI Standardisation & Helpers**:
    *   **`OverviewSection`**: Generalized overview text rendering across person, album, and artist screens to seamlessly handle HTML content and expandable text blocks.
    *   **Location Intent**: Added `IntentUtils.openMapLocation` to allow clicking a person's birthplace to launch map applications or browser fallbacks.
This commit introduces comprehensive support for remote session control and remote message display via Jellyfin WebSockets. Users can now monitor and control active playback sessions (play, pause, seek, volume, mute, and track skip) and send messages from the Server Control Panel. In addition, the player and app UI can now process incoming remote commands for playback, volume, stream selection, and message displays.

### Key Changes:

*   **Remote Session Control (Server Settings)**:
    *   **`SessionRemoteSheet`**: Created a bottom sheet component offering full remote control over active Jellyfin sessions, including playback state toggles, interactive seek bar, volume/mute sliders, session termination, and message dispatch.
    *   **`PlayingSessionCard`**: Integrated an inline `SessionControlStrip` for quick playback controls directly on session cards.
    *   **`ControlPanelViewModel` & `JellyfinRepository`**: Implemented API dispatch methods for `PlaystateCommand`, `GeneralCommand`, volume adjustments, and message commands via `SessionApi`. Added optimistic UI state handling with pending pause reconciliation.

*   **Remote Command & Message Processing**:
    *   **`RemoteMessageManager` & `RemoteMessagePill`**: Added a dedicated manager and Jetpack Compose pill overlay component to show incoming remote display messages with auto-dismissal timeouts across `MainActivity` and `PlayerIndicators`.
    *   **`JellyfinWebSocketManager`**: Subscribed to `GeneralCommandMessage` and exposed shared flows for incoming remote playstate, general, and play commands.
    *   **`PlayerViewModel` & `MusicPlaybackManager`**: Added command handlers to process incoming remote playstate (pause, resume, seek, skip), volume adjustments, stream index switching, and mute commands during video and music playback.

*   **Resources & Configuration**:
    *   Added `ic_message_incoming.xml` and `ic_message_outgoing.xml` vector drawables.
    *   Refactored `SUPPORTED_REMOTE_COMMANDS` into a standalone constant in `JellyfinAuthRepository`.
    *   Bumped app version to `0.11.00-beta` (version code `74`) in `gradle.properties`.
This commit improves EPG performance and state management in Live TV, refactors current time ticking to a shared state flow, and introduces guide-specific program querying logic. It also includes layout cleanups for EPG program cells, string resources for active session controls, and search filter adjustments.

### Key Changes:

*   **Live TV & EPG Enhancements**:
    *   **Repository & ViewModel**: Introduced `getGuidePrograms` in `LiveTvRepository` and `JellyfinLiveTvRepository` to query programs using time window constraints (`minEndDate` and `maxStartDate`). Refactored `LiveTvViewModel` to use the new guide query and prevented redundant UI state updates when EPG channels and programs remain unchanged.
    *   **Shared Ticker Flow**: Converted `currentTimeTicker` in `CurrentTime.kt` to a shared `StateFlow` via `stateIn` with `SharingStarted.WhileSubscribed` to prevent redundant flow emissions across multiple EPG cells.
    *   **EPG UI Optimization**: Memoized program filtering and sorting within `EpgProgramRow` using `remember`. Cleaned up `EpgProgramCell` layout to display episode titles, prevent zero-width cell clipping, and simplify badge alignment.
    *   **Time Header**: Updated `EpgTimeHeader` to share the current time state and `remember` date time formatters.

*   **Search**:
    *   Commented out filtering of `LocationType.VIRTUAL` items in `SearchViewModel` search results.

*   **Server Support & Resources**:
    *   Updated `ServerVersionSupport` to define explicit minimum server version `10.0.0`.
    *   Added string resources for active session remote control actions and messaging dialogs.
This commit updates the window insets handling in `PlayerControls` to use `WindowInsets.safeDrawing` for horizontal padding instead of relying strictly on `displayCutout`. For top overlay controls, safe horizontal insets are combined with top display cutout insets using `union` to ensure consistent layout behavior across devices with varying system bar and cutout configurations.

### Key Changes:

*   **Window Insets Handling**:
    *   Replaced `WindowInsets.displayCutout` with `WindowInsets.safeDrawing` for horizontal side insets across player control components.
    *   Utilized `WindowInsets.union` to combine horizontal safe drawing insets with top display cutout insets for top action bars and overlay containers.
…nscode negotiation

This commit introduces dynamic device capability detection and server profile generation for ExoPlayer and MPV, enabling fine-grained transcode negotiation and playback decision resolution. It also adds Wi-Fi and mobile data quality settings for both video and music playback, transcode diagnostic overlays, collection-based auto-queuing, and standardized UI slider components.

### Key Changes:

*   **Device Profiles & Stream Resolution**:
    *   **Codec Detection**: Introduced `DeviceCodecs` and `AndroidDeviceProfileFactory` to query system codec capabilities via `MediaCodecList` and build dynamic `DeviceProfile` instances for ExoPlayer and MPV.
    *   **Stream Decision Engine**: Updated `PlaybackRepository` with `resolveStream()` and `TranscodingUrl` helpers to evaluate playback capabilities, parse `StreamDecision` types (Direct Play, Direct Stream, or Transcode), and inspect transcode reasons or burned-in subtitles.

*   **Streaming Quality Settings**:
    *   **Quality Models**: Added `VideoQuality` and `MusicQuality` ladders providing preset bitrate and resolution thresholds for Wi-Fi and cellular connections.
    *   **Preferences**: Added settings for Wi-Fi and mobile data quality limits, maximum audio channels for transcoding, HDR passthrough toggle, and a "Never transcode" enforcement option.
    *   **Music Player Quality**: Introduced `MusicQualitySheet` and a quality pill indicator in `MusicPlayerScreen` to allow on-the-fly streaming bitrate switching.

*   **Video Player & UI Extensions**:
    *   **Unified Track & Quality Panel**: Extended `TrackPanel` in `PlayerControls` to support video quality selection alongside audio and subtitle tracks, renegotiating server streams when required.
    *   **Transcoding Diagnostics & Fallback**: Added auto-fallback to transcoding upon direct play errors, "Play anyway" options when transcoding is disabled by default, and expanded `PlaybackStatsOverlay` with active server transcode metrics (hardware acceleration, encoding speed, output bitrate, and transcode reasons).
    *   **Playback Badges**: Added resolution indicators (4K, HD, SD) and play method badges (DIRECT vs TRANSCODE) to player controls.

*   **Playlist & Collection Queuing**:
    *   **BoxSet Enrichment**: Added `enrichWithCollectionQueue` to `PlaylistManager` to automatically build movie collection queues from Jellyfin BoxSets.

*   **Component & Resource Standardization**:
    *   **`AfinitySlider`**: Created a centralized wrapper for `Slider` and `RangeSlider` components, replacing duplicated slider code across settings, speed selectors, equalizers, and filter sheets.
    *   **Assets & Strings**: Added badges (`ic_badge_4k`, `ic_badge_hd`, `ic_hdr`, `ic_speaker`, `ic_cellular_data`) and localized string resources for transcode reasons and quality selections. Bumped minimum supported server version to 12.0.0.
…tions sheet

This commit refactors the audio playback interfaces across music and audiobook screens to use standardized UI control slots and layout constraints. It consolidates secondary player actions into a unified "More options" bottom sheet and simplifies Chromecast integration with a reusable composable launcher.

### Key Changes:

*   **Shared Audio Components**:
    *   **`AudioPlayerControls.kt`**: Introduced `AudioPlayerControlRow`, `AudioPlayerControlSlot`, `AudioPlayerValueSlot`, and `AudioPlayerLayout` constants for uniform icon sizing and cover image constraints.
    *   **`PlayerMoreSheet.kt`**: Created modular bottom sheet components (`PlayerMoreSheet`, `PlayerMoreSectionHeader`, `PlayerMoreRow`, `PlayerMoreDivider`) for secondary player actions.

*   **Cast Integration**:
    *   **`CastChooser.kt`**: Created `rememberCastChooserLauncher()` to encapsulate `MediaRouteButton` setup and programmatic click triggering.
    *   Refactored `PlayerScreen`, `MusicPlayerScreen`, and `AudiobookshelfPlayerScreen` to replace boilerplate Cast setup with the new launcher helper.

*   **Player UI Updates**:
    *   **`MusicPlayerScreen`**: Cleaned up the main controls layout using standard `AudioPlayerControlSlot` components. Shifted secondary actions (Equalizer, Audio Quality, Sleep Timer, Add to Playlist, Instant Mix, Start Radio, and Playback Info) into the new `PlayerMoreSheet`.
    *   **`AudiobookshelfPlayerScreen`**: Updated speed, chapter, and sleep timer controls to use standardized control and value slots. Added a "More options" bottom sheet for Equalizer, Skip Silence, and Playback Info.
    *   Applied `AudioPlayerLayout.CoverSizeCap` across player layouts to consistently constrain cover image proportions.

*   **Resources & Helpers**:
    *   Exposed `formatSpeed()` internal helper in `PlaybackSpeedSelector.kt` for value slots.
    *   Added string resources for overflow sheet section headers and state indicators (`On`/`Off`).
…orientation checks

This commit introduces bookmark management for Audiobookshelf items, including full UI integration in the audiobook player, offline Room persistence, WebSocket update handling, and background synchronization. Additionally, it refactors landscape orientation detection across the application into a unified composable helper.

### Key Changes:

*   **Audiobookshelf Bookmarks**:
    *   **Data & API**: Added `AudiobookshelfBookmarkEntity`, Room DAO queries, Retrofit endpoints, and database migration (v77 to v78) to support bookmark storage and API operations.
    *   **Repository & Sync**: Implemented offline-first persistence with background mutation queueing and synchronization via `AudiobookshelfRepositoryImpl` and `AbsProgressSyncWorker`.
    *   **Real-time Updates**: Updated `AudiobookshelfSocketManager` to parse and cache bookmarks from `user_updated` WebSocket payloads.
    *   **Player UI**: Added `BookmarksSheet` and player control action to `AudiobookshelfPlayerScreen`, enabling users to view, create, seek to, and delete bookmarks during playback.

*   **UI & Orientation Refactoring**:
    *   Introduced `isLandscapeWindow()` composable helper using `LocalWindowInfo` container size.
    *   Replaced direct `LocalConfiguration.current.orientation` checks with `isLandscapeWindow()` across various screens, player controls, and home carousels for consistent landscape detection.

*   **Data Resilience & Serialization**:
    *   Updated Kotlinx Serialization settings (`isLenient`, `coerceInputValues`) in `NetworkModule` and `AudiobookshelfSocketManager`.
    *   Added custom `NullableStringFromAny` serializer and fallbacks to `MediaProgress` and `AudiobookshelfUser` data models to improve robustness against unexpected JSON fields.
…s sub-navigation

This commit reorganizes the settings navigation structure and refactors the playback options into a categorized, sub-navigation layout. It also enhances settings building blocks with proper disabled state visual feedback, converts video quality selection to modal dialogs, and updates download screen layout ordering.

### Key Changes:

*   **Playback Settings Categorization**:
    *   **Sub-navigation Flow**: Introduced `PlaybackSection` enum and back handling in `PlayerOptionsScreen` to categorize playback controls into Video Quality, Music Quality, Audio & Subtitle Tracks, Subtitle Appearance, Controls, Chromecast, and Advanced settings.
    *   **Video Quality Picker**: Replaced inline dropdowns with `VideoQualitySelectorItem` and `VideoQualityPickerDialog`, adding hint text and disabled states when the "Never transcode" preference is enabled.
    *   **Buffer Size Selector**: Replaced the buffer size slider with a `DropdownMenu` showing memory impact warnings and size labels.

*   **Settings Screen Restructuring**:
    *   **Group Reorganization**: Reordered main settings into "Playback & downloads" and "Account & servers" groups.
    *   **Custom Sections**: Moved custom section settings item from top-level settings into `AppearanceOptionsScreen`.
    *   **Download Screen**: Updated `DownloadSettingsScreen` layout order to show active and completed downloads higher up, preceding storage and cache controls.

*   **UI Components & Styling**:
    *   **`SettingsItem` & `SettingsSwitchItem`**: Added support for `enabled` states with alpha dimming (`0.38f`) for text, icons, and trailing chevrons, as well as custom `subtitleColor` overrides.
    *   **`SectionHeader`**: Standardized section headers to use `titleSmall` typography without forcing uppercase text conversion.
MakD added 20 commits September 3, 2026 23:53
This commit standardizes the tint color for destructive action icons (such as delete and cancel actions) across the application to consistently use `MaterialTheme.colorScheme.error` instead of hardcoded colors or generic surface tints. Additionally, it updates resource resolution in `PlaylistScreen` and eager-loads Audiobookshelf bookmarks on player initialization.

### Key Changes:

*   **Destructive Action Tinting**:
    *   Standardized delete and cancel icon tints to `MaterialTheme.colorScheme.error` across `DownloadProgressIndicator`, `EditMetadataScreen`, `PlaylistScreen`, `PlaylistEntryRow`, `ItemHeader`, `BookmarksSheet`, `MusicTrackRow`, and `DownloadListItemRow`.
    *   Replaced hardcoded `Color.Red` and `onSurfaceVariant` tints to ensure consistent theme-aware visual cues for destructive actions.

*   **Audiobookshelf Player**:
    *   Updated `AudiobookshelfPlayerViewModel` to eager-load bookmarks on initialization for valid item IDs.
    *   Added a `showLoading` parameter to `refreshBookmarks()` to allow background refreshes without toggling the bookmark loading state UI.

*   **Playlist Resources**:
    *   Refactored `PlaylistScreen` to resolve string and plural resources via `LocalResources.current` when formatting download message snackbars.
…election

This commit refactors the downloads screen into a poster-based grid catalog featuring category filtering, multi-selection bulk deletion, and child item group sheets. Storage location and cache configuration settings are separated into a dedicated `StorageSettingsScreen`.

### Key Changes:

*   **Download Catalog Architecture**:
    *   Introduced `DownloadCatalog` data models (`DownloadCatalogEntry`, `DownloadCatalogRef`, `DownloadCategory`, `DownloadSort`) to aggregate Jellyfin and Audiobookshelf downloads into unified catalog entries.
    *   Implemented grouping logic for TV series, music albums, and podcasts alongside standalone books and videos via `buildDownloadCatalog`.

*   **Grid View & Multi-Selection**:
    *   Replaced the completed downloads list in `DownloadSettingsScreen` with a dynamic `LazyVerticalGrid` featuring poster cards, size badges, and storage availability indicators.
    *   Added category filter chips (`DownloadFilterChips`) to filter between Videos, Music, Audiobooks, and Podcasts.
    *   Implemented multi-selection mode with custom action bars allowing bulk deletion and tracking freed storage space.
    *   Added `DownloadGroupSheet` modal bottom sheet to inspect and delete individual episodes or tracks within grouped entries.

*   **Storage & Cache Settings Separation**:
    *   Created `StorageSettingsScreen` to isolate download preferences (Wi-Fi only, max concurrent downloads), storage locations, and image/video cache management.
    *   Added top app bar action in the main downloads screen to navigate directly to storage settings.

*   **ViewModel & Navigation Updates**:
    *   Updated `DownloadsViewModel` with `catalog` and `availableCategories` state flows combining download state, category filters, and sorting choices.
    *   Added `deleteCatalogEntry` and `deleteCatalogEntries` to handle multi-type item deletions.
    *   Registered `STORAGE_SETTINGS_ROUTE` in navigation graphs and list-detail settings pane scaffolding.
…alculation

This commit refactors the home section refresh logic to perform targeted updates and updates the section interleaving algorithm to be deterministic. It also improves list recycling behavior in `HomeScreen` by factoring in card styles when determining section content types.

### Key Changes:

*   **UI List Optimization**:
    *   Updated `contentType` logic in `HomeScreen.kt` for both pinned and combined sections to pair `section::class` with `section.cardStyle` for `HomeSection.Items` and `HomeSection.Pending`. This prevents composition recycling issues between sections with distinct card layouts.

*   **Repository & Refresh Logic**:
    *   Introduced `refreshContent(reason)` and `refreshDiscoverySections(reason)` in `HomeSectionsRepository` to explicitly refresh active sections and defer off-screen hydration.
    *   Updated `HomeViewModel` to trigger `refreshContent` instead of forcing a full layout recalculation on events.

*   **Spotlight Layout Calculation**:
    *   Refactored `interleaveSpotlights` in `HomeSectionsRepository` to use a deterministic, stride-based placement algorithm.
    *   Removed the random offset logic and the `computeSpotlightPositions` helper method.
…daction

This commit refactors device codec capabilities to accurately detect Dolby Vision and dynamic video ranges, updates playback profile generation, introduces automatic transcode fallback for unsupported video tracks, and ensures exported logs are redacted.

### Key Changes:

*   **HDR & Codec Detection**:
    *   **`DeviceCodecs`**: Added explicit parsing for Dolby Vision profile capabilities and dynamic `VideoRangeType` matching (SDR, HDR10, HLG, Dolby Vision variants).
    *   Added deterministic profile sorting (`PROFILE_ORDER`) for `h264`, `hevc`, and `vp9`.
    *   **`AndroidDeviceProfileFactory`**: Updated video profile conditions to populate supported video ranges from device capabilities instead of defaulting strictly to SDR checks.

*   **Playback Logic**:
    *   **`PlayerViewModel`**: Extracted `forceTranscodeRetry()` helper function to centralize transcoding fallback requests.
    *   Implemented `retryWithoutPlayableVideo()` inside `onTracksChanged` to automatically force transcoding if the device reports no supported video tracks during direct play.

*   **Logging & Security**:
    *   **`LogExporter`**: Wrapped logcat capture output with `LogRedactor.redact()` to sanitize log exports before writing or sharing.

*   **UI & Localization**:
    *   Simplified string titles and descriptions in `strings.xml` for the HDR playback device preference.
… handling

This commit introduces a coalescing mechanism for playback section refreshes in `AppDataRepository` to reduce redundant operations during rapid state changes. It also optimizes cache invalidation execution and removes an unnecessary derived state refresh in `MediaChangeManager`.

### Key Changes:

*   **`AppDataRepository`**:
    *   **Coalescing & Rate Limiting**: Added `playbackSectionsMutex` and `PLAYBACK_SECTIONS_COALESCE_MS` (5000ms cooldown) to prevent `refreshPlaybackSections()` from running repeatedly within a short timeframe.
    *   **Concurrent Invalidation**: Updated `refreshPlaybackSections()` to launch `invalidateContinueWatchingCache()` and `invalidateNextUpCache()` concurrently within a `coroutineScope`.

*   **`MediaChangeManager`**:
    *   Removed redundant `refreshDerivedState` invocation when handling `USER_DATA_CHANGED` media updates.
…yback

This commit introduces dynamic audio stream resolution and transcode decision-making for music playback. It leverages server playback info requests with a dedicated music device profile to determine direct play or transcoding requirements, dynamically replacing ExoPlayer media items when transcoding is needed.

### Key Changes:

*   **Audio Stream Decision Logic**:
    *   **`PlaybackRepository`**: Added `resolveAudioStream` interface method and `JellyfinPlaybackRepository` implementation to evaluate audio playability via Jellyfin's `MediaInfoApi.getPostedPlaybackInfo`, returning a `StreamDecision` (`DirectPlay` or `Transcode`).
    *   **`AndroidDeviceProfileFactory`**: Created `createMusicProfile()` to construct an audio-specific `DeviceProfile` with supported container/codec capabilities and an AAC/HLS fallback transcoding profile.

*   **Queue & Player Integration**:
    *   **`MusicQueueManager`**: Added stream caching via `resolvedStreams` and introduced `ensureResolved()` to fetch stream decisions before generating playback URIs. Updated stream URI building to respect quality settings and resolved stream decisions.
    *   **`AudioService`**: Added `resolveAroundCurrent()` to proactively resolve audio streams for the active and upcoming tracks, dynamically updating ExoPlayer with `replaceMediaItem` if a transcoded stream is required.

*   **Playback State Updates**:
    *   **`MusicPlaybackManager`**: Updated position updates to derive track duration directly from track `runtimeTicks` when available, ensuring accurate track durations in UI state.
…ervice

This commit improves the accuracy of music playback stats, refactors transcoding detection logic, and ensures thread safety for engine state in `AudioService`. It also fixes coroutine cancellation handling when resolving Jellyfin audio streams.

### Key Changes:

*   **Playback Stats & Transcoding**:
    *   **`MusicQueueManager`**: Added `isServerTranscode` helper to check if a track is being transcoded by the server.
    *   **`MusicPlayerViewModel`**: Updated playback stats calculation to determine play method using `isServerTranscode` instead of URI path checks. Added caching for `lastTranscodingInfo` per track to retain transcoding details during stats polling cycles.
    *   **`MusicProgressReporter`**: Introduced `updatePlayMethod` to update the reporter's play method state as tracks are resolved.

*   **Service & Thread Safety**:
    *   **`AudioService`**: Wrapped `activeEngine` in an `AtomicReference` to allow safe concurrent reads from background threads.
    *   **`DynamicDataSourceFactory`**: Refactored from an inner class to a static class accepting explicit context, reference, and repository dependencies, avoiding implicit outer class references.
    *   **Queue Resolution**: Ensured `musicProgressReporter.updatePlayMethod` is invoked when track streams are resolved during queue preloading.

*   **Repository Exception Handling**:
    *   **`JellyfinPlaybackRepository`**: Updated `resolveAudioStream` to re-throw `CancellationException`, preventing coroutine cancellations from being caught as generic exceptions.
This commit updates `AudiobookshelfPlayer` to allow releasing the player session without stopping the background `AudioService`. This ensures the audio service remains running when switching playback engines.

### Key Changes:

*   **`AudiobookshelfPlayer`**:
    *   Added a `stopService` parameter (defaulting to `true`) to `closeSession()` so stopping `AudioService` can be bypassed.
    *   Introduced `releaseForEngineSwitch()` to close the session with `stopService = false`.

*   **`MusicLibraryScreen`**:
    *   Updated the engine switch logic to call `releaseForEngineSwitch()` instead of `release()` before starting `AudioService` with `ACTION_ENGINE_MUSIC`.
This commit introduces real-time audio format and transcoding status indicators to the playback interface for both music and audiobooks. It refactors the playback managers and service layer to track codec information and playback methods, surfacing these details via a new badge component in the transport controls.

### Key Changes:

*   **Playback State & Service**:
    *   **`AudioService`**: Added an `AnalyticsListener` to capture `onAudioInputFormatChanged`, allowing the service to update the current audio codec dynamically from the ExoPlayer instance.
    *   **Transcoding Logic**: Integrated checks in `AudioService` and `MusicPlaybackManager` to detect and propagate server-side transcoding status.
    *   **State Management**: Updated `MusicPlaybackState` and `AudiobookshelfPlaybackManager` to store `audioCodec` and `isServerTranscode` (or `playMethod`).
    *   **Data Integrity**: Refactored `MusicPlaybackManager.updateTrack` to reset codec information when a new track starts to prevent stale metadata display.

*   **UI Architecture**:
    *   **`AudioFormatBadge`**: Introduced a new UI component in `QualityPresentation.kt` that displays the codec and playback method (e.g., "AAC · Direct" or "MP3 · Transcoding") with conditional styling.
    *   **`TransportControls`**: Updated the base transport component to include a `timeRowCenter` slot, allowing badges to be displayed between the elapsed and total time labels.
    *   **Screen Integration**: Refactored `MusicPlayerScreen` and `AudiobookshelfPlayerScreen` to pass codec and transcoding information down to the controls.

*   **Logic & Formatting**:
    *   **`AudiobookshelfPlayer`**: Refined `PlaybackStats` logic to correctly map ABS play methods (Direct Play, Direct Streaming, Transcoding, Local).
    *   **Localization**: Updated `QualityPresentation.kt` to use the locale from `LocalConfiguration` when formatting bitrate strings for better internationalization support.
    *   **Logging**: Replaced `android.util.Log` with `Timber` in `AudiobookshelfPlaybackManager` for consistent logging.
This commit enhances the nightly workflow to support building and releasing from branches other than `master`. It introduces dynamic tagging, branch-specific APK naming, and conditional compatibility warnings to the release notes.

### Key Changes:

*   **Dynamic Tagging & Naming**:
    *   Replaced the hardcoded `nightly` tag with a dynamic `$TAG` variable. Non-master branches now use a `nightly-<branch-name>` slug format.
    *   Updated release titles to include the branch name for clarity.
    *   Refactored APK filenames to use the dynamic tag, preventing naming collisions between different branch builds.

*   **Improved Commit History Logic**:
    *   Updated the changelog generator to compare against the branch-specific tag.
    *   Added logic to calculate the commit delta from `origin/master` when a branch-specific nightly release is created for the first time.

*   **Conditional Compatibility Notices**:
    *   Introduced a dynamic `notice` output for the release body.
    *   Added a "Caution" block for non-master builds specifically warning about Jellyfin 12 targeting and the removal of Jellyfin 10 server compatibility.

*   **Workflow Refinement**:
    *   Updated the release deletion and publishing steps to use the dynamic tags.
    *   Synchronized the Discord notification trigger to pass the correct branch-specific release tag.
This commit introduces local network discovery using Multicast DNS (mDNS) to simplify the setup process for Jellyfin, Jellyseerr, and Audiobookshelf instances. By leveraging Android's `NsdManager`, the app can now automatically detect and display compatible services available on the user's local network.

### Key Changes:

*   **Service Discovery Core**:
    *   **`LocalServiceDiscovery`**: Introduced a new singleton to manage mDNS lookups using `NsdManager`. It uses `callbackFlow` to stream discovered services and includes `MulticastLock` handling to ensure reliable packet reception.
    *   **Service Types**: Added support for detecting `_jellyfin._tcp`, `_jellyseerr._tcp`, and `_audiobookshelf._tcp` service types.

*   **Repository Enhancements**:
    *   **`JellyfinServerRepository`**: Refactored `discoverServersFlow` to use `channelFlow`, merging results from legacy broadcast discovery and the new mDNS implementation.
    *   Implemented a thread-safe discovery cache using `Mutex` to aggregate and deduplicate servers found via multiple discovery methods.

*   **UI & Component Integration**:
    *   **`DiscoveredServicesSection`**: Created a shared Compose component to display discovered network services. It allows users to quickly select a detected server to auto-populate connection details.
    *   **Login Flows**: Integrated discovery logic into `JellyseerrLoginViewModel` and `AudiobookshelfLoginViewModel`, updating their respective setup bottom sheets to show found instances.

*   **Permissions & Resources**:
    *   Added `CHANGE_WIFI_MULTICAST_STATE` to `AndroidManifest.xml` to facilitate mDNS discovery.
    *   Added localized strings and vector assets for the discovery UI.
This commit introduces a dedicated log viewer screen and refactors the settings navigation logic to better support adaptive layouts. By distinguishing between single-pane and dual-pane states, the app now ensures a consistent navigation experience across different device form factors.

### Key Changes:

*   **Log Viewer Integration**:
    *   Added `LOGS_ROUTE` to `Destination.kt`.
    *   Registered `LogViewerScreen` in `MainNavigation.kt`, enabling users to view application logs directly within the UI.

*   **Adaptive Navigation Logic**:
    *   Refactored `SettingsScreen.kt` to utilize an `isDualPane` check for all primary settings categories (Appearance, Playback, Downloads, Server Management, Licenses, and Logs).
    *   **Dual-Pane (Tablets/Foldables)**: Uses `navigator.navigateTo` to update the detail pane of the `ListDetailPaneScaffold`.
    *   **Single-Pane (Phones)**: Uses standard `navController.navigate` to perform a full-screen transition to the selected setting.

*   **Refactoring**:
    *   Added `createLogsRoute()` helper to the `Destination` object for standardized route generation.
    *   Streamlined `SettingsItem` click listeners to handle conditional navigation flows.
…agement

This commit significantly upgrades the internal logging and crash reporting infrastructure. It introduces a dual-tab interface to distinguish between real-time application logs and persistent crash reports, while adding robust search, tag-based filtering, and time-windowing capabilities.

### Key Changes:

*   **Crash Reporting System**:
    *   **`CrashStore`**: Introduced a repository to manage local crash reports, including parsing stack traces and metadata (build, device, thread) from stored text files.
    *   **`CrashFileExporter`**: Updated to capture richer device and build metadata during uncaught exceptions and apply redaction to stack traces.
    *   **Crash UI**: Added `CrashListContent` and `CrashDetailContent` for browsing, deleting, and sharing detailed crash reports with preceding application logs.

*   **Advanced Log Filtering & Search**:
    *   **Search**: Implemented a real-time search field that filters logs by message, tag, or stack trace, with visual highlighting of matching terms in the UI.
    *   **Tag Filtering**: Added a `TagFilterSheetContent` bottom sheet to filter the log buffer by specific categories, including error/warning indicators per tag.
    *   **Time Windowing**: Introduced temporal filtering (Last 1m, 5m, 15m, or All) to narrow down events during active debugging.

*   **Enhanced UI & Navigation**:
    *   **Expanded Rows**: Log rows are now interactive; clicking a row expands it to show the full message, timestamp, and stack trace.
    *   **Row Actions**: Added contextual actions to expanded logs, including "Copy with context" (includes 20 preceding lines) and "Only this tag".
    *   **Error Navigation**: Introduced an `ErrorJumpPill` to allow developers to quickly navigate between error-level entries in the buffer.
    *   **Timeline Markers**: Added `LaunchRow` to visually indicate application start events in the log timeline.

*   **Data Management & Privacy**:
    *   **`LogClipboard`**: Implemented a secure clipboard helper that marks copied log data as sensitive to prevent leakage in clipboard history.
    *   **`LogExporter`**: Refactored to support sharing both filtered log buffers and individual crash reports, ensuring all output is scrubbed of sensitive secrets.
    *   **State Management**: Refactored `LogViewerViewModel` to handle complex filtering logic and buffer snapshots while maintaining a "following" state for real-time updates.

*   **Resources**:
    *   Added comprehensive string resources for all new filtering and crash management features.
    *   Introduced new vector drawables for copy and navigation actions.
…nagement

This commit introduces a comprehensive sleep timer system for the video player and Audiobookshelf, featuring "End of Item" and "End of Chapter" modes. It also refactors the download and storage settings to provide a categorized, interactive view of offline media usage.

### Key Changes:

*   **Advanced Sleep Timer**:
    *   **Video Player**: Introduced `SleepTimerPanel` with duration presets and an "End of Item" mode. Implemented a volume-fading transition upon expiration, a 60-second grace period before closing the player, and an interactive "Extend" prompt that appearing near the timer's end.
    *   **Audiobookshelf**: Added support for chapter-based sleep timers, allowing playback to stop at the end of the current or future chapters/episodes.
    *   **UI Integration**: Added a dedicated sleep timer toggle in the player controls and unified the countdown formatting across the app.

*   **Storage & Download Management**:
    *   **Categorized Storage Strip**: Refactored the storage visualization to show a color-coded breakdown by media type (Video, Music, Audiobook, Podcast).
    *   **Interactive Legend**: Added a legend to the storage strip that acts as a filter, allowing users to quickly drill down into specific media categories within the download catalog.
    *   **Conditional Visibility**: Updated the settings screen to only show the "Offline media" section when downloaded content is actually present on the device.

*   **Log Viewer Enhancements**:
    *   **State Management**: Introduced a "Pause" functionality distinct from "Following" to allow manual inspection of logs without the buffer jumping during high-traffic events.
    *   **UI Optimization**: Refactored log rows to use `drawBehind` for ribbons and timeline rails, reducing layout depth. Added a "Jump to latest" pill for easier navigation.
    *   **Refined Styles**: Centralized log typography in `LogTextStyles.kt` and improved the grouping logic for repeated log signatures.

*   **General Fixes**:
    *   Fixed vector dimensions for `ic_arrows_output.xml`.
    *   Improved navigation routing between storage settings and the offline media catalog.
…gress

This commit introduces a unified system for handling window insets within the player interface and enhances the episode switcher to provide real-time playback feedback. By centralizing inset logic, overlays now consistently avoid system bars and display cutouts across various device configurations.

### Key Changes:

*   **Unified Inset Management**:
    *   Introduced `playerOverlayInsets`, a custom `Modifier` extension that applies padding for `systemBarsIgnoringVisibility` and `displayCutout`.
    *   Applied the new inset modifier to key player components, including the `PlaybackSpeedDialog`, `SleepTimerPanel`, `EpisodeSwitcher`, and specific overlay buttons in `PlayerScreen`.

*   **Episode Switcher Enhancements**:
    *   **Live Progress Tracking**: Refactored `EpisodeSwitcher` to accept `currentPositionMs` and `currentDurationMs`. It now calculates and displays a real-time progress bar for the currently playing item.
    *   **Refined "Played" Logic**: Updated the "watched" state logic to mark an episode as played if either the backend flag is set or the live playback progress exceeds 90%.
    *   **UI Consistency**: Prioritizes live playback progress over stored metadata ticks to ensure the UI matches the user's current session.

*   **Refactoring & Compatibility**:
    *   **Media3 Updates**: Added `@OptIn(UnstableApi::class)` annotations to several components to support internal Media3 API usage.
    *   **Code Cleanup**: Standardized indentation and simplified `WindowInsets` logic in `PlayerControls.kt` by using `union` for complex inset combinations.
    *   **Resource Formatting**: Improved string resource formatting and alignment within the `SleepTimerPanel`.
This commit introduces support for the `ACCESS_LOCAL_NETWORK` permission required by Android 15 (API 37) and later. It adds a centralized permission management utility, integrates permission checks into server discovery and connection logic, and provides UI components to prompt users when local network access is required but not granted.

### Key Changes:

*   **Permission Management**:
    *   **`LocalNetworkPermission`**: Introduced a singleton utility to check permission status, determine if it's required (SDK 37+), and evaluate if specific URLs are blocked by the restriction.
    *   **Manifest**: Added the `ACCESS_LOCAL_NETWORK` permission declaration.

*   **Core Logic & Repositories**:
    *   **`LocalServiceDiscovery`**: Refactored mDNS discovery to return a `DiscoveryResult`, explicitly surfacing when discovery fails due to missing local network permissions.
    *   **`ServerAddressResolver`**: Updated to return a `PermissionRequired` result when address probing fails and local network access is restricted.
    *   **`SessionManager`**: Added a `needsLocalNetworkPermission` state to track and signal when the app loses access to a local server due to permission changes.
    *   **`MediaDownloadWorker`**: Implemented checks to defer or fail downloads from local servers if the required permission is missing, including a retry mechanism.

*   **UI & User Experience**:
    *   **`LocalNetworkPermissionPrompt.kt`**: Created reusable `LocalNetworkPermissionCard` and `LocalNetworkPermissionGrantButton` components for consistent permission requesting across the app.
    *   **Integrated Prompts**: Added permission prompts to the `LoginScreen`, `AddEditServerScreen`, `ServicesHubScreen`, and bottom sheets for Jellyseerr and Audiobookshelf.
    *   **Global Handling**: Updated `MainNavigation` to show a system-level alert dialog when a configured server becomes unreachable due to the missing permission.

*   **Refactoring**:
    *   Updated `LoginViewModel`, `AddEditServerViewModel`, and `ServicesHubViewModel` to handle the new `LocalNetworkPermissionRequired` states and allow users to refresh and retry connections after granting access.
This commit applies automated formatting across the entire codebase using `ktfmt` to ensure consistent code style and import ordering. It also introduces a continuous integration (CI) workflow and improves the management of R8 mapping files for better crash deobfuscation.

### Key Changes:

*   **Code Formatting**:
    *   Applied project-wide reformatting to adhere to a standardized Kotlin style, including reorganized import blocks, consistent indentation, and the addition of trailing commas.

*   **Continuous Integration**:
    *   **`ci.yml`**: Introduced a new GitHub Actions workflow to automate formatting checks (`ktfmtCheck`) and verify successful compilation (`compileDebugKotlin`) on all pushes and pull requests.

*   **Build & Release Engineering**:
    *   **Archive Mapping Tasks**: Updated `app/build.gradle.kts` with a new task to automatically zip and archive R8 obfuscation mapping files during `release` and `nightly` builds.
    *   **Nightly Workflow**: Updated the nightly release pipeline to package and upload the R8 mapping as a build artifact, allowing for easier deobfuscation of crash reports.
    *   **Proguard Configuration**: Modified `app/proguard-rules.pro` to preserve `SourceFile` and `LineNumberTable` attributes, ensuring that deobfuscated stack traces point to accurate source lines.

*   **UI & Logic Maintenance**:
    *   Cleaned up various Composable functions and ViewModels by streamlining logic blocks and removing redundant whitespace.
…ting

This commit enables Room schema exporting to track database changes and introduces automated tests to verify the integrity of the migration chain. It also standardizes Kotlin import layouts across the project via `.editorconfig` and integrates unit test execution into the CI pipeline.

### Key Changes:

*   **Database Tooling & Schema**:
    *   **Room Configuration**: Added the Room Gradle plugin and configured the schema directory in `app/build.gradle.kts`.
    *   **Schema Export**: Set `exportSchema = true` in `AfinityDatabase` and exported the current schema (version 78).
    *   **Version Management**: Introduced `AFINITY_DB_VERSION` constant to centralize database versioning.

*   **Testing Infrastructure**:
    *   **Migration Tests**: Added `AfinityMigrationTest` using `MigrationTestHelper` to ensure the database can be opened with all registered migrations.
    *   **Chain Validation**: Created `MigrationChainTest` to programmatically verify that migrations are contiguous, sequential, and reach the current database version without gaps or duplicates.
    *   **CI Integration**: Updated `.github/workflows/ci.yml` to include a `testDebugUnitTest` step, ensuring tests run on every pull request and push.

*   **Code Quality & Style**:
    *   **Import Formatting**: Added `.editorconfig` with `ij_kotlin_imports_layout = *` to enforce a consistent import order.
    *   **Project-wide Refactor**: Reorganized imports across nearly all source files to comply with the new formatting rules (moving `java.*` and `javax.*` groups).

*   **Dependencies**:
    *   Added `androidx.room:room-testing` and updated JUnit/Test Runner dependencies in `libs.versions.toml`.
…I consistency

This commit performs a wide-scale refactor to improve the robustness and maintainability of the codebase. Key improvements include standardized coroutine cancellation handling, removal of unused dependencies and dead code, and alignment with modern Kotlin and Jetpack Compose best practices.

### Key Changes:

*   **Structured Concurrency & Error Handling**:
    *   Updated `HomeViewModel`, `SearchViewModel`, `PlayerViewModel`, `ItemDetailViewModel`, and several repositories to explicitly catch and re-throw `CancellationException`. This ensures that coroutine cancellation propagates correctly and isn't swallowed by generic `catch` blocks.
    *   Refactored `SyncPlayViewModel` to replace Flow's `.catch` operator with manual `try-catch` within collectors for finer control over exception handling and logging.

*   **Code Cleanup & Dependency Refactoring**:
    *   **Dependency Injection**: Removed unused `Context`, `CoroutineScope`, and various Repository/Manager injections across multiple ViewModels and Repositories (e.g., `JellyfinMediaRepository`, `SessionManager`, `FavoritesViewModel`).
    *   **Dead Code Removal**: Eliminated unused methods and parameters, including `buildStreamUrl`, `getDatabaseSize`, and several unused database DAO operations.
    *   **API Refinement**: Removed redundant `suspend` modifiers from functions that no longer perform asynchronous work (e.g., `toAfinitySource`, `decodeReferenceMovie`).

*   **Modern Kotlin & Compose Standards**:
    *   **Compose Best Practices**: Reordered parameters in numerous Composables (e.g., `PlayerScreenWrapper`, `SearchScreen`, `LiveTvChannelsTab`) to place the `Modifier` as the first optional parameter.
    *   **Performance**: Replaced generic `mutableStateOf` with `mutableLongStateOf` and `mutableIntStateOf` where appropriate to avoid boxing.
    *   **Kotlin Features**: Switched from `Enum.values()` to the more efficient `Enum.entries`.
    *   **Formatting**: Standardized `String.format` calls by providing `Locale.getDefault()` to prevent lint warnings and ensure consistent formatting.

*   **UI & UX Improvements**:
    *   **Update System**: Enhanced `UpdateManager` and `GlobalUpdateDialog` to pass and display release metadata during the download phase.
    *   **Resources**: Updated `ic_audiobookshelf_light` dimensions to a standard 24dp and renamed toggle state strings (e.g., `state_on` to `state_toggle_on`) for better clarity.
    *   **Components**: Simplified the "Ends At" time logic in `MetadataRow` and added login/retry actions to request error states.

*   **Player & Media Handling**:
    *   **MPVPlayer**: Updated the MPV implementation to better align with Media3's `BasePlayer` requirements, including improved period and volume/mute handling.
    *   **Playback**: Removed `maxStreamingBitrate` constraints from playback repository calls to simplify stream negotiation.
    *   **Downloads**: Simplified `AbsMediaDownloadWorker` by removing redundant SDK version checks for foreground service info.
…uality management

This commit introduces a dedicated "Never transcode" setting for music playback and significantly refactors the video player UI to separate track selection from quality management. It provides users with more granular control over media streaming behavior and improves the visibility of playback technical details.

### Key Changes:

*   **Music Playback & Settings**:
    *   **Music Never Transcode**: Added a new preference to force original quality for music, bypassing bitrate limits.
    *   **`MusicQueueManager`**: Updated to respect the "Never transcode" flag, forcing original bitrates and using static stream URIs when enabled. It now clears and re-resolves the queue when this setting changes.
    *   **Settings UI**: Introduced a dedicated toggle for music transcoding in `PlayerOptionsScreen` and added logic to disable manual quality selection when the setting is active.
    *   **Music Player**: Updated the music quality bottom sheet with usage hints and disabled quality switching if transcoding is prohibited.

*   **Video Player UI & UX**:
    *   **Quality Panel**: Extracted quality selection into its own `QualityPanel`, featuring a `LazyColumn` for smoother navigation of resolution options.
    *   **Interactive Playback Badges**: Made the resolution and play method badges clickable, acting as a shortcut to the new Quality Panel.
    *   **Track Selection**: Simplified the `TrackPanel` to focus exclusively on audio and subtitle streams.
    *   **Enhanced Metadata**: Added real-time playback information to the quality selector, showing the current output resolution vs. the source resolution and bitrate.
    *   **Transcode Feedback**: Introduced `TranscodeReasonLine` to provide clear, categorized reasons (e.g., codec support) when media is being transcoded.

*   **Data & State Management**:
    *   **`PreferencesRepository`**: Added DataStore support for the `music_never_transcode` key.
    *   **`PlayerViewModel`**: Implemented `isQualityLocked` state to prevent quality adjustments when transcoding is globally disabled or restricted by the session.
    *   **UI State**: Refactored `PlayerUiState` to include specific output dimensions and transcode reasons for more accurate UI reporting.

*   **Resources**:
    *   Added string resources for detailed quality reporting ("Playing original quality", "Playing 1080p, transcoded from 4K").
    *   Updated existing preference descriptions for better clarity and brevity.
@MakD
MakD merged commit 51c3f3e into master Sep 15, 2026
2 checks passed
@MakD
MakD deleted the v12-support branch September 15, 2026 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant