Emby is Jellyfin's upstream ancestor and speaks a near-identical MediaBrowser
API, so the existing Jellyfin stack is parameterised by a `MediaBrowserDialect`
rather than forked. `JellyfinClient`, its auth service, endpoint discovery, LAN
discovery, and the add/edit connection screens all take the dialect and keep one
implementation; `MediaBackend.emby` and `ConnectionKind.emby` carry it through
the neutral models, the Drift `kind` discriminator, downloads, and caches.
Every divergence below was measured against a live Emby 4.9.5 server, not
inferred from documentation, and each is documented at its capability getter.
Jellyfin's request strings stay byte-identical so nothing about its behaviour
changes.
Routes and auth
- Emby only accepts the pre-10.9 user-scoped item routes (`/Users/{id}/Items/…`,
`/Users/{id}/PlayedItems/…`, `/Users/{id}/FavoriteItems/…`); the unprefixed
forms Jellyfin 10.11 added return 404.
- The API is also served under a legacy `/emby` prefix, and both dialects accept
the token as `X-Emby-Token` or `api_key=`.
- Emby answers only its own LAN discovery datagram ("who is EmbyServer?") and
ignores Jellyfin's; its default HTTPS port is 8920.
- No `/QuickConnect` route exists, so Quick Connect stays Jellyfin-only.
Row fields Emby withholds
- `ProductionYear`, `OfficialRating`, `PremiereDate` and `DateCreated` are absent
from list rows unless named in `Fields`, which would otherwise strip the year
and age-rating badge from every card in the app.
- `UserData.LastPlayedDate` never appears on a list row under `Fields=UserData`,
`EnableUserData=true` or the user-scoped `Ids=` form — only on the single-item
detail route, or when the Emby-specific `UserDataLastPlayedDate` token is
requested. Without it every recency-ordered surface silently degrades to
library-add time, and `JellyfinApiCache.applyWatchState` stamps
`DateTime.now()` on watched rows, so an offline watch-state pull would rewrite
the cached play time of everything it walked.
Continue Watching and Next Up
- Emby computes Next Up per series only: the library-wide `/Shows/NextUp` query
returns nothing under every parameter combination tried. The shelf is
therefore reconstructed from a played-episode recency scan plus one
`/Shows/NextUp?SeriesId=` per distinct series, bounded by a shared wall clock
that covers the scan as well — per-request timeouts cannot bound the pass
because `MediaServerHttpClient` times the connect and receive phases
independently. Rows are stamped with their series' newest play from the same
response that ordered them, so no per-series enrichment request is needed.
- `/Shows/NextUp` ignores `NextUpDateCutoff`, and no server-side played-date
filter exists to delegate to (`MinDatePlayed` and `MinDateLastPlayed` are
ignored; `MinDateLastSaved`, `MinDateCreated` and `MinPremiereDate` filter
unrelated dates), so the 365-day window is applied to the scanned dates.
- The resume route returns items with no saved position, including plain next
episodes, so the Emby resume leg reads from `/Items?Filters=IsResumable`.
- Emby is ahead of Jellyfin in one place: `/Users/{id}/Items/{id}/HideFromResume`
makes Continue Watching removal a real capability.
Everything else
- `/Sessions/Playing` and `/Sessions/Playing/Progress` reject a body with no
`PlaySessionId` (HTTP 400), so playback reporting always sends one.
- Passing any `MediaTypes` value to the playlist query returns an empty list.
- There is no aggregate `/Items/Filters` route; the four filter facets are
reassembled from `/Genres`, `/OfficialRatings`, `/Studios` and `/Tags`.
- Metadata writes take name-pair lists (`Genres: [{'Name': 'Action'}]`); the
plain string array is accepted and then silently discarded.
- Custom artwork uploads must be base64 text, not raw bytes — which was broken
for Jellyfin too and is fixed for both.
- Trickplay, media segments and lyrics 404 on Emby, so scrub previews are absent
and intro/credit markers fall back to chapter names.
Verified against a local Emby 4.9.5 and a Jellyfin 10.11.11 control server:
onboarding, browse, detail, playable stream URLs serving real bytes, subtitle
sidecars, watch-state write and restore, hubs, cross-server aggregation and
search across both backends simultaneously.
163 lines
5.0 KiB
YAML
163 lines
5.0 KiB
YAML
name: plezy
|
|
description: "A beautiful Plex and Jellyfin client for Flutter"
|
|
publish_to: "none"
|
|
version: 2.12.1+127
|
|
|
|
environment:
|
|
sdk: ">=3.12.0 <4.0.0"
|
|
flutter: ">=3.44.0"
|
|
|
|
dependencies:
|
|
flutter:
|
|
sdk: flutter
|
|
intl: ^0.20.2
|
|
json_annotation: ^4.12.0
|
|
shared_preferences: ^2.5.4
|
|
cached_network_image_ce: ^4.9.0
|
|
http: ^1.2.0
|
|
url_launcher: ^6.3.0
|
|
uuid: ^4.4.0
|
|
window_manager: ^0.5.1
|
|
logger: ^2.7.0
|
|
package_info_plus: ^9.0.0
|
|
device_info_plus: ^12.3.0
|
|
provider: ^6.1.2
|
|
flex_color_picker: ^3.8.0
|
|
qr_flutter: ^4.1.0
|
|
slang: ^4.14.0
|
|
slang_flutter: ^4.14.0
|
|
duration: ^4.0.3
|
|
connectivity_plus:
|
|
git:
|
|
url: https://github.com/fluttercommunity/plus_plugins
|
|
ref: bf04cdf66598dc3fca274b8b1db2b92b0bf6b73e
|
|
path: packages/connectivity_plus/connectivity_plus
|
|
os_media_controls:
|
|
git:
|
|
url: https://github.com/edde746/media_controls
|
|
ref: 75556a968a4a1ebc42fbb8c31942c2418d4326e1
|
|
rate_limiter: ^1.0.0
|
|
wakelock_plus:
|
|
path: packages/wakelock_plus
|
|
path_provider: ^2.1.0
|
|
path: ^1.9.0
|
|
universal_gamepad: ^1.5.7
|
|
drift: ^2.31.0
|
|
crypto: ^3.0.7
|
|
cryptography: ^2.9.0
|
|
file_picker: ^11.0.2
|
|
saf_util:
|
|
# Vendored upstream 3.1.0 with a pending Result lifecycle fix (CHANGELOG) —
|
|
# upstream still double-replies MethodChannel results and crashes the Activity.
|
|
path: packages/saf_util
|
|
saf_stream: ^3.1.0
|
|
material_symbols_icons: ^4.2951.0
|
|
web_socket_channel: ^3.0.1
|
|
dart_discord_presence: ^1.2.0
|
|
flutter_svg: ^2.2.3
|
|
# Fork note: 41c16be creates the desktop temp parent before opening the
|
|
# partial file, independently fixing upstream #649's missing-directory case.
|
|
# Upstream's later target-directory fallback is unnecessary while this fork
|
|
# retains its same-filesystem move and resume-data cleanup implementation.
|
|
# No per-host trust bypass is carried: Plezy has no app-wide certificate
|
|
# exception policy, and weakening only background downloads would make API
|
|
# and playback behavior inconsistent. Revisit only with an explicit,
|
|
# host-scoped trust setting shared by every media-server HTTP client.
|
|
background_downloader:
|
|
git:
|
|
url: https://github.com/edde746/background_downloader
|
|
ref: 021e2075260e324b7440a5c81b57bf0242d35020
|
|
sentry_flutter: ^9.25.0
|
|
auto_updater:
|
|
git:
|
|
url: https://github.com/edde746/auto_updater
|
|
path: packages/auto_updater
|
|
ref: 9e150f7
|
|
cupertino_http: ^3.0.2
|
|
cronet_http: ^1.6.0
|
|
win_http: ^0.2.2
|
|
collection: ^1.18.0
|
|
freezed_annotation: ^3.1.0
|
|
xml: ^7.0.1
|
|
string_similarity: ^2.2.0
|
|
unorm_dart: ^0.3.2
|
|
|
|
dev_dependencies:
|
|
flutter_test:
|
|
sdk: flutter
|
|
flutter_lints: ^6.0.0
|
|
# Root analysis traverses packages/saf_util and consumes its lint profile.
|
|
mgenware_dart_lints: ^8.0.0
|
|
fake_async: ^1.3.3
|
|
stream_channel: ^2.1.4
|
|
build_runner: ^2.13.0
|
|
json_serializable: ^6.7.1
|
|
slang_build_runner: ^4.14.0
|
|
sentry_dart_plugin: ^3.3.0
|
|
# Used by the explicit check-unused-* CI steps; its analyzer plugin is disabled.
|
|
dart_code_linter: ^4.1.8
|
|
drift_dev: ^2.28.3
|
|
shared_preferences_platform_interface: ^2.4.0
|
|
path_provider_platform_interface: ^2.1.0
|
|
# Drive the real desktop preference backends against a temp file in tests, so
|
|
# the #1732 corrupt-store repair loop and the vendored atomic write are both
|
|
# covered against genuine file I/O. All four expose `fs`/`pathProvider`.
|
|
# The two shared_preferences packages resolve to the vendored copies below.
|
|
shared_preferences_linux: ^2.4.1
|
|
shared_preferences_windows: ^2.4.1
|
|
path_provider_linux: ^2.2.1
|
|
path_provider_windows: ^2.3.0
|
|
plugin_platform_interface: ^2.1.0
|
|
freezed: ^3.2.5
|
|
analyzer: 10.2.0
|
|
|
|
dependency_overrides:
|
|
auto_updater_platform_interface:
|
|
git:
|
|
url: https://github.com/edde746/auto_updater
|
|
ref: 9e150f7
|
|
path: packages/auto_updater_platform_interface
|
|
auto_updater_macos:
|
|
git:
|
|
url: https://github.com/edde746/auto_updater
|
|
ref: 9e150f7
|
|
path: packages/auto_updater_macos
|
|
auto_updater_windows:
|
|
git:
|
|
url: https://github.com/edde746/auto_updater
|
|
ref: 9e150f7
|
|
path: packages/auto_updater_windows
|
|
# Vendored 2.4.1 with an atomic store replacement (CHANGELOG, provenance.json)
|
|
# — upstream truncates the live preference document on every write, which is
|
|
# the corruption behind #1732.
|
|
shared_preferences_windows:
|
|
path: packages/shared_preferences_windows
|
|
shared_preferences_linux:
|
|
path: packages/shared_preferences_linux
|
|
sentry:
|
|
org: plezy
|
|
project: plezy
|
|
url: https://bugs.plezy.app
|
|
upload_sources: true
|
|
commits: false
|
|
|
|
flutter:
|
|
uses-material-design: true
|
|
assets:
|
|
- assets/plezy.png
|
|
- assets/plezy_adaptive_foreground.svg
|
|
- assets/plex_chevron.svg
|
|
- assets/jellyfin_icon.svg
|
|
- assets/emby_icon.svg
|
|
- assets/trakt_circlemark.svg
|
|
- assets/mal_mark.svg
|
|
- assets/anilist_mark.svg
|
|
- assets/simkl_mark.svg
|
|
- assets/seerr_mark.svg
|
|
- assets/go-noto-current-regular.ttf
|
|
- assets/shaders/nvscaler/
|
|
- assets/shaders/artcnn/
|
|
- assets/shaders/anime4k/
|
|
- assets/player_icons/
|
|
- assets/rating_icons/
|