brainstate

raulshma/jellyplay · source

Snapshot 0 · 2026-11-04T12:47:28Z · Kotlin · GPL-3.0

Content fingerprint1428b38116f50d7e609e2e5b0f788f605a5de15841802908704780619f5a1f07

GitHub description at capture

Jellyfin client for Android, Android TV & Fire TV. Native Kotlin/Jetpack Compose app with ExoPlayer, mpv & VLC playback, synced-lyrics music, offline downloads, SyncPlay, IPTV, and Jellyseerr/Overseerr requests. Open-source self-hosted media player.

README at capture

<div align="center">

# JellyPlay

</div>

<p align="center">
	<img src="media-resources/app_logo.svg" width="150" alt="JellyPlay app logo" />
</p>

<p align="center">
	<img alt="API 28+" src="https://img.shields.io/badge/Api%2028%2B-50f270?logo=android&logoColor=black&style=for-the-badge" />
	<img alt="Kotlin" src="https://img.shields.io/badge/Kotlin-7F52FF?logo=kotlin&logoColor=white&style=for-the-badge" />
	<img alt="Jetpack Compose" src="https://img.shields.io/badge/Jetpack%20Compose-4285F4?logo=jetpackcompose&logoColor=white&style=for-the-badge" />
	<img alt="Material 3" src="https://custom-icon-badges.demolab.com/badge/material%20you-lightblue?style=for-the-badge&logoColor=333&logo=material-you" />
	<img alt="Media3" src="https://img.shields.io/badge/Media3-FF6F00?style=for-the-badge&logo=android&logoColor=white" />
	<img alt="License GPL-3.0" src="https://img.shields.io/github/license/raulshma/jellyplay?style=for-the-badge&color=blue" />
	<img alt="Status - Beta" src="https://img.shields.io/badge/Status-Beta-orange?style=for-the-badge" />
</p>

<p align="center">
	<sub>Native Jellyfin client for Android, Android TV, Fire TV & Windows desktop &nbsp;&middot;&nbsp; Material 3 Expressive &nbsp;&middot;&nbsp; ExoPlayer, libmpv & LibVLC &nbsp;&middot;&nbsp; Self-hosted, no tracking</sub>
</p>

<div align="center">

### Modern, open-source Jellyfin client for Android, Android TV, Fire TV & Windows desktop

**JellyPlay** is a native, high-performance **Jellyfin client** — built from scratch in **Kotlin** and **Jetpack Compose** with **Material 3 Expressive**. No web wrapper, no Cordova, no embedded browser.

One app for every screen: **phones, tablets, foldables, Android TV, and Amazon Fire TV**. Stream movies and shows, play music with synced lyrics, read comics and ebooks, request content via **Jellyseerr/Overseerr**, manage your **Radarr/Sonarr** queues, download for offline, and run your server from a built-in **admin dashboard** — with three switchable video engines (**ExoPlayer, libmpv, LibVLC**), in-app **self-update** via GitHub Releases, no accounts, and no tracking.

**Also growing beyond Android:** the codebase is now Kotlin Multiplatform — an early **Windows desktop** build (Compose Multiplatform + libmpv) runs browsing, search, details, home, settings, music with real audio playback (full effect stack: equalizer, bass boost, night mode, ReplayGain, …), in-player video with working keyboard media keys, the video effect stack (brightness/contrast/saturation/hue/sharpen/blur/rotate), **render profiles with Anime4K upscaling**, frame capture to `~/Pictures/JellyPlay`, live subtitle cues, the full metadata editor, and the in-app book reader (CBZ/CBR/PDF/EPUB — read-aloud excepted); sign-in uses the shared auth flow (Quick Connect, remembered users, server-address alternates). The KMP migration itself is complete (docs/kmp-migration-plan.md). Player polish, and macOS/Linux (build-from-source, untested), are still landing — treat the desktop build as a preview, not a release; the auto-update client and release checklist have landed, signing and per-OS passes remain.

If you self-host Jellyfin and want a truly native, beautiful, capable client — or a **Plex/Kodi alternative** — give JellyPlay a try.

