Jellyfin never consults IsAdministrator when authorizing a library delete: BaseItem.IsAuthorizedToDelete looks at EnableContentDeletion and the per-library grant, and only the first user a server creates gets the former for free. Gating the "Delete from server" entry on the admin bit therefore offered a destructive action that answers 401 to later administrators, and hid it from plain users who do hold the grant. Ask the server per item instead, through the new MediaDeletionPermissionClient capability: BaseItemDto.CanDelete already folds the global grant, the per-library grant, and item state such as missing files or an in-progress recording. The probe runs when a menu opens on a deletable kind, costs ~0.5 KB, carries a whole-request deadline because the client's own budget covers connect and receive separately, and fails closed on anything unknown. Plex keeps its account-level owner/admin gate; it has no per-item permission on the wire. close #1749
68 lines
3.4 KiB
Dart
68 lines
3.4 KiB
Dart
/// Centralized HTTP timeout constants for both backends. The same
|
|
/// [MediaServerHttpClient] wrapper is used by Plex and Jellyfin clients —
|
|
/// timeouts are kept here so the budgets per phase are visible at a
|
|
/// glance.
|
|
class MediaServerTimeouts {
|
|
static const connect = Duration(seconds: 10);
|
|
|
|
static const receive = Duration(seconds: 120);
|
|
|
|
/// Retry budget for home `/hubs` startup calls. These endpoints can be slow
|
|
/// while Plex wakes idle disks, but should not block forever.
|
|
static const homeHubAttemptTimeouts = [Duration(seconds: 10), Duration(seconds: 5), Duration(milliseconds: 2500)];
|
|
|
|
/// Retry budget for per-library home hub rows (`/hubs/sections/{id}`). These
|
|
/// can be slower than the top-level home hub call on remote Plex servers.
|
|
static const libraryHubAttemptTimeouts = [Duration(seconds: 10), Duration(seconds: 8), Duration(seconds: 5)];
|
|
|
|
/// Timeout for probing a cached/preferred endpoint (used in
|
|
/// [PlexServer.findBestWorkingConnection]).
|
|
static const preferredEndpointProbe = Duration(milliseconds: 1500);
|
|
|
|
/// How long the cached/preferred endpoint probe gets to answer before the
|
|
/// full candidate race starts alongside it. A healthy cached endpoint
|
|
/// answers well inside this window and wins deterministically; a stale one
|
|
/// (e.g. a cached LAN address probed from outside the LAN) only delays
|
|
/// discovery by this much instead of the full [preferredEndpointProbe].
|
|
static const preferredEndpointHeadStart = Duration(milliseconds: 300);
|
|
|
|
/// Timeout for the connection race where all candidates are tested in
|
|
/// parallel (used in [PlexServer.findBestWorkingConnection]).
|
|
static const connectionRace = Duration(seconds: 2);
|
|
|
|
/// Per-server connection watchdog ceiling. The discovery path is no longer
|
|
/// strictly serial (cached probe overlaps the race; the HTTPS upgrade runs
|
|
/// off the critical path), so this is a generous upper bound rather than a
|
|
/// sum of phases.
|
|
static const perServerConnect = Duration(milliseconds: 6500);
|
|
|
|
/// HTTP timeout for the live-TV tune POST. Matches Plex web's value — the
|
|
/// default 10s connect budget is too tight on Fire-TV cold starts.
|
|
static const tune = Duration(seconds: 30);
|
|
|
|
static const plexTvConnect = Duration(seconds: 15);
|
|
|
|
static const plexTvReceive = Duration(seconds: 10);
|
|
|
|
/// Authenticated health probe timeout. Health sweeps await every server, so
|
|
/// a stale Plex endpoint must not hold the whole sweep for [receive].
|
|
static const plexProbe = Duration(seconds: 8);
|
|
|
|
/// Probe + token-validate timeout — Jellyfin servers respond fast on
|
|
/// `/System/Info/Public` and `/Users/Me`.
|
|
static const jellyfinProbe = Duration(seconds: 8);
|
|
|
|
/// Per-item delete-permission probe. Shorter than [jellyfinProbe] because it
|
|
/// blocks a context menu from opening: a server that is nominally online but
|
|
/// hung must not hold the menu for a health-sweep budget. Unlike the other
|
|
/// values here it is also applied as a whole-request deadline by the caller
|
|
/// (the per-request budget covers the connect and receive phases
|
|
/// individually), and expiry fails closed — no delete entry — so the ceiling
|
|
/// only ever costs an entry, never safety.
|
|
static const jellyfinDeletePermission = Duration(seconds: 3);
|
|
|
|
/// Best-effort `/Sessions/Logout` timeout — short because the call is
|
|
/// fire-and-forget; the token is removed locally regardless.
|
|
static const jellyfinSignOut = Duration(seconds: 5);
|
|
}
|