# Requirements Specification This document captures JellyTau's user requirements, software requirements, traceability matrix, test traceability, and known technical debt. For a narrative overview of the system design, see [docs/architecture/](architecture/). For development workflows, see the [README](../README.md) and [scripts/README.md](../scripts/README.md). ## 1. User Requirements | ID | Requirement | Priority | Status | |----|-------------|----------|--------| | UR-001 | Run the app on multiple platforms (Linux, Android) | High | In Progress | | UR-002 | Access media when online or offline | High | Done | | UR-003 | Play videos | High | Done | | UR-004 | Play audio uninterrupted | High | Done | | UR-005 | Control media playback (pause, play, skip, scrub) | High | Done | | UR-006 | Control media when device is on lock screen or via BLE headsets | Medium | Done | | UR-007 | Navigate media in library | High | Done | | UR-008 | Search media across libraries | High | Done | | UR-009 | Connect to Jellyfin to access media | High | Done | | UR-010 | Control playback of Jellyfin remote sessions | Low | Done | | UR-011 | Download media on demand | Medium | Done | | UR-012 | Login info shall be stored securely and persistently | High | Done | | UR-013 | View and manage downloaded media | Medium | Done | | UR-014 | Make and edit playlists of music that sync back to Jellyfin | Medium | Done | | UR-015 | View and manage current audio queue (add, reorder tracks) | Medium | Done | | UR-016 | Change system settings while playing (brightness, volume) | Low | Planned | | UR-017 | Like or unlike audio, albums, movies, etc. | Medium | Done | | UR-018 | Choose to download series, albums, songs, artist discography | Medium | Done | | UR-019 | Resume playback from where you left off (movies, shows, albums) | High | Done | | UR-020 | Select subtitles for video content | High | Done | | UR-021 | Select audio track for video content | High | Done | | UR-022 | Control streaming quality and transcoding settings | Medium | Planned | | UR-023 | View "Next Up" / Continue Watching on home screen; auto-play next episode with countdown popup and configurable episode limit | Medium | Done | | UR-024 | View recently added content on server | Medium | Done | | UR-025 | Sync watch history and progress back to Jellyfin | High | Done | | UR-026 | Sleep timer for audio and video playback (roller UI, time/track/episode modes) | Low | Done | | UR-027 | Audio equalizer for sound customization | Low | Done (Linux only) | | UR-028 | Navigate to artist/album by tapping names in now playing view | High | Done | | UR-029 | Toggle between grid and list view in library | Medium | Done | | UR-030 | Quick genre browsing and filtering | Medium | Done | | UR-031 | Crossfade between audio tracks | Low | Not implemented (blocked — see DR-034) | | UR-032 | Gapless playback for seamless album listening | Medium | Done (Linux only) | | UR-033 | Volume normalization to prevent volume jumps between tracks | Low | Done (Linux only) | | UR-034 | Rich home screen with hero banners, carousels, and personalized sections | High | Done | | UR-035 | View cast/crew (actors, directors) on movie/show detail pages | High | Done | | UR-036 | Navigate to actor/person page showing their filmography | Medium | Done | | UR-037 | Visually appealing video library with poster grids and metadata | High | Done | | UR-038 | Movie/show detail page with backdrop, ratings, and rich metadata | High | Done | | UR-039 | Navigate between main sections via bottom navigation bar | High | Done | | UR-040 | Keep a video's audio playing when the app is backgrounded or the screen is locked, stopping video decode until the app returns to the foreground (per-player toggle; Android) | Medium | Done (pending device verification) | | UR-041 | Continue watching *locally-playing video* in a floating picture-in-picture window when leaving the app (Android) — PiP applies to video only, never to audio playback, library/menu browsing, or remote/cast sessions | Medium | Done | | UR-042 | Authenticate to a server and manage the session lifecycle (connect, log in, Quick Connect, background session verification, re-authenticate, log out) | High | Done | | UR-043 | Automatically detect server reachability and switch between online and offline operation without user intervention | High | Done | | UR-044 | Pin downloaded media so it is protected from automatic cache eviction | Low | Done | | UR-045 | Predictively pre-cache likely-next media (queue lookahead and album affinity) within a storage budget | Low | Done | | UR-046 | Group multiple remote players into a synchronized playback group (LMS SyncGroups) | Low | Done | | UR-047 | Manage multiple Jellyfin servers (add, list, remove) and switch the active server/account | Medium | Planned (backend store done; switcher UI pending) | | UR-048 | See the next episodes of a series directly below the episode/series being viewed, above cast and similar-shows content, so continuing a show is the shortest path (see [ux-flows.md §5B](ux-flows.md)) | High | Done | | UR-049 | Search is scoped by where it was started — inside a library it searches that library, from Home/library-root/search-tab it searches everything — with the scope shown as filter chips under the search bar that preselect from context and can be changed without retyping (see [ux-flows.md §6.1](ux-flows.md)) | High | Implemented | | UR-050 | Reorder search result groups (Songs, Albums, Artists, Movies, TV Shows) by drag and drop in settings, so the media a user cares about most appears first (see [ux-flows.md §6.3](ux-flows.md)) | Medium | Implemented | | UR-051 | Browse library pages in a consistent layout where card shape signals media type (square music, poster video, thumbnail episode), ordinal content stays listed, and the grid/list preference persists across pages (see [ux-flows.md §5A](ux-flows.md)) | Medium | Partial (implemented; toggle not reachable from settings) | | UR-052 | While offline, library pages show only media available on the device by default, with an opt-in toggle that additionally reveals the cached server catalog as greyed-out entries which can be queued for download on the next reconnect | High | Done | | UR-053 | Restrict media downloads to unmetered networks via a "WiFi Only" setting: when enabled, queued downloads are held while the device is on cellular or a metered connection (including metered WiFi hotspots) and resume automatically once an unmetered network is available | Medium | Done (pending device verification) | | UR-054 | Reach account actions (Settings, Downloads, Display preferences, Sign out) from every authenticated screen via a single account menu anchored to the user's name, identical on desktop and mobile (see [ux-flows.md §1.2](ux-flows.md)) | High | Done | | UR-055 | Browse downloaded media as an offline-scoped library — reusing the same library grids, cards, and detail pages as online browsing, showing only libraries/containers with downloaded content — with the transfer-progress list demoted to a secondary "Transfers" view (see [ux-flows.md §7.2](ux-flows.md)) | High | Done | | UR-056 | See how much disk each downloaded item/album/series consumes, in familiar rounded units shown on the card and detail page, with a device total on the Downloaded surface and a reclaim amount stated at the point of removal (see [ux-flows.md §7.3.1](ux-flows.md)) | Medium | Done | | UR-057 | Settings apply the instant a control is changed — no "Save" button and no save/dirty state — so leaving the page never loses a change; sliders show a live readout while dragging but persist on release (see [ux-flows.md §8.1](ux-flows.md)) | Medium | Done | | UR-058 | On the home screen, a tap on a media card opens the item (movie/episode detail page, or the series Episode Focus View for episodes) rather than starting playback; a long-press starts "play now" after a confirm; an episode detail/focus page links back to its parent series and season (see [ux-flows.md §5B.5](ux-flows.md) and [§5B.1](ux-flows.md)) | Medium | Done | | UR-059 | Skipping to the next episode records the episode left behind as **fully watched** rather than saving a mid-episode resume point — skipping means "done with this one", not "stopped here" — and Continue Watching hides episodes the viewer has already moved past (a partial position behind that series' next-up episode), so the row only ever offers genuinely unfinished media | Medium | Done | | UR-060 | Search results are ordered by how well they match: a name that *starts* with the query outranks one matching mid-word (typing "parks" finds "Parks and Recreation" before "Sparks of Love"), and at equal match quality a container outranks its contents (a series before its episodes). Results are grouped into distinct categories — TV Shows, Episodes, Movies, Songs, Albums, Artists and People — so a show never competes with its own episodes for the same slot, and searching an actor's name reaches their bio | High | Done | | UR-061 | Double tapping the video skips within it — right half jumps **forward 30 seconds**, left half jumps **back 10 seconds** — with an on-screen indicator naming the amount. A double tap leaves the play state unchanged — playing jumps and keeps playing, paused jumps and stays paused — because the second tap re-toggles what the first tap toggled (see DR-098); the skip lands relative to the position the player actually reports, and repeated double taps accumulate rather than all skipping from the same spot | Medium | Done | | UR-062 | Opening a TV series lands the viewer **where they are in it**, not at season 1: the series page scrolls the current season into view and highlights the current episode, and the hero button opens that episode (labelled `Resume S2E4` / `Play S1E1`). "Current" means the episode in progress, else the server's Next Up for that series, else the first unwatched episode, else the first — resolved by the backend so it also works offline. A season is **never a page of its own**: every route that names a season lands on the series with that season in view, so the episodes of all seasons are always one continuous scrollable list | High | Done | | UR-063 | Each video library is **one page**, not three. Browsing (hero, Continue Watching, Next Up, Recently Added, genre rows), the full title grid, and the genre browser are tabs of `/library/tv` and `/library/movies` rather than separate routes with inconsistent names (`/library/tv/shows` vs `/library/movies/all`, `/library/shows/genres` vs `/library/movies/genres`). The old routes redirect so existing links keep working | Medium | Done | | UR-064 | Watch history can be **erased**, per series and per season, from the series page. Clearing marks every episode inside unwatched and clears resume positions, so the show returns to "never watched" and reopens on its premiere. It asks for confirmation first (it cannot be undone) and requires a connection to the server, since history cleared only locally would be undone by the next sync | Medium | Done | | UR-066 | The app's own chrome stays clear of the device's system chrome. On Android the bottom navigation sits above the navigation/gesture bar instead of underneath it, the header clears the status bar, and full-screen video and audio playback keep their controls inside the usable screen — clear of the gesture bar and, in landscape, of the display notch. This must hold across navigation modes (gesture and 3-button) and rotation, not only on the handsets it happened to be tested on | High | Done | --- ## 2. Software Requirements ### 2.1 Integration Requirements External system integrations and platform-specific implementations. | ID | Requirement | Category | Traces To | Status | |----|-------------|----------|-----------|--------| | IR-001 | Build system supporting multiple targets (Linux, Android) | Build | UR-001 | Done | | IR-002 | Build scripts for Android and Linux | Build | UR-001 | Done | | IR-003 | Integration of libmpv for Linux playback | Playback | UR-003, UR-004 | Done | | IR-004 | Integration of ExoPlayer for Android playback | Playback | UR-003, UR-004 | In Progress (basic playback works, audio settings missing) | | IR-005 | MPRIS D-Bus integration for Linux lockscreen/media controls | Platform | UR-006 | Planned | | IR-006 | Android MediaSession integration for lockscreen controls | Platform | UR-006 | Done | | IR-007 | Bluetooth AVRCP integration via system media session | Platform | UR-006 | Planned | | IR-008 | Android audio focus handling (pause on call) | Platform | UR-004, UR-006 | Done | | IR-009 | Jellyfin API client for authentication | API | UR-009, UR-012 | Done | | IR-010 | Jellyfin API client for library browsing | API | UR-007, UR-008 | Done | | IR-011 | Jellyfin API client for playback streaming | API | UR-003, UR-004 | Done | | IR-012 | Jellyfin Sessions API for remote playback control | API | UR-010 | Done | | IR-021 | Android MediaRouter integration for remote volume in system panel | Platform | UR-010, UR-016 | Planned | | IR-013 | SQLite integration for local database | Storage | UR-002, UR-011 | Done | | IR-014 | Secure credential storage (keyring/keychain) | Security | UR-012 | Done | | IR-015 | Jellyfin API client for playback progress reporting | API | UR-019, UR-025 | Done | | IR-016 | Jellyfin API client for subtitle/audio track info | API | UR-020, UR-021 | Done | | IR-017 | Jellyfin API client for transcoding parameters | API | UR-022 | Planned | | IR-018 | libmpv subtitle rendering and selection | Playback | UR-020 | Planned | | IR-019 | libmpv audio track selection | Playback | UR-021 | Planned | | IR-020 | libmpv/ExoPlayer equalizer integration | Playback | UR-027 | Done (Linux/MPV; Android parity pending) | | IR-022 | Jellyfin API client for person/cast data | API | UR-035, UR-036 | Done | | IR-023 | Database schema for person/cast caching | Storage | UR-035, UR-036 | Done | | IR-024 | Jellyfin API client for home screen data (featured, continue watching) | API | UR-034 | Done | | IR-025 | Android background-audio handoff: WebView `