MemoryLogOutput extended LogOutput but was never wired as a Logger output (storage happens in the printer); DeletionNotifier/WatchStateNotifier's forServer/forItem filtered streams and WatchStateEvent.mediaType had no production consumers (tests now filter .stream directly); context.hiddenLibraries/profileSettings and toPlexUrl had zero call sites; MonoTokens.splashFactory was never read; OptimizedMediaImage's enableTranscoding/cacheKey chains, FocusableMediaCard.width/height/forceGridMode, TvBrowseRail's constant-zero gap functions, and media_image_helper's scaleFactor were never varied by any caller. The sha1 LRU stays (rebuild-hot artwork URLs); TvRailTrailing.none and the mono-theme copyWith stay live (lerp delegates to copyWith). Also carries the MusicPlayContext.id argument drops in the music screens and remaining sweep test updates.
137 lines
6.0 KiB
Dart
137 lines
6.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import '../media/ids.dart';
|
|
import 'package:provider/provider.dart';
|
|
import '../media/media_item.dart';
|
|
import '../media/media_library.dart';
|
|
import '../media/media_server_client.dart';
|
|
import '../services/plex_client.dart';
|
|
import '../i18n/strings.g.dart';
|
|
import '../providers/multi_server_provider.dart';
|
|
import '../providers/user_profile_provider.dart';
|
|
import 'app_logger.dart';
|
|
|
|
extension ProviderExtensions on BuildContext {
|
|
UserProfileProvider get userProfile => Provider.of<UserProfileProvider>(this, listen: false);
|
|
|
|
/// Internal: resolve a [PlexClient] from a serverId or fall back to the
|
|
/// first online server. Returns null if neither yields a Plex client.
|
|
/// Non-Plex servers (Jellyfin) are skipped — these helpers exist for
|
|
/// Plex-only flows that have no neutral equivalent (DVR tuning, match).
|
|
/// Backend-agnostic flows use the [_resolveMediaClient]
|
|
/// helpers below.
|
|
PlexClient? _resolveClient(ServerId? serverId) {
|
|
final provider = Provider.of<MultiServerProvider>(this, listen: false);
|
|
return _resolvePrioritized(serverId, provider.onlineServerIds, provider.getPlexClientForServer);
|
|
}
|
|
|
|
/// Internal: like [_resolveClient] but throws a localized exception when
|
|
/// no client is available. The thrown message is the canonical
|
|
/// `t.errors.noClientAvailable` so callers can surface it directly.
|
|
PlexClient _requireClient(ServerId? serverId, {bool fallback = true}) {
|
|
final provider = Provider.of<MultiServerProvider>(this, listen: false);
|
|
if (serverId != null) {
|
|
final client = provider.getPlexClientForServer(serverId);
|
|
if (client != null) return client;
|
|
if (!fallback) {
|
|
appLogger.e('No Plex client found for server $serverId');
|
|
throw Exception(t.errors.noClientAvailable);
|
|
}
|
|
}
|
|
final client = _resolveClient(null);
|
|
if (client == null) {
|
|
throw Exception(t.errors.noClientAvailable);
|
|
}
|
|
return client;
|
|
}
|
|
|
|
PlexClient getPlexClientForServer(ServerId serverId) => _requireClient(serverId, fallback: false);
|
|
|
|
PlexClient? tryGetPlexClientForServer(ServerId? serverId) {
|
|
if (serverId == null) return null;
|
|
final provider = Provider.of<MultiServerProvider?>(this, listen: false);
|
|
return provider?.getPlexClientForServer(serverId);
|
|
}
|
|
|
|
PlexClient getPlexClientForLibrary(MediaLibrary library) {
|
|
final serverId = serverIdOrNull(library.serverId);
|
|
if (serverId == null) throw Exception(t.errors.noClientAvailable);
|
|
return getPlexClientForServer(serverId);
|
|
}
|
|
|
|
PlexClient getPlexClientWithFallback(ServerId? serverId) => _requireClient(serverId);
|
|
|
|
// ── Backend-neutral helpers ──────────────────────────────────────
|
|
// These return [MediaServerClient] regardless of backend kind so callers
|
|
// that consume only the [MediaServerClient] surface don't need to type-
|
|
// check the result. Use [getPlexClientForServer] / [getPlexClientForLibrary]
|
|
// when you specifically need a [PlexClient] (Plex-only flows like Live TV or
|
|
// match/fix-match).
|
|
|
|
MediaServerClient? _resolveMediaClient(ServerId? serverId) {
|
|
final provider = Provider.of<MultiServerProvider>(this, listen: false);
|
|
return _resolvePrioritized(serverId, provider.onlineServerIds, provider.getClientForServer);
|
|
}
|
|
|
|
MediaServerClient? tryGetMediaClientForServer(ServerId? serverId) {
|
|
if (serverId == null) return null;
|
|
final provider = Provider.of<MultiServerProvider?>(this, listen: false);
|
|
return provider?.getClientForServer(serverId);
|
|
}
|
|
|
|
/// Get a [MediaServerClient] for the given serverId. Throws when the
|
|
/// server isn't registered or is offline. Mirrors the throwing variant of
|
|
/// the Plex-typed [getPlexClientForServer] helpers.
|
|
MediaServerClient getMediaClientForServer(ServerId serverId) {
|
|
final provider = Provider.of<MultiServerProvider>(this, listen: false);
|
|
final c = provider.getClientForServer(serverId);
|
|
if (c == null) throw Exception(t.errors.noClientAvailable);
|
|
return c;
|
|
}
|
|
|
|
MediaServerClient getMediaClientForLibrary(MediaLibrary library) {
|
|
final serverId = serverIdOrNull(library.serverId);
|
|
if (serverId == null) throw Exception(t.errors.noClientAvailable);
|
|
return getMediaClientForServer(serverId);
|
|
}
|
|
|
|
/// Get a [MediaServerClient] for a [MediaItem], or null in offline mode /
|
|
/// when the server isn't online.
|
|
MediaServerClient? getMediaClientForItemOrNull(MediaItem item, {bool isOffline = false}) {
|
|
if (isOffline) return null;
|
|
return tryGetMediaClientForServer(serverIdOrNull(item.serverId));
|
|
}
|
|
|
|
/// Get a [MediaServerClient] for [serverId], falling back to the first
|
|
/// online server when not found. Throws if no client is available.
|
|
MediaServerClient getMediaClientWithFallback(ServerId? serverId) {
|
|
final c = _resolveMediaClient(serverId);
|
|
if (c == null) throw Exception(t.errors.noClientAvailable);
|
|
return c;
|
|
}
|
|
|
|
/// Like [getMediaClientWithFallback] but returns null instead of throwing
|
|
/// when no client is registered. Use this for non-critical surfaces (image
|
|
/// loaders, list cards) that can render a fallback when the client isn't
|
|
/// available — throwing during `build` would crash the widget instead.
|
|
MediaServerClient? tryGetMediaClientWithFallback(ServerId? serverId) {
|
|
final provider = Provider.of<MultiServerProvider?>(this, listen: false);
|
|
if (provider == null) return null;
|
|
return _resolvePrioritized(serverId, provider.onlineServerIds, provider.getClientForServer);
|
|
}
|
|
}
|
|
|
|
/// Try [preferred] first, then fall back through [fallbacks] in order. Returns
|
|
/// the first non-null result from [resolve], or `null` if every candidate
|
|
/// resolves to null.
|
|
T? _resolvePrioritized<T>(String? preferred, Iterable<String> fallbacks, T? Function(ServerId) resolve) {
|
|
if (preferred != null) {
|
|
final c = resolve(ServerId(preferred));
|
|
if (c != null) return c;
|
|
}
|
|
for (final id in fallbacks) {
|
|
final c = resolve(ServerId(id));
|
|
if (c != null) return c;
|
|
}
|
|
return null;
|
|
}
|