[Highlights](#highlights) • [Why JellyPlay?](#why-jellyplay) • [Features](#features) • [Tech Stack](#tech-stack) • [Requirements](#requirements) • [Building](#building) • [Local Development](#local-development) • [Permissions](#permissions) • [Project Structure](#project-structure) • [Docs](./docs) • [Website](https://raulshma.github.io/jellyplay/)

</div>

## Highlights

|                                  |                                                                                                                                                                                                                                                                                                                                                                                   |
| :------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 🎬 **Multi-engine video player** | Switch between **ExoPlayer (Media3)**, **libmpv**, and **LibVLC** per device — HDR, refresh rate/resolution matching, **render profiles** (Anime4K shader packs, tone mapping, quality), trickplay seeking, A/B repeat with seek-bar region visualization, multi-range buffered seek bar, gestures, Chromecast, floating Picture-in-Picture, and live transcode-reason surfacing. |
| 💬 **Full subtitle system**      | **ASS/SSA** & **VTT** parsing, external subtitle loading & download, full styling, delay offset, **live sync preview** with cue stack & ±30 s offset slider, **multi-provider search** (Jellyfin, Wyzie, OpenSubtitles), **per-series role memory**, and consistent track labels & badges across engines.                                                                         |
| 📱 **Native on every screen**    | Phone, tablet, foldable, **Android TV**, and **Fire TV** with D-pad navigation, a Leanback launcher, and adaptive Material 3 layouts — plus an early **Windows desktop** build from the same Kotlin Multiplatform codebase.                                                                                                                                                       |
| 🎵 **Rich audio player**         | Synced lyrics via LRCLIB, 10-band equalizer, Night Mode & Dialogue Boost, ambient visualizer, mood playlists, and gapless playback.                                                                                                                                                                                                                                               |
| 📚 **In-app book reader**        | Read comics (CBZ/CBR), PDFs, and EPUBs with bookmarks, highlights & notes, in-book search, EPUB typography, read-aloud with sleep timer, and server-synced reading position.                                                                                                                                                                                                      |
| ⬇️ **Offline downloads & sync**  | WorkManager-backed downloads with HTTP Range resumption, multi-connection acceleration, bundled subtitles & trickplay tiles, dedicated offline library, and durable watch-progress sync outbox.                                                                                                                                                                                   |
| 📡 **Seerr + Arr integration**   | Discover and request via **Jellyseerr/Overseerr**, manage **Radarr/Sonarr** queues, and track an upcoming-releases calendar — all in-app.                                                                                                                                                                                                                                         |
| 👯 **SyncPlay & Play On**        | Real-time watch parties with speed/skip-to-sync correction and in-player chat, plus cast-to and control other Jellyfin sessions.                                                                                                                                                                                                                                                  |
| 🛠️ **Server admin dashboard**    | System health, active sessions, scheduled tasks, server logs, user stats, and stale-media cleanup — without leaving the app.                                                                                                                                                                                                                                                      |
| 📺 **Live TV & DVR**             | Browse live channels, an Electronic Program Guide, **delivery-method selection** (Auto / Direct Stream / Transcode), and manage DVR recordings.                                                                                                                                                                                                                                   |
| 🎨 **Fully customizable**        | 8 themes (Standard, Synthwave, Soothing, Monochrome, Vivid, Aurora, Sakura, Vector Pop), OLED mode, **390+ settings**, 4 home-screen widgets, and a 10-step onboarding wizard.                                                                                                                                                                                                    |

<table align="center" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0;">
	<tr>
		<td align="center" style="padding: 1px;"><img src="media-resources/screenshots/home.webp" width="180" alt="Home screen" style="display: block;" /></td>
		<td align="center" style="padding: 1px;"><img src="media-resources/screenshots/library.webp" width="180" alt="Library screen" style="display: block;" /></td>
		<td align="center" style="padding: 1px;"><img src="media-resources/screenshots/search.webp" width="180" alt="Search screen" style="display: block;" /></td>
		<td align="center" style="padding: 1px;"><img src="media-resources/screenshots/settings.webp" width="180" alt="Settings screen" style="display: block;" /></td>
	</tr>
	<tr>
		<td align="center" style="padding: 1px;"><img src="media-resources/screenshots/audio_home.webp" width="180" alt="Audio home screen" style="display: block;" /></td>
		<td align="center" style="padding: 1px;"><img src="media-resources/screenshots/audio_player.webp" width="180" alt="Audio player screen" style="display: block;" /></td>
		<td align="center" style="padding: 1px;"><img src="media-resources/screenshots/media_detail.webp" width="180" alt="Media detail screen" style="display: block;" /></td>
		<td align="center" style="padding: 1px;"><img src="media-resources/screenshots/media_detail_2.webp" width="180" alt="Secondary media detail screen" style="display: block;" /></td>
	</tr>
	<tr>
		<td colspan="4" align="center" style="padding: 1px;"><img src="media-resources/screenshots/video_player.webp" width="760" alt="Landscape video player screen" style="display: block;" /></td>
	</tr>
</table>

---

> [!NOTE]
> **JellyPlay is currently in Active Beta.**
> The application is under active development. While the core features (such as playback, multi-server support, and offline downloads) are functional and stable, you may encounter occasional visual bugs or edge-case issues. We highly appreciate any bug reports, feedback, and contributions!

## Why JellyPlay?

JellyPlay is built for people who self-host a [Jellyfin](https://jellyfin.org/)
media server and want a **truly native**, **beautiful**, and **capable** client
on every screen — phone, tablet, foldable, Android TV, and Amazon Fire TV.

- **Native, not a web wrapper.** Written from scratch in **Kotlin** and
  [Jetpack Compose](https://developer.android.com/jetpack/compose), with
  [Compose for TV](https://developer.android.com/tv/compose) on the big
  screen. No Cordova, no embedded browser, no compromises.
- **Three video engines.** Pick **[ExoPlayer / Media3](https://developer.android.com/media/media3)**,
  **[libmpv](https://github.com/mpv-player/mpv)**, or **[LibVLC](https://www.videolan.org/vlc/)**
  per device — each has its own strengths for codec support, post-processing,
  and ASS/SSA subtitle rendering.
- **One app, every form factor.** Adaptive layouts across phones, tablets,
  foldables, and Android TV with a dedicated Leanback launcher. Edge-to-edge
  immersive mode, predictive back gesture, and Material 3 dynamic theming
  from your artwork.
- **Self-hosted first.** No accounts, no telemetry, no cloud dependency.
  Multi-server, multi-user support with Quick Connect and token-based auth.
  Automatic **server address failover** across primary/alternate URLs keeps you
  connected whether you're on LAN or remote.
- **Extensive offline & watch-progress sync.** Take your library anywhere with
  WorkManager-backed background downloads, multi-connection byte-range
  acceleration, HTTP Range resumption, and bundled subtitles, trickplay preview
  tiles, and intro/outro skip markers. A durable **playback outbox** records
  progress and watched states offline, automatically syncing back to your server
  with latest-wins reconciliation on reconnect. **Selective freshness resync**
  refreshes changed metadata or artwork without re-downloading media files.
- **Beyond streaming.** **SyncPlay** watch parties, **Jellyseerr / Overseerr**
  requests with **Radarr/Sonarr queue management**, an **upcoming releases
  calendar**, a full **metadata editor**, an in-app **server admin dashboard**,
  **Play On** casting to other Jellyfin sessions, synchronized lyrics via LRCLIB,
  10-band equalizer with night mode, and a weekly **newsletter digest** of your
  library activity.

<details>
<summary><strong>Comparison with Official Jellyfin Clients</strong></summary>

| Capability                      | Official Jellyfin Clients (`jellyfin-android` / `jellyfin-androidtv`) | JellyPlay                                                                                     |
| :------------------------------ | :-------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- |
| **Native UI Architecture**      | ❌ Cordova WebView (Mobile) / Leanback (TV)                           | ✅ **100% Jetpack Compose** (Unified across all devices)                                      |
| **Playback Engines**            | ❌ ExoPlayer only                                                     | ✅ **3 switchable engines** (Media3 / ExoPlayer, libmpv, LibVLC)                              |
| **Complex Subtitles (ASS/SSA)** | ❌ Server transcoding required                                        | ✅ **Client-side styled rendering** via libmpv (Direct Play)                                  |
| **Adaptive Form Factors**       | ❌ Split across 2 separate apps                                       | ✅ **Single unified adaptive app** (Phone, Tablet, TV, Foldable)                              |
| **Material You & Theming**      | ❌ Web / static themes only                                           | ✅ **Material 3 Expressive** with dynamic artwork palette (8 themes + OLED)                   |
| **Offline Downloads**           | ⚠️ Basic browser save (Mobile only, no TV, no background queue)       | ✅ **WorkManager Engine** (HTTP Range, multi-connection, bundled subtitles & trickplay)       |
| **Watch-Progress Sync**         | ❌ No offline progress tracking or sync                               | ✅ **Durable Outbox** (Captures progress offline; auto-reconciles on reconnect)               |
| **Freshness Resync**            | ❌ Full re-download required                                          | ✅ **Selective SHA-256 hash diff** (Refreshes metadata/artwork/subs with zero media download) |
| **SyncPlay (Watch Parties)**    | ⚠️ Web-based SyncPlay only (limited on TV)                            | ✅ **Native multi-screen sync** (Real-time speed/skip sync, in-player chat, Mobile + TV)      |
| **Rich Music Player & DSP**     | ❌ Basic web audio playback                                           | ✅ **Full audio DSP suite** (10-band EQ, LRCLIB lyrics, ReplayGain, visualizer)               |
| **Seerr & \*Arr Integration**   | ❌ Not supported                                                      | ✅ **Jellyseerr / Overseerr** with Sonarr/Radarr queues & release calendar                    |
| **Server Administration**       | ⚠️ Web UI (Mobile only, not on TV)                                    | ✅ **Native dashboard** (Mobile, Tablet, and TV with D-pad navigation)                        |
| **Metadata Editor**             | ⚠️ Web UI (Mobile only, not on TV)                                    | ✅ **Native editor** for titles, posters, and tags (Mobile, Tablet, TV)                       |
| **Connection Failover**         | ❌ Single server URL (manual switching)                               | ✅ **Automatic LAN/WAN address failover** without session loss                                |

</details>

> If you're looking for a Kodi alternative, a Plex alternative, or a
> self-hosted media player for Android & Android TV — give JellyPlay a try.

## Features

Click any section to expand. The full feature list is preserved — collapsed only to keep this page scannable.

<details>
<summary><strong>Video player & subtitles</strong></summary>

**Video player**

- **Three built-in engines**: ExoPlayer (Media3), libmpv, and LibVLC, on an engine-agnostic core
- **Force direct play** profile with automatic transcode fallback
- **Transcode-reason surfacing** — the Stats-for-Nerds overlay, playback error dialogs, and the Live TV banner explain _why_ the server is transcoding, with localized reasons and remedy hints
- **Server-side audio/subtitle switching** on transcode (no restart needed)
- **FFmpeg software decoder** fallback for unsupported codecs
- Video filter controls: Adjust brightness, contrast, saturation, and sharpness in-player (libmpv & LibVLC)
- Enhanced Video Stats Overlay presenting real-time stream bitrate, frame rate, and dropped frames
- External player launching (MX Player, VLC, etc.)
- Direct play, direct stream, and transcoding support with quality/transcoding picker
- Resume playback and progress reporting
- Audio and subtitle track selection with language matching and reset functionality — selections preserved across transcodes and engine reloads
- Per-item playback preferences
- Playback speed control (0.25x–4x) with current speed display and estimated end time
- **Hold speed** — press and hold to temporarily increase playback speed
- Gesture controls for seek, brightness, and volume
- Chapter and episode navigation
- HDR badge indicator
- Picture-in-Picture via a dedicated player activity that floats over the browse UI — transport remote actions, auto-enter on home, aspect-ratio handling, and auto-exit
- Mini player overlay
- Trickplay thumbnail seeking (Jellyfin trickplay sprite sheets) with offline caching support
- **Refresh rate & resolution matching** — 3 modes (Off / Frame Rate Only / Frame Rate + Resolution) with judder-free cadence matching (24→60/120, etc.) and ±0.5 Hz tolerance
- Cross-episode audio/subtitle **track memory** with role-aware scoring and remembered container codec (re-matches when track labels churn), plus **track-selection presets & language rules** — ordered rules with language preferences and title patterns under _Settings → Language → Track Selection_
- **Rendering sheet (render profiles)** — pick a shader pack (**Anime4K** modes A/B/C on desktop, user shaders supported), HDR→SDR tone mapping, and render quality in-player; apply for this session only or save as a per-item / per-series override
- **Multi-range buffered seek bar** — the seek bar visualizes every buffered range (not just the contiguous band), and a stalled-at-end detector completes the item cleanly when the stream freezes near the finish
- **A/B repeat** — loop any segment of the video, visualized as a highlighted seek-bar region with repeat badges
- **Video byte cache** — direct-play/direct-stream segments are cached to disk so backward seeks don't re-fetch
- **Screenshot capture** — save the current frame via PixelCopy
- **Subtitle AV-sync measurement** — press-and-hold to measure and correct audio/video/subtitle sync offset
- **Subtitle sync preview** — live bidirectional preview with a cue stack of the played range and an offset slider (±30 s); malformed-track detection gates cue accumulation until a valid track is active
- Per-item subtitle delay persistence
- Free-form `mpv.conf` option parsing for power users
- Adaptive bitrate streaming
- Intro skip and next episode auto-play with segment auto-skip (intro/outro/recap)
- **Play On** — cast to and control other Jellyfin sessions, with a full-screen companion transport screen
- Chromecast support via Google Cast SDK
- Media session integration for lock screen and notifications
- Sleep timer with configurable duration
- Aspect ratio selection
- Hardware/software decoder selection
- Audio delay adjustment (ms)
- Screen orientation toggle
- Player lock screen overlay to prevent accidental touches
- **Companion Dashboard** — view lyrics and transport controls alongside video playback
- Tap-to-translate subtitle feature

**Subtitle system**

- External subtitle loading and download
- ASS/SSA and VTT subtitle format parsing with enhanced cue handling
- Subtitle styling (font size, color, background, edge type, position) with persistence
- Subtitle delay offset control
- Preferred subtitle language selection
- **Per-series subtitle role preferences** — remember language + forced/SDH role across episodes, with tiered fallback matching (exact → relax SDH → language only), plus an explicit per-series subtitles-off choice
- **Consistent track labels everywhere** — unified label formatting & role badges (Forced / Default / SDH) across all engines and the Jellyfin server path, with marker detection from titles
- **Per-subtitle download status & retry** — independent download state per remote subtitle (downloading / ready / delayed / failed) with inline retry, "Use" action, and live track-list refresh
- **Multi-provider subtitle search** — search across **Jellyfin**, **Wyzie**, and **OpenSubtitles** (OpenSubtitles uses secure username/password account login; configure providers under _Integrations → Subtitle Providers_), with provenance badges and per-provider download dispatch
- **Subtitle sync preview** — bidirectional live preview with a cue stack of the played range and an offset slider (±30 s) so you can dial in delay before committing; malformed-track detection gates cue accumulation until a valid track is active
- **Provider subtitle upload** — persist externally-fetched subtitles back to the Jellyfin server via upload, so they're available across devices
- **Provider subtitles work offline** — a streaming subtitle store persists downloaded provider subtitles and re-attaches them during offline playback
- **Zoom-safe captions** — screen-pinned subtitles that stay put during pinch/crop zoom (ExoPlayer reparents its native subtitle views; mpv emits a live cue via `sub-text` rendered as a Compose overlay)
- Community rating indicator for search results and subtitle download sheet

</details>

<details>
<summary><strong>Audio player & music discovery</strong></summary>

**Audio player**

- Music browsing for artists, albums, tracks, genres, and playlists
- Queue management with drag-to-reorder
- Shuffle and repeat modes
- Playback speed control
- Waveform-style seek bar
- Real-time FFT audio visualizer
- Synced and unsynced lyrics via LRCLIB API
- 10-band equalizer with presets
- Night Mode (loudness enhancement with configurable strength)
- Dialogue Boost (vocal frequency equalization with configurable strength)
- Audio normalization (ReplayGain), channel mix modes, dynamics compression, and dialogue de-noise
- Virtualizer (3D audio) and Reverb effect presets
- Gapless playback and crossfade between tracks
- Ambient Mode with animated color blobs derived from album art
- Sleep timer with configurable duration
- **Playback focus** — music and book read-aloud never talk over each other; the interrupted side stays paused for manual resume
- **Volume memory** — remember one volume level each for video, music, and audiobooks (master toggle; desktop mpv and the audio players)

**Music discovery**

- Smart playlists with criteria-based filtering (genre, artist, year, rating, play count, tags)
- Mood playlists with 10 presets (Happy Vibes, Chill Out, Energetic, Deep Focus, Workout, Melancholy, Romantic, Party Time, Sleep, Late Night Drive)
- Recently played, frequent artists, and recommended albums

</details>

<details>
<summary><strong>Library, browsing & discovery</strong></summary>

- Home sections: Continue Watching, Next Up, Recently Added, Latest, Favorites, and Surprise Me shuffle
- **Persistent home-section cache** with stale-while-revalidate — instant home screen, background refresh, and full invalidation on pull-to-refresh
- **Hide top header on scroll** toggle for a more immersive browse
- Episode cards show a series badge in the footer with resolved series posters
- Library browsing with pagination and folder filtering
- **Library filter chips & sheet** — filter by genre, year, tags, video/audio format, and more via quick chips and a full filter sheet, with a custom year-range filter and reset confirmation flow
- **Compound sort** — sort by multiple criteria (e.g. title + year) with ascending/descending per key
- **Section mode & grouping** — group the library by first letter, genre, studio, or other keys
- **Adjustable poster size & masonry view** — pick poster size and a masonry layout for dense browsing
- **Quick actions** — long-press any poster for play, mark watched/unwatched, favorite, download, add-to-playlist, delete (offline items), and details, with undo support
- Media detail pages with cast, crew, metadata, related items, **TMDB reviews**, and a **special features/extras** row
- **Chapter navigation** — chapter markers on the seek bar plus a chapter list sheet with timestamps
- **Collection management** — add titles to an existing collection or create-and-add from the detail screen
- **Audio instant mix** — build a shuffled queue around any track or album
- **Start a watch party** from any detail screen; the last-viewed season tab is remembered per series
- **Unified remote & offline detail screens** — offline items show probed local stream info as quality/audio badges
- **Pull-to-refresh** on the detail screen triggers full cache invalidation
- Person detail pages with filmography browsing, plus a dedicated **cast & crew screen**
- **Advanced series delete sheet** with multi-select episode management
- Collection/box set browsing
- Global Jellyfin search across movies, shows, music, albums, and more
- **Seerr & offline side rows** — search results also surface Seerr discover hits and downloaded items alongside the library grid, with independent error/retry handling
- Search filters for genre, year, and media type; voice search support
- Search history persisted per-user with per-item deletion, bulk clear, and hide option
- **Add-to-Playlist picker, Watch Later, and playlist creation** from the detail screen — add movies, episodes, and series to existing playlists or create new ones, with a pinned Watch Later row
- **Compact episode list preference & vertical layout toggle** for series detail
- **Photo albums & viewer**: album browsing, full-screen zoom/pan, swipe navigation, EXIF metadata
- **Watch Progress Heatmap** — GitHub-style year-in-review calendar of watch activity, filter by media type, streak tracking, tap any day for session details, share as PNG (requires Jellyfin Playback Reporting plugin)

</details>

<details>
<summary><strong>Book reader</strong></summary>

- Read comics (**CBZ/CBR**), **PDF**, and **EPUB** books in-app, straight from the detail screen (**Read** / **Continue**) — on Android, Android TV, and desktop
- **Bookmarks** — save any position (page or exact EPUB spot) with per-book counts on the detail screen
- **Highlights & notes** — select EPUB text to highlight in four colors (or underline), attach notes, and export everything to Markdown or JSON
- **In-book search** — full-text EPUB search with jump-to-hit; PDF outline (table of contents) with page jumps
- **EPUB typography** — light/sepia/dark themes, type size, font family, line height, margins, justification, and continuous-scroll mode; theme and type size settable per book
- **Paged zoom & fit** — pinch-zoom with pan, double-tap toggle, and fit modes; zoomed PDF pages re-raster sharply (up to 3×)
- **Read aloud (Android)** — system TTS with paragraph highlighting, auto chapter advance, sentence-level skip, and adjustable speed/pitch
- **Sleep timer & auto-scroll** — stop read-aloud/auto-scroll after 5–60 minutes or at chapter end
- **Chapter tick rail** — opt-in side ticks showing the current chapter in the table of contents; tap to jump, press-drag to scrub
- **Book-aware detail screen** — cover hero, format badge, reading-progress card, chapter **Contents** row that deep-links into the reader, and "Mark as finished"
- **Resume that survives devices** — position syncs to the server encoded exactly like jellyfin-web (page ticks for paged books, percent ticks for EPUB), with exact local CFI resume per install on EPUBs
- **Offline reading** — downloaded books open with no network; offline positions sync back on reconnect
- Works on **Jellyfin 10.x and 12.x** servers (12.x probes page counts and comic series metadata; older servers render a flat list)

See [Book reader](./docs/book-reader.md) for the full guide.

</details>

<details>
<summary><strong>Seerr, Radarr/Sonarr & calendar</strong></summary>

**Seerr integration**

- Jellyseerr and Overseerr connection support
- Discover trending, popular, and upcoming content
- Request content via Radarr/Sonarr directly from the app
- **Full request management screen** with:
  - Filter by request status (Pending, Approved, Declined, Processing, etc.)
  - Filter by media type (All, Movies, TV)
  - Sort by Recent or Modified, with ascending/descending toggle
  - "My Requests Only" filter for admins
  - Paginated request list with prev/next navigation
- **Admin actions**: Approve, Decline, Retry, Delete requests (with confirmation timer)
- **Remove from Radarr/Sonarr** action for processed requests
- **Request Detail Bottom Sheet** — full metadata: poster, title, status badge, overview, request ID, TMDB ID, requester, date, quality profile, root folder, seasons, and download status
- Per-request TMDB metadata enrichment (title, poster, year, overview)
- **Anime-aware TV requests** — TMDB keyword detection auto-selects your Sonarr anime quality profile, root folder, and tags
- Seerr detail pages for unavailable media
- Region configuration for streaming and discover content
- **Radarr/Sonarr connection testing** with server discovery and force-import flows

**Arr queue management**

- Browse and manage **Radarr** and **Sonarr** queues directly in the app
- Delete and re-download flow via the \*arr file-delete API
- Per-series management screen (replaces per-episode redownload dialogs)
- Queue status, progress, and quality tracking

**Calendar**

- **Upcoming releases calendar** for movies and shows from your Radarr/Sonarr libraries
- Month view with release badges and filtering by media type
- Tap any date to see what's premiering

</details>

<details>
<summary><strong>Downloads & offline</strong></summary>

- Video downloads via WorkManager with progress tracking
- Series downloads with episode selection (seasons pre-fetched when opening the sheet)
- Queued, paused, resumed, and retried downloads with HTTP Range resumption
- **Auto-retry budget** (3 attempts) with pause-reason tracking — network interruptions auto-resume; user-paused downloads stay paused
- **Notification actions** — pause, resume, or cancel transfers straight from the progress notification
- Offline playback for completed downloads
- Offline Library browser organized by series
- Offline Series view for downloaded episode browsing
- **Download details sheet** — full on-disk file inventory with sizes and a storage breakdown
- **Freshness check & selective resync** — per-axis sync baselines (metadata, artwork, subtitles, trickplay, segments) with granular force-resync options
- **Offline state sync** — favorite changes made offline sync back via an outbox with fan-out
- Foreground notification with speed and ETA

</details>

<details>
<summary><strong>SyncPlay, Play On & Live TV</strong></summary>

**SyncPlay (watch parties)**

- Create and join synchronized watch groups
- Start a **watch party from any detail screen** — group creation, join, and queue seeding in one tap
- Real-time playback sync with speed-to-sync and skip-to-sync correction
- Server time synchronization for precise coordination
- Player opens app-wide automatically when a joined group starts playing
- In-player group chat
- Group settings for repeat and shuffle modes

**Play On / casting**

- Cast to and control other Jellyfin sessions from the player
- Chromecast support via Google Cast SDK
- Media session integration for lock screen and notifications

**Live TV & DVR**

- Live TV channel browsing with current program info
- Electronic Program Guide (EPG) with program timeline
- **Delivery method selection** — choose Auto, Direct Stream, or Transcode per channel, with a live play-method badge (DIRECT/TRANSCODE) and on-error delivery-method fallback
- Direct Stream support for HTSP/HLS tuners (raw MPEG-TS passthrough)
- DVR recording management

</details>

<details>
<summary><strong>Server admin, metadata editor & remote control</strong></summary>

**Admin dashboard**

- Accessible directly via settings menu for server administrators
- Real-time system health monitor showing server status, CPU/Memory load, OS details, and server controls (restart/shutdown)
- Active User Sessions: View all active devices connected to the server, view session details, and end sessions remotely
- Library stats row: Direct overview of item counts across movies, series, episodes, albums, songs, and books
- Scheduled Tasks manager: Monitor, trigger, and cancel scheduled tasks on the Jellyfin server with **live realtime tracking** (~1 s updates)
- Server Logs viewer: Browse, view, and download active server logs with severity indicators, recent activity timelines, and a **live activity-log stream** with automatic polling fallback
- Running tasks card tracking background operations with live progress updates
- User Statistics: Per-user playback history and statistics with charts
- Stale Media Scanner: Detect unwatched/stale media with background scan worker
- Watched Media Cleanup: Bulk cleanup of watched media with audit history log

**Plugin management**

- View installed server plugins with version and status info
- Enable/disable plugins remotely
- Plugin configuration viewer
- **Markdown-rendered changelogs** on the plugin detail screen

**Metadata & media editor**

- Directly accessible from the media detail screen for authorized/admin users
- Comprehensive metadata editing (title, original title, tagline, overview, premiere/release dates, sorting titles, and custom ratings)
- Rich artwork manager: Upload, update, or remove Primary, Backdrop, Banner, Logo, Art, Disc, and Thumb images
- Subtitle editor: View embedded subtitle tracks, upload external subtitle files, and delete unwanted external tracks

**Remote control**

- WebSocket-based remote control from Jellyfin server
- Receive Play, Pause, Seek, and general commands remotely
- **General-command ladder** — navigate the app remotely (GoBack, move focus, select, open context menu) and request display content, which is idle-gated and requires explicit user consent
- Media browser service integration for third-party controller apps
- Active player management and remote playback reporting

</details>

<details>
<summary><strong>JellyPlay companion server plugin</strong></summary>

JellyPlay works great against a plain Jellyfin server — but install the companion **[JellyPlay server plugin](https://github.com/raulshma/jellyfin-plugin-jellyplay)** and a set of JellyPlay-exclusive features light up. The app probes the server for the plugin's capabilities endpoint at sign-in; every feature below degrades gracefully when the plugin is absent.

**Install** (Jellyfin 12.x / .NET 10 hosts): Jellyfin Dashboard → Plugins → Repositories → add

```
https://raw.githubusercontent.com/raulshma/jellyfin-plugin-jellyplay/main/manifest.json
```

then install **JellyPlay** from the catalog and restart.

What it unlocks:

- **Settings & profile sync** — opt-in per-user sync of preferences, search history, hidden-content (continue-watching) filters, book bookmarks/annotations and home layout across your devices: last-write-wins with server-side conflict previews (keep mine / take theirs), deletions that roam both ways, per-namespace selective sync with pending counts, restore points and export/import — live via SSE with background flushes and a silent push nudge when no live connection exists
- **Admin defaults** — tri-state (unset / suggested / forced) client defaults pushed to users from the plugin dashboard, with config backup/restore
- **Events & messages** — new-media events, admin broadcasts and a rich inbox message system over per-user SSE streams
- **Seerr bridge** — server-brokered Seerr single sign-on (password + Quick Connect) with a full catch-all API proxy, so no per-user Seerr tokens live on devices
- **Newsletter** — the weekly server digest gets a real SMTP backend
- **Ratings** — MDBList aggregates, TMDB episode/season ratings, and IMDb charts with server-held API keys and caching
- **Custom & seasonal home rows** — Letterboxd / IMDb / MDBList lists and seasonal keyword rows resolved against your library
- **Anime markers** — filler / recap / canon badges (AnimeFillerList + Tenrai) with MAL↔AniList mapping
- **Recommendations** — server-scored similar items (genres / tags / people / studios / franchise)
- **User data & transcode insights** — one-response personal ratings plus book-reader bookmark/notes sync, admin transcode dashboard data and per-user download progress

Client-side architecture, sync semantics and the wire contract are documented in the [plugin guide](./docs/jellyplay-plugin.md).

</details>

<details>
<summary><strong>Platform, theming, widgets & accessibility</strong></summary>

- Multi-server Jellyfin support with auto-discovery
- Token-based and Quick Connect authentication
- **Client certificates (mTLS)** — import a PKCS#12 bundle or PEM cert/key pair (plus an optional server CA) under _Settings → Server Management_ for servers that require mutual TLS; presented automatically, fail-closed when enabled but missing
- **Server address failover** — register primary and alternate addresses (Settings → Server Management); connectivity probes pick a reachable one, failover triggers only on connection failures, and the primary is preferred again once healthy
- Multi-user support with per-server user switching and a home-screen **user switcher** chip for multi-user servers
- **Realtime user-data sync** — home rows and open detail screens refresh live from server WebSocket pushes
- Material 3 UI with dynamic theming from artwork
- Expressive animations and spring-based motion specifications
- **Material 3 Expressive navigation bar style** option
- Shared element transitions between screens (incl. season layout switching and offline artwork)
- Performance Mode (disables animations for low-end devices)
- Predictive back gesture support
- Edge-to-edge immersive layouts
- Adaptive layouts for phone, tablet, foldable, and TV
- Android TV support with D-pad navigation and Leanback launcher
- Android TV screensaver (Daydream) with configurable slideshow and Ken Burns effect
- PIN lock and biometric authentication (fingerprint/face) with auto-lock timer
- Kids Mode with content filtering
- **8 theme variants**: Standard (Material 3 dynamic), Synthwave (neon/retro), Soothing (GitHub-dark-inspired), Monochrome (black-and-white), Vivid (bold two-tone pop), Aurora (night-sky gradient, dark-only), Sakura (soft pastels), and Vector Pop (flat poster look with thick outlines)
- 9 accent color swatches (Dynamic, Brand, Sapphire Blue, Emerald Green, Amethyst Purple, Rose Pink, Coral Orange, Amber Gold, Crimson Red)
- 3 contrast levels (Default, Medium, High) with full light/dark variants
- OLED mode for AMOLED displays
- **Home screen widgets** with configurable sources:
  - **Now Playing** — album art, transport controls (play/pause, next/prev, rewind/forward), and a 7-zone seek bar
  - **Continue Watching** — list of in-progress media items
  - **Library Recommendations** — poster grid with selectable source (Similar to Recent, Latest, Favorites, Surprise Me)
  - **Seerr Recommendations** — poster grid with selectable source (Trending, Popular Movies/TV, Upcoming Movies/TV)
- Quick settings tile launcher
- App shortcuts:
  - **Static**: Continue Watching, Search, Play Music, Downloads, Surprise Me, Settings
  - **Dynamic**: Continue Listening — auto-updates with the currently playing track and album art
- BlurHash image placeholders for smooth loading
- Localized in 9 languages (English, Deutsch, Español, Français, Italiano, Português, 日本語, 한국어, 中文)
- Deep link support via `jellyplay://` and `https://raulshma.github.io/jellyplay/` URI schemes
  - `jellyplay://media/{id}`, `jellyplay://newsletter/{section}`, `jellyplay://seerr/{tmdbId}/{type}`
  - HTTPS equivalents under `https://raulshma.github.io/jellyplay/...`
  - Widgets, notifications, and shortcuts all use deep links for navigation
- **Onboarding Wizard** — 10-step first-run setup (appearance, performance, home layout, player, audio, subtitles, security, Seerr), re-accessible anytime from Settings
- **Newsletter digest** — weekly server library-activity digest (Recently Added, Continue Watching, Next Up, Fresh Picks, Activity Digest, Library Stats) with Home banner, pull-to-refresh, and configurable schedule
- **Settings search** — find any setting instantly by name
- **New media notifications** — real-time per-library notifications when new content is added, with quiet hours, seen-media tracking, grouped notifications, and notification actions (configurable check interval, per-library channels, 30-day seen pruning, Open-detail / Mark-as-seen actions)
- **In-app self-update** — check, download, and install new releases directly from GitHub Releases (auto-check toggle + manual check in Settings → About; dismissed versions are suppressed for 24h; APK downloads survive restarts with cancel/re-download support and an opt-in auto-download of new releases). Every update URL is **fail-closed against a compiled-in GitHub owner/repo allow-list** before a download or browse starts
- **Desktop idle screen** _(desktop)_ — a configurable "Ready to play" ambient overlay after inactivity, respecting the screensaver-consent setting
- **Navigation customization** — global overflow menu on the navigation bar with active-download count badges, plus a tablet navigation-rail toggle
- **Settings deep-linking** — search from the home bar jumps straight into the matching settings screen with entry highlighting
- **Privacy & Data hub** — confirm-gated clearing of caches, image cache, and search history, plus sign-out and factory reset in one place
- **Factory reset** — reset all settings to defaults or per-category (15 categories), with a changed-count diff preview and a coverage guard keeping the reset list in sync
- **Home ambient backdrop** — blurred hero artwork / drifting palette blobs behind the home screen (auto-disabled in Performance Mode)
- **Accessibility** — blue light filter with strength control, reduce motion toggle, haptic feedback intensity, and font scaling

</details>

<details>
<summary><strong>Settings — 390+ options across 19 top-level sections</strong></summary>

- **Player**: engine selection, decoder mode, audio passthrough, orientation, seek duration, gesture toggles, autoplay, controls timeout, preload buffer, force direct play, refresh rate/resolution matching, A/B repeat, AV-sync
- **Audio**: default speed, gapless playback, crossfade, night mode, dialogue boost, equalizer, audio normalization, channel mix, dynamics compression, dialogue de-noise, virtualizer, reverb, volume memory, output device _(desktop)_
- **Language**: app language, preferred audio & subtitle languages, **track-selection presets & language rules**, subtitle tester
- **Subtitles**: language, style, trickplay, intro/outro skip (manual and auto), tap-to-translate, per-series role preferences
- **SyncPlay**: progress reporting, auto-join, sync correction parameters
- **Downloads**: connections preference, max cache size (with unlimited/0 option support)
- **Storage**: offline media management, cache size, download location
- **Visual**: dynamic theming, theme variant (Standard/Synthwave/Soothing/Monochrome/Vivid/Aurora/Sakura/Vector Pop), accent color, contrast level, OLED mode, streaming quality, performance mode, home backdrop
- **Navigation**: overflow menu visibility, tablet navigation-rail toggle, hide header on scroll
- **Account**: switch user, sign out / sign out from server, **server management** (primary & alternate addresses, failover, client certificates)
- **Activity & insights**: favorites, watch-progress heatmap, Radarr/Sonarr activity queue, upcoming releases, Seerr requests
- **Security**: PIN lock, biometric lock, auto-lock timer
- **Privacy & Data**: clear cache/image cache/search history, sign out, factory reset
- **Kids**: mode toggle, max content rating
- **Screensaver**: interval, Ken Burns effect, transition style, image categories, title overlay
- **Newsletter**: enable/disable, delivery day, notification badge
- **Notifications**: new media notifications, quiet hours, per-library channels
- **System**: admin dashboard entry, setup wizard, and the **desktop idle screen** (ambient "Ready to play" overlay)
- **Live TV/DVR**: channel sources, EPG refresh, recording defaults
- **Widget**: per-widget source configuration, refresh interval
- **Accessibility**: color blind modes, blue light filter, reduce motion, haptic intensity, font scaling
- **Experimental**: feature flags for in-development capabilities
- **Seerr**: server URL, API key, feature toggles, regions
- **Arr (Radarr/Sonarr)**: server URLs, API keys, connection testing, queue management
- **Calendar**: enabled libraries, media-type filters
- **About**: auto-generated open-source licenses (AboutLibraries), in-app update checker (auto-check toggle + manual check)
- **Backup & Reset**: export/import settings, per-category or full factory reset
- **Onboarding**: re-run setup wizard anytime
- **Settings search**: find any setting instantly by name

</details>

---

<a name="tech-stack"></a>

<details>
<summary><strong>Tech Stack</strong></summary>

| Category         | Technologies                                                                                                 |
| ---------------- | ------------------------------------------------------------------------------------------------------------ |
| Language         | Kotlin 2.3.21, Java 17                                                                                       |
| UI               | Jetpack Compose (BOM 2026.06.01), Material 3, Material 3 Expressive                                          |
| Build            | AGP 9.4.1, Gradle, KSP2                                                                                      |
| TV               | Android TV Material, Leanback                                                                                |
| Navigation       | Navigation 3                                                                                                 |
| DI               | Koin 4.2.2 (Kotlin Multiplatform)                                                                            |
| Multiplatform    | Compose Multiplatform 1.11.1 (shared core/features), libmpv via JNA (desktop)                                |
| Storage          | Room 3.0.3 (androidx.room3), DataStore Preferences, AndroidX Security-Crypto                                 |
| Background       | WorkManager, Coroutines, StateFlow                                                                           |
| Video Players    | Media3/ExoPlayer 1.10.1, libmpv, LibVLC                                                                      |
| Subtitles        | libass (ExoPlayer ASS/SSA), ASS/SSA & VTT parsing                                                            |
| Book Reader      | PDFBox 3.0.5 (desktop) / pdfbox-android 2.0.27.0, junrar 8.1.1 (CBR), vendored epub.js 0.3.93 + JSZip 3.10.1 |
| Audio Effects    | Android Equalizer, LoudnessEnhancer, Virtualizer, Reverb                                                     |
| Media Session    | Media3 Session, Media3 Cast                                                                                  |
| Casting          | Google Play Services Cast, DLNA/UPnP                                                                         |
| Networking       | OkHttp 5.4.0, Jellyfin SDK 1.8.12, kotlinx.serialization 1.11.0                                              |
| Images           | Coil 3.4.0 (3.5.0 on Android, with BlurHash), Palette (color extraction)                                     |
| Typography       | Google Fonts (Compose integration)                                                                           |
| Markdown         | multiplatform-markdown-renderer (release notes, plugin changelogs)                                           |
| Pagination       | Paging 3                                                                                                     |
| Text Recognition | ML Kit                                                                                                       |
| Biometrics       | AndroidX Biometric                                                                                           |
| Testing          | JUnit 4, MockK, Espresso, Compose UI Test, Robolectric, UIAutomator, OkHttp MockWebServer, Room Testing      |
| Code Quality     | Kover (coverage), R8 Full Mode, KSP2, CodeQL                                                                 |
| Performance      | Baseline Profiles (Macro Benchmark, phone & TV generators)                                                   |

</details>

---

## Requirements

- Android 9.0 (API 28) or later
- JDK 17
- Android Studio with Android SDK (compileSdk 37)
- A Jellyfin server (10.x or 12.x) for authentication and playback
- Optional: the [JellyPlay companion server plugin](https://github.com/raulshma/jellyfin-plugin-jellyplay) (Jellyfin 12.x / .NET 10 hosts) — unlocks settings sync, Seerr SSO, the newsletter backend, ratings, custom home rows and more; everything degrades gracefully without it

---

<a name="building"></a>

<details>
<summary><strong>Building</strong></summary>

<details>
<summary><strong>Prerequisites</strong></summary>

- Android Studio or Gradle CLI
- JDK 17
</details>

### Build Commands

```bash
# Debug builds (phone and TV)
./gradlew assemblePhoneDebug
./gradlew assembleTvDebug

# Release builds
./gradlew assemblePhoneRelease
./gradlew assembleTvRelease

# Desktop (JVM; Windows tested, macOS/Linux build-from-source and untested)
./gradlew :apps:desktop:run
```

The project uses product flavors — `phone` (standard mobile) and `tv` (Android TV with Leanback launcher). Release builds ship `arm64-v8a` and universal APKs; debug builds additionally produce `x86_64` and `x86` APKs for emulators and legacy Android TV system images.

<details>
<summary><strong>Desktop playback: bundled libmpv (Windows)</strong></summary>

Desktop video/audio playback goes through **libmpv** loaded via JNA. On
**Windows nothing is required from the user**: the build fetches a pinned,
sha256-verified `libmpv-2.dll` (LGPL build from the
[mpv-winbuild](https://github.com/zhongfly/mpv-winbuild) project) into the
gitignored `tools/mpv/` directory — the `fetchBundledLibmpv` task in
`apps/desktop/build.gradle.kts` — and ships it inside the installed app
image, so both `gradlew :apps:desktop:run` and the packaged MSI resolve it
automatically. The fetch needs a 7-Zip binary on the machine (the dev
package ships as `.7z`; standard on Windows dev boxes and CI runners); a
manually dropped `tools/mpv/libmpv-2.dll` always wins over the fetch, and
`-Pjellyplay.mpvDevUrl=…` pins a different archive.

On **Linux/macOS** (build-from-source, untested) the app still loads the
system `libmpv.so` / `libmpv.dylib` through JNA's normal search path. The
`MPV_LIBRARY` environment variable pointing at an absolute path overrides
everything on every OS, and a load failure now surfaces as a playback-error
dialog instead of a silent black screen.

</details>

</details>

---

<a name="local-development"></a>

<details>
<summary><strong>Local Development</strong></summary>

### Setup

1. Install the [Requirements](#requirements): JDK 17, Android Studio with SDK 37 (compileSdk), and a Jellyfin server (10.x or 12.x) to develop against.
2. Clone the repo and open it in Android Studio — Gradle sync pulls the wrapper, KMP toolchains, and the bundled libmpv fetch (see below).
3. Point the app at a local server (e.g. `http://<LAN-IP>:8096`) during onboarding, or use the seeded Docker server from `tools/e2e/bootstrap-jellyfin.sh`.

> The repo is Kotlin Multiplatform: day-to-day feature work happens in `shared/` (commonMain + platform `androidMain`/`jvmMain` actuals). `app/` is the only Android-only module.

### Run each target

| Target          | Command                            | Notes                                                                                       |
| :-------------- | :--------------------------------- | :------------------------------------------------------------------------------------------ |
| Android phone   | `./gradlew :app:installPhoneDebug` | Needs a device/emulator connected via adb, or run the `app` config from Android Studio      |
| Android TV      | `./gradlew :app:installTvDebug`    | Same, on a TV device/emulator                                                               |
| Windows desktop | `./gradlew :apps:desktop:run`      | First run fetches a pinned `libmpv-2.dll` — needs 7-Zip on PATH (see [Building](#building)) |

### Tests & verification

```bash
# JVM tests for a shared KMP module
./gradlew :shared:core:data:jvmTest

# Android unit tests (flavor-aware)
./gradlew :app:testPhoneDebugUnitTest

# Desktop unit tests (includes the Koin-registration guard)
./gradlew :apps:desktop:test

# Coverage report (opt-in flag required)
./gradlew koverHtmlReport -PenableCoverage
```

### Local E2E & verification lanes

`tools/e2e/` ships the driver scripts used by the project's verification passes:

- `bootstrap-jellyfin.sh` — spins up a local Jellyfin in Docker at `http://localhost:8096` (user `harness` / `harness-e2e-pass`) seeded with generated test media; requires Docker and ffmpeg.
- Desktop lanes: `desktop-session-pass.sh`, `msi-boot-pass.sh`, `desktop-native-dialog-pass.sh`, `desktop-native-dialog-flows-pass.sh`.
- Physical-device lanes (adb + UiAutomator): `device-locale-pass.sh`, `device-pip-pass.sh`.

### Tips

- Desktop playback without the auto-fetch: drop a `libmpv-2.dll` into `tools/mpv/`, or point `MPV_LIBRARY` at an absolute path to override on any OS.
- Prefer the shared tree over `app/` for new features — `app/` is Android glue (deep links, widgets, Cast, PiP host).
- After touching Koin module registration, `:apps:desktop:test` catches missing registrations that only fail at runtime otherwise.

</details>

---

<details>
<summary><strong>CI/CD</strong></summary>

A GitHub Actions workflow (`.github/workflows/release.yml`) automates release builds:

- Triggered on push to `v[0-9]+*` or `release/**` branches
- Auto-calculates version from branch name with git tag-based patch incrementing
- Builds signed release APKs for both phone and TV flavors
- Publishes APKs to GitHub Releases with auto-generated release notes

Two more workflows run alongside it:

- `.github/workflows/codeql.yml` — CodeQL security analysis on pushes/PRs to `main`, plus a weekly scheduled scan
- `.github/workflows/deploy-pages.yml` — publishes the landing page to GitHub Pages on pushes to `release-web`

A fourth workflow keeps the Kotlin Multiplatform tree honest:

- `.github/workflows/kmp-build.yml` — on pushes to `main`/`master`/`kmp-alpha` and every PR, compiles all configured targets of the `shared/` tree (JVM + Android) on Ubuntu, Windows, and macOS, runs the full `jvmTest` suite per shared module, and runs `:apps:desktop:test` (home of the Koin-registration guard)

</details>

---

<a name="permissions"></a>

<details>
<summary><strong>Permissions</strong></summary>

| Permission                          | Purpose                                                                                               |
| ----------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `INTERNET`                          | Jellyfin API access and media playback                                                                |
| `ACCESS_NETWORK_STATE`              | Network connectivity monitoring                                                                       |
| `ACCESS_WIFI_STATE`                 | WiFi state for server discovery and streaming                                                         |
| `ACCESS_LOCAL_NETWORK`              | Android 17+ (API 37) runtime permission for LAN server discovery and direct local-network connections |
| `CHANGE_WIFI_MULTICAST_STATE`       | Jellyfin server auto-discovery                                                                        |
| `WAKE_LOCK`                         | Keeps CPU/Wi-Fi awake during backgrounded streaming playback                                          |
| `FOREGROUND_SERVICE`                | Foreground service for playback and downloads                                                         |
| `FOREGROUND_SERVICE_MEDIA_PLAYBACK` | Media playback foreground service type                                                                |
| `FOREGROUND_SERVICE_DATA_SYNC`      | Download worker foreground service type                                                               |
| `POST_NOTIFICATIONS`                | Playback, download, and widget notifications                                                          |
| `MODIFY_AUDIO_SETTINGS`             | Audio effects (equalizer, loudness, virtualizer)                                                      |
| `RECORD_AUDIO`                      | Required by the real-time audio visualizer                                                            |
| `USE_BIOMETRIC`                     | Biometric authentication (fingerprint/face)                                                           |
| `REQUEST_INSTALL_PACKAGES`          | In-app self-update from GitHub Releases                                                               |
| `SYSTEM_ALERT_WINDOW`               | Overlay media-controller card                                                                         |
| `WRITE_EPG_INPUT`                   | Publishes Watch Next / EPG rows on Android TV                                                         |

</details>

---

<a name="project-structure"></a>

<details>
<summary><strong>Project Structure</strong></summary>

The codebase is Kotlin Multiplatform: features and core live in the `shared/` KMP tree (commonMain + platform actuals). The legacy Android-only core modules were folded in at the 2026-09-12 cutover — their Android halves are now `androidMain` source sets of the shared core modules — leaving `app/` the only Android-only Gradle module, beside the desktop shell.

```
apps/
  desktop/                Windows desktop shell (Compose Multiplatform; libmpv engine via JNA) — preview
app/                      Android application module (deep links, widgets, Cast, PiP host activity, shortcuts, TV)
shared/
  core/
    concurrency/          Coroutine utilities (map-concurrent, task bundling, cancellation-safe runCatching)
    model/                Pure Kotlin data models (111 model files)
    designsystem/         Theming (8 variants), colors, shapes, typography, motion
    datastore/            DataStore preferences (390+ settings), encrypted credentials, OS keyring seam
    database/             Room KMP database (20 DAOs), v57 migration chain
    network/              Jellyfin API clients, Radarr/Sonarr (Arr) clients, Seerr client, TMDB, LRCLIB, Wyzie/OpenSubtitles, GitHub Releases, server discovery, address failover
    data/                 Repositories, playback managers, audio effects, SyncPlay, downloads; Android-only machinery (workers, receivers, media session, Cast, notifications) lives in its androidMain
    ui/                   Shared UI components, adaptive layouts, TV focus, animations, navigation; Android shims (biometric, WebView trailer, TV bits) in its androidMain
    player-contract/      Engine-agnostic MediaEngine contract + engine-shared machinery
  feature/
    auth/                 Server selection and authentication
    onboarding/           First-run setup wizard (10-step preferences)
    home/                 Home screen, Kids home, newsletter banner, discover sections, and the offline home (downloaded content shelf + gate)
    library/              Library browsing and media collections (auto-filters to downloads while offline)
    photos/               Photo albums and full-screen photo viewer with brightness/contrast/saturation adjustments and export
    search/               Search experience with history
    details/              Media detail, person detail, collection detail, Seerr detail
    player-video/         Video playback UI, multi-engine support, SyncPlay integration, Play On
    player-audio/         Audio playback UI, lyrics, equalizer, ambient mode
    player-live/          Live TV playback UI, delivery-method selection, play-method badge, error recovery
    player-book/          Book reader (CBZ/CBR/PDF/EPUB — typography, bookmarks, highlights & notes, in-book search, TTS read-aloud, sleep timer, chapter tick rail)
    downloads/            Download management, offline library, and offline playback
    settings/             Settings, server/user management, Seerr & Arr configuration, settings search, import preview
    music/                Music browsing, smart/mood playlists, artist/album details
    livetv/               Live TV channels, EPG guide, and DVR
    syncplay/             SyncPlay group management and watch party UI
    editor/               Metadata, artwork, and subtitle editor for media items
    admin/                Admin dashboard for server status, active devices, logs, tasks, user stats
    newsletter/           Weekly server digest with library activity, curated picks, and stats
    insights/             Watch progress heatmap with streak tracking and share-as-image
    requests/             Jellyseerr/Overseerr request management with filter/sort/admin actions
    arrqueue/             Radarr/Sonarr queue management, delete & re-download flow
    calendar/             Upcoming releases calendar from Radarr/Sonarr libraries
    shortcuts/            App shortcuts (static + dynamic, e.g. Continue Listening)
    subtitle-tester/      Subtitle parser & styling test harness (dev/diagnostic tool, Android-only)
    shell/                Shell-graph aggregator (appSections nav graph + ShellHostHooks shared by the Android and desktop shells)
baselineprofile/         Baseline profile generator for phone startup optimization
tools/                   Dev/CI drivers: e2e/ (bootstrap-jellyfin; desktop-session / msi-boot / native-dialog passes — desktop lane scripts; device-locale / device-pip passes — physical-phone adb+uiautomator lanes), perf/ (desktop-baseline.sh), and the gitignored mpv/ drop-in dir for libmpv-2.dll (wired into :apps:desktop:test, see Building)
website/                 Landing page (GitHub Pages)
docs/                    Documentation guides
```

</details>

---

<details>
<summary><strong>Documentation</strong></summary>

Looking for setup, integration, or troubleshooting guides?

- [Setup guide](./docs/setup.md) — install JellyPlay on any device
- [Android TV & Fire TV setup](./docs/android-tv-setup.md) — sideload & install on the big screen
- [Jellyseerr/Seerr/Overseerr integration](./docs/jellyseerr-integration.md) — request movies & shows from inside the app
- [SyncPlay watch parties](./docs/syncplay-guide.md) — synchronized group playback
- [Offline downloads](./docs/offline-downloads.md) — download media for travel
- [Music player & synced lyrics](./docs/lyrics-music-player.md) — get the most out of your library
- [Book reader](./docs/book-reader.md) — read comics, PDFs, and EPUBs in-app
- [Player engines comparison](./docs/player-engines.md) — ExoPlayer vs libmpv vs LibVLC
- [Playback progress sync](./docs/playback-progress-sync.md) — how watch progress is reported and synced
- [Companion server plugin](./docs/jellyplay-plugin.md) — the JellyPlay server plugin: what it adds, install, and how the client integrates

</details>

---

<details>
<summary><strong>See Also</strong></summary>

Other open-source projects in the Jellyfin ecosystem:

- [jellyfin-plugin-jellyplay](https://github.com/raulshma/jellyfin-plugin-jellyplay) — the JellyPlay companion server plugin (settings sync, events, Seerr bridge, ratings, custom rows & more)
- [jellyfin/jellyfin](https://github.com/jellyfin/jellyfin) — the free software media system
- [jellyfin/jellyfin-android](https://github.com/jellyfin/jellyfin-android) — the official Android client (web wrapper)
- [jellyfin/jellyfin-androidtv](https://github.com/jellyfin/jellyfin-androidtv) — the official Android TV client
- [jarnedemeulemeester/findroid](https://github.com/jarnedemeulemeester/findroid) — third-party native Android client
- [damontecres/Wholphin](https://github.com/damontecres/Wholphin) — OSS Android TV client
- [MakD/AFinity](https://github.com/MakD/AFinity) — modern Compose + LibMPV client
- [streamyfin/streamyfin](https://github.com/streamyfin/streamyfin) — cross-platform Expo client
- [fallensword/awesome-jellyfin](https://github.com/awesome-jellyfin/awesome-jellyfin) — curated list of Jellyfin plugins, themes & clients

</details>

---

<details>
<summary><strong>At a Glance</strong></summary>

| Metric                | Value                                                                                                                          |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Gradle modules        | 37 (app + 9 shared core + 25 shared feature + desktop shell + baseline profile)                                                |
| Feature modules       | 25 (KMP, under `shared/feature/`)                                                                                              |
| Configurable settings | 390+                                                                                                                           |
| Data models           | 111                                                                                                                            |
| API clients           | 20+                                                                                                                            |
| Room DAOs             | 20                                                                                                                             |
| Supported languages   | 9 (English, Deutsch, Español, Français, Italiano, Português, 日本語, 한국어, 中文)                                             |
| External integrations | Jellyfin, Jellyseerr/Overseerr, Radarr, Sonarr, LRCLIB, TMDB, Wyzie, OpenSubtitles, GitHub Releases (self-update), Google Cast |
| Min SDK               | 28 (Android 9.0)                                                                                                               |
| Target/Compile SDK    | 37                                                                                                                             |

</details>

---

## Contributing

We welcome contributions of all sizes — bug reports, feature requests, code,
translations, and documentation.

## Sponsor

If JellyPlay is useful to you, consider sponsoring development:

<iframe src="https://github.com/sponsors/raulshma/button" title="Sponsor raulshma" height="32" width="114" style="border: 0; border-radius: 6px;"></iframe>

## License

This project is licensed under the **GNU General Public License v3.0** — see
the [`LICENSE`](LICENSE) file for details.

By contributing to JellyPlay, you agree that your contributions will be
licensed under the same GPL-3.0 license.

## Star History

<a href="https://star-history.com/#raulshma/jellyplay&Date">
 <picture>
   <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=raulshma/jellyplay&type=Date&theme=dark" />
   <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=raulshma/jellyplay&type=Date" />
   <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=raulshma/jellyplay&type=Date" />
 </picture>
</a>