Two regressions since 2.9.1 broke subtitles on transcoded playback. Sincea1b6a8971sidecars load with the media behind a 10s open guard, so a subtitle URL the server is slow to serve — Jellyfin extracting an embedded stream while its transcoder spins up — tripped the guard: stop, reopen without subtitles, "Selected subtitles could not be loaded" snackbar, and an emptied subtitle menu. Since2b3853a88every embedded Plex subtitle was handed to the player as a sidecar whose URL is the original container, so a transcode also range-read and demuxed the source over HTTP — for a 40 GB remux, purely to find a subtitle track — which is also why PGS never appeared: the client was handed a container to demux rather than a rendition to play. Delivery is the server's job again, backported from the AVPlayer branch (42ba01440, the subtitle subset of 6852ac274, and a3da81e83) and adapted to main's mpv backend: Plex burns every embedded track (subtitles=burn); only a real external file with a /library/streams key stays a client-fetched sidecar. A burn is a re-encode, so directPlay is withdrawn — a real PMS answers HTTP 400 to directPlay=1 with burn — and the burn is aimed by selecting the stream on the part first via the selectStreams PUT, because the decision endpoint ignores subtitleStreamID alongside subtitles=burn. An unaimable or undeliverable burn (dvb_teletext) refuses the transcode and falls back to warned direct play rather than welding the wrong language in or silently dropping the caption. Main's per-preset directPlay/directStream pinning is kept; verified against a live PMS that burn works under directStream=0. Jellyfin never offers image formats as External, so bitmaps fall through to Encode and are burned; text External is withheld per request when the effective selection — including the server's DefaultSubtitleStreamIndex — is embedded, and offered when it is a real file, so a file is delivered as a file and never fetched twice. The burned row is excluded from the sidecars; remaining text rows stay extractable, which is how a secondary track still renders over a transcode. Sidecar URLs now use the format extension the endpoint expects instead of the reported codec name. The controls and selection layers learn what burning means: burn eligibility is the codec's property, so burned rows stay selectable in the menu; any change away from a burned selection renegotiates with the server instead of pretending a local switch worked; the visibility shortcut explains itself instead of doing nothing; and the track manager is told when the primary is server-rendered so it stops waiting out a thirty-second deadline for a native track that is already pixels. Verified: analyzer parity, clean_translations --check --strict, full flutter test (5749), and decision-level runs against live Plex and Jellyfin servers — text and PGS burn decisions, the directPlay=1+burn 400, External file delivery, an unchanged no-burn baseline, and a real burn session serving its playlist. The pre-commit aggregate was bypassed for pre-existing main-state findings outside this diff: 21 format-drifted files and three unused test seams in lib/main.dart. close #1738 Refs #1815, #1622.
Plezy
A modern client for Plex, Jellyfin, and Emby on desktop, mobile, and TV. Built with Flutter for native performance and a clean interface.
Website · Screenshots · Download · Contributing · License
Download
| Platform | Download |
|---|---|
| macOS | DMG (x64, arm64) |
| Linux x64 | .deb · .rpm · .pkg.tar.zst · portable tar.gz |
| Linux arm64 | .deb · .rpm · .pkg.tar.zst · portable tar.gz |
Package managers:
- Nix - Community package by @mio-19 and @MiniHarinn
- Homebrew (macOS):
brew tap edde746/plezy https://github.com/edde746/plezy brew install --cask plezy - AUR (Arch Linux) - Community maintained by @jianglai:
yay -S plezy-bin - WinGet (Windows):
winget install edde746.Plezy
Features
Browse & Discover
- Libraries, collections, and playlists — video and audio
- Discover hub — Continue Watching, Next Up, trending, and recommendations
- Cross-server search across every connected Plex, Jellyfin, and Emby server
- Filtering, sorting, and alphabetical jump navigation
- Folder browsing and folder playback — home-video libraries open in folder view
- Resolution, HDR/Dolby Vision, and audio-format badges on cards and detail pages
- Favorites and unwatched library filters1
- Extras — trailers, deleted scenes, behind-the-scenes
Explore & Requests
- Explore tab — watchlist, trending, popular, and recommendation rows from Plex Discover2 , Trakt, MyAnimeList, AniList, Simkl, and Seerr3
- Search any connected catalog source
- Catalog titles matched back to your own libraries by external ID
- Seerr — request movies and shows with per-season, 4K, and advanced destination options, and see request status inline
- Watchlist sync — add and remove titles on Plex, Trakt, MyAnimeList, AniList, and Simkl from anywhere in the app
Playback
- Wide codec support (HEVC, AV1, VP9, and more)
- HDR and Dolby Vision4
- Direct play, or transcode presets from 240p/320 kbps to 1080p/20 Mbps
- Multi-version switching with per-version file details
- Full ASS/SSA subtitles with customizable styling
- Online subtitle search & download2
- Audio & subtitle choices remembered per title, or follow the server's per-episode selections
- Progress sync and resume
- Auto-play next episode with skip intro / skip credits
- Chapter navigation with thumbnail scrub previews
- Playback speed from 0.25x to 8x, audio sync offset, sleep timer (fixed durations or end of video)
- Video zoom 50-200% with pinch, presets, and hotkeys
- Audio passthrough5 , stereo downmix with center-channel boost, and loudness normalization
- File Info sheet — every version, file, and stream the server reports
- Ambient lighting and GLSL shader presets6
- Picture-in-Picture7
- Refresh-rate matching8
- External player launch (VLC, MX Player, etc.) with progress sync back9
Music
- Music libraries — artist, album, and track browsing with square artwork
- Album and artist screens with play, shuffle, and Instant Mix
- Gapless playback with a full play queue — reorder, remove, play next, add to queue
- Now Playing with synced lyrics10 , persistent mini-player, and sleep timer
- Background playback with lock-screen, media-key, and notification controls11
- Offline playback of downloaded albums and tracks
- Streaming quality presets — Original, 320, 192, or 128 kbps
Live TV & DVR
- Live TV channel browsing, tuning, and favorites
- EPG guide with What's On and per-show schedules
- DVR recording rules, scheduled recordings, and a rememberable recording target library2
- Multi-server Live TV support where available
Downloads & Offline
- Download movies, shows, and music for offline playback12
- Background queue with pause / resume
- Sync rules for automatic downloads, with per-show "Include Specials"
- Offline browsing with watch state sync-back on reconnect
Watch Together
- Synchronized playback with friends
- Real-time play / pause / seek sync
Integrations
- Discord Rich Presence13
- Trakt, MyAnimeList, AniList, and Simkl — ratings, watched sync, and real-time scrobbling14
- Plezy Remote — control desktop and TV from mobile
- Watch Next row and tvOS Top Shelf15
Platform & Customization
- Desktop, mobile, and TV — full D-pad, keyboard, and gamepad support
- Multiple servers at once — Plex, Jellyfin, and Emby side by side
- Profiles with per-profile downloads, watch state, and settings; Plex Home switching with PIN
- Jellyfin and Emby local-server discovery and multiple URLs per server; Quick Connect sign-in16
- TV layout options — corner spotlight backdrop, full-card artwork, and Force TV mode on desktop
- Customizable keyboard shortcuts13
- Metadata and artwork editing
- Settings import/export
- Localized in English plus 21 translations
Building from Source
Prerequisites
- Flutter SDK 3.44.0+
- A Plex account, or a Jellyfin or Emby server with user credentials
Setup
git clone https://github.com/edde746/plezy.git
cd plezy
flutter pub get
scripts/codegen.sh
flutter run
Code Generation
After modifying model classes or other generated sources:
scripts/codegen.sh
After modifying translations:
dart run slang
Local Checks
scripts/ci_checks.sh
To install the same pre-commit checks locally:
scripts/setup_hooks.sh
End-to-end tests (Android emulator plus a Dockerized Jellyfin fixture):
python3 scripts/run_maestro.py basic
Contributing
See CONTRIBUTING.md for development workflow, formatting, tests, and translation guidelines.
License
Plezy is licensed under GPL-3.0.
Acknowledgments
- Built with Flutter
- Supports Plex Media Server, Jellyfin, and Emby
- Playback powered by mpv, MPVKit, Android ExoPlayer, libass-android, and libmpv-android
-
Jellyfin and Emby only. ↩︎
-
Plex only. ↩︎
-
Requires connecting the service under Settings > Services. ↩︎
-
In-app HDR toggle on Windows, macOS, iOS, and tvOS. Dolby Vision on Android and Apple TV. ↩︎
-
Desktop, Android TV, and Apple TV. ↩︎
-
Requires the mpv player backend — unavailable on iOS and tvOS, and Android defaults to ExoPlayer. ↩︎
-
Android, iOS, and macOS — not on Android TV or Apple TV. ↩︎
-
Windows, Android, and tvOS. ↩︎
-
Progress sync on Android. ↩︎
-
Where your server provides lyrics. ↩︎
-
tvOS pauses music when the app is backgrounded. ↩︎
-
Not available on tvOS. ↩︎
-
Desktop only. ↩︎
-
Real-time scrobbling on Trakt and Simkl; MyAnimeList and AniList update on completion. ↩︎
-
Android TV / Fire TV and tvOS. ↩︎
-
Jellyfin only. ↩︎




