Files
plezy/pubspec.yaml
T
edde746 05fd622968 feat(emby): add Emby as a MediaBrowser backend alongside Jellyfin
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.
2026-08-05 06:09:26 +02:00

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/