Files
plezy/lib/media/server_capabilities.dart
T
edde746 352b88109b refactor: extract shared mixins and helpers, drop dead abstractions
Introduces shared seams for paginated views, D-pad reorder, media control
routing, async singletons and the device method channel, then points the
open-coded copies at them.

Also removes unused models and duplicated provider/server plumbing, folds
the twice-implemented artifact store in the server, and factors the
repeated Flutter toolchain prologue in CI into a composite action.
2026-07-26 06:09:48 +02:00

247 lines
9.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// How the alpha-jump bar behaves for libraries on this backend.
enum AlphaBarMode {
/// No alpha bar — hide entirely.
none,
/// Plex: server reports per-letter cumulative offsets via `/firstCharacter`,
/// taps scroll the grid to the offset.
scrollSnap,
/// Jellyfin: bar acts as a filter button — taps set `NameStartsWith` query
/// param, results re-fetch.
nameStartsWithFilter,
}
/// Static capability flags advertised by a [MediaServerClient]. UI consults
/// these to gate feature affordances per server (e.g. hide Live TV when no
/// connected server supports it).
///
/// These describe what the *backend kind* supports in this app's current
/// implementation — not necessarily what the wire protocol can do. As more
/// Jellyfin features are wired in over time, the corresponding flags flip
/// without changing call sites.
class ServerCapabilities {
/// Server-side `PlayQueue` resource (Plex `/playQueues`) — enables shared
/// queue state across devices and Watch Together coordination.
final bool serverSidePlayQueue;
/// Server-side editable playlists (Plex `/playlists`, Jellyfin
/// `/Playlists`).
final bool serverSidePlaylists;
/// This backend kind has a Live TV / DVR API the app can talk to. Whether
/// a *specific* server has Live TV configured is a runtime concern —
/// [MultiServerProvider.checkLiveTvAvailability] probes each server and
/// only those with channels surface in [MultiServerProvider.liveTvServers].
final bool liveTv;
/// Server has DVR/recording lineups (Plex `/livetv/dvrs`). Channel listing
/// is gated by [liveTv]; this flag enables the additional recordings/scheduling
/// UI. Jellyfin's DVR API isn't wired in this app yet, so it stays false even
/// when [liveTv] is true.
final bool liveTvDvr;
/// Server proxies subtitle search (e.g. OpenSubtitles).
final bool subtitleSearch;
/// Server can transcode video.
final bool videoTranscoding;
/// Server supports server-side downloads / "sync" (the queued-from-server
/// model). Both Plex and Jellyfin support client-driven downloads, which
/// is a separate concept.
final bool serverSideSync;
/// Server provides curated recommendation hubs (Plex Discover). Jellyfin
/// returns synthesized hubs but with sparser categorisation.
final bool richHubs;
/// Numeric ratings (Plex 010 via [Item.userRating]). Jellyfin has no
/// numeric user rating, so star sliders should be hidden.
final bool numericUserRating;
/// Per-user favorite flag ("heart") on media items. Jellyfin exposes it via
/// `/UserFavoriteItems/{itemId}?userId=...`; Plex has no equivalent.
final bool userFavorites;
/// Hide an item from Continue Watching without changing watch state or
/// playback progress. Plex exposes this directly; Jellyfin does not.
final bool continueWatchingRemoval;
/// External subtitle search/marketplace (Plex `/library/metadata/{id}/subtitles`).
/// Hides the "Search subtitles" affordance when false.
final bool externalSubtitleSearch;
/// Persisting per-track audio/subtitle preferences server-side. Plex uses
/// `/library/metadata/{id}/prefs` + `selectStream`; Jellyfin saves selected
/// stream indexes from `/Sessions/Playing/Progress` when the user's Jellyfin
/// remember-selection settings are enabled. When false, in-player switching
/// still works but choices don't follow the user across devices.
final bool trackPreferencePersistence;
/// Multi-endpoint connection model with endpoint racing/failover. Plex gets
/// local/remote/relay candidates from plex.tv; Jellyfin uses user-entered
/// URLs for the same server.
final bool endpointFailover;
/// Watch progress can be queued offline and replayed when reconnected
/// ([OfflineWatchSyncService]). Jellyfin reports inline only today.
final bool offlineWatchQueue;
/// Discord rich-presence integration. Plex-only because the RPC payload
/// uses Plex-shaped session/metadata.
final bool discordRpc;
/// Server exposes metadata edit endpoints. Hides edit affordances when false.
final bool richMetadataEdit;
/// How the alpha-jump bar should behave for this backend's libraries.
final AlphaBarMode alphaBar;
/// Server can supply thumbnails for the player's seek-bar scrub preview.
/// Plex serves them as a `.bif` asset; Jellyfin uses `/Trickplay` sprite
/// sheets. Both backends are wired through [ScrubPreviewSource]; the flag
/// gates whether the player attempts the load at all.
final bool scrubThumbnails;
/// Library section exposes a folder hierarchy. Plex uses
/// `/library/sections/{id}/folders`; Jellyfin uses direct-child
/// `/Items?ParentId=...&Recursive=false` queries.
final bool folderGrouping;
/// Server can supply track lyrics. Jellyfin exposes `/Audio/{id}/Lyrics`;
/// Plex surfaces sidecar `.lrc`/`.txt` files as track streams
/// (`streamType 4`) fetched via `/library/streams/{id}`. Gates the lyrics
/// affordance in the music player; per-track absence is the runtime gate.
final bool lyrics;
/// Server can build an "instant mix" / radio track list from a seed item.
/// Jellyfin: `/Items/{id}/InstantMix`; Plex: station play queues
/// (`POST /playQueues?type=audio&uri=...station...`).
final bool instantMix;
/// Server can transcode audio to a capped bitrate. Plex:
/// `/music/:/transcode/universal`; Jellyfin: `PlaybackInfo` with an audio
/// `TranscodingProfile`. Gates the music quality picker (vs original-only).
final bool audioTranscoding;
const ServerCapabilities({
this.serverSidePlayQueue = false,
this.serverSidePlaylists = false,
this.liveTv = false,
this.liveTvDvr = false,
this.subtitleSearch = false,
this.videoTranscoding = true,
this.serverSideSync = false,
this.richHubs = false,
this.numericUserRating = false,
this.userFavorites = false,
this.continueWatchingRemoval = false,
this.externalSubtitleSearch = false,
this.trackPreferencePersistence = false,
this.endpointFailover = false,
this.offlineWatchQueue = false,
this.discordRpc = false,
this.richMetadataEdit = false,
this.alphaBar = AlphaBarMode.none,
this.scrubThumbnails = false,
this.folderGrouping = false,
this.lyrics = false,
this.instantMix = false,
this.audioTranscoding = false,
});
/// Defaults for a fully-featured Plex server.
static const ServerCapabilities plex = ServerCapabilities(
serverSidePlayQueue: true,
serverSidePlaylists: true,
liveTv: true,
liveTvDvr: true,
subtitleSearch: true,
videoTranscoding: true,
serverSideSync: true,
richHubs: true,
numericUserRating: true,
userFavorites: false,
continueWatchingRemoval: true,
externalSubtitleSearch: true,
trackPreferencePersistence: true,
endpointFailover: true,
offlineWatchQueue: true,
discordRpc: true,
richMetadataEdit: true,
alphaBar: AlphaBarMode.scrollSnap,
scrubThumbnails: true,
folderGrouping: true,
lyrics: true,
instantMix: true,
audioTranscoding: true,
);
/// Defaults for a Jellyfin server.
///
/// `videoTranscoding` is `true` — `JellyfinClient.getPlaybackInitialization`
/// negotiates via `POST /Items/{id}/PlaybackInfo` and uses the server's
/// `TranscodingUrl` when a non-original quality preset is selected.
///
/// `liveTv` is `true` because Jellyfin exposes `/LiveTv/Channels` and
/// `/LiveTv/Programs`. Detection + channel listing are wired today;
/// EPG and tuning are follow-ups.
static const ServerCapabilities jellyfin = ServerCapabilities(
serverSidePlayQueue: false,
serverSidePlaylists: true,
liveTv: true,
liveTvDvr: false,
subtitleSearch: false,
videoTranscoding: true,
serverSideSync: false,
richHubs: false,
numericUserRating: false,
userFavorites: true,
externalSubtitleSearch: false,
trackPreferencePersistence: true,
endpointFailover: true,
offlineWatchQueue: false,
discordRpc: false,
richMetadataEdit: true,
alphaBar: AlphaBarMode.nameStartsWithFilter,
scrubThumbnails: true,
folderGrouping: true,
lyrics: true,
instantMix: true,
audioTranscoding: true,
);
/// Every flag here is fixed per backend *kind* except [videoTranscoding],
/// which Plex probes per server (`PlexClient.capabilities`) — so that is the
/// only override this type needs. Widen the parameter list if another flag
/// ever becomes a runtime probe.
ServerCapabilities copyWith({bool? videoTranscoding}) {
return ServerCapabilities(
serverSidePlayQueue: serverSidePlayQueue,
serverSidePlaylists: serverSidePlaylists,
liveTv: liveTv,
liveTvDvr: liveTvDvr,
subtitleSearch: subtitleSearch,
videoTranscoding: videoTranscoding ?? this.videoTranscoding,
serverSideSync: serverSideSync,
richHubs: richHubs,
numericUserRating: numericUserRating,
userFavorites: userFavorites,
continueWatchingRemoval: continueWatchingRemoval,
externalSubtitleSearch: externalSubtitleSearch,
trackPreferencePersistence: trackPreferencePersistence,
endpointFailover: endpointFailover,
offlineWatchQueue: offlineWatchQueue,
discordRpc: discordRpc,
richMetadataEdit: richMetadataEdit,
alphaBar: alphaBar,
scrubThumbnails: scrubThumbnails,
folderGrouping: folderGrouping,
lyrics: lyrics,
instantMix: instantMix,
audioTranscoding: audioTranscoding,
);
}
}