133 lines
5.7 KiB
Dart
133 lines
5.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import '../media/media_item.dart';
|
|
import '../media/media_library.dart';
|
|
import '../media/media_server_client.dart';
|
|
import '../media/media_server_user_profile.dart';
|
|
import '../services/plex_client.dart';
|
|
import '../i18n/strings.g.dart';
|
|
import '../providers/hidden_libraries_provider.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);
|
|
|
|
HiddenLibrariesProvider get hiddenLibraries => Provider.of<HiddenLibrariesProvider>(this, listen: false);
|
|
|
|
MediaServerUserProfile? get profileSettings => userProfile.profileSettings;
|
|
|
|
/// 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(String? 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(String? 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(String serverId) => _requireClient(serverId, fallback: false);
|
|
|
|
PlexClient? tryGetPlexClientForServer(String? serverId) {
|
|
if (serverId == null) return null;
|
|
final provider = Provider.of<MultiServerProvider>(this, listen: false);
|
|
return provider.getPlexClientForServer(serverId);
|
|
}
|
|
|
|
PlexClient getPlexClientForLibrary(MediaLibrary library) => _requireClient(library.serverId);
|
|
|
|
PlexClient getPlexClientWithFallback(String? 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(String? serverId) {
|
|
final provider = Provider.of<MultiServerProvider>(this, listen: false);
|
|
return _resolvePrioritized(serverId, provider.onlineServerIds, provider.getClientForServer);
|
|
}
|
|
|
|
MediaServerClient? tryGetMediaClientForServer(String? 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(String serverId) {
|
|
final c = tryGetMediaClientForServer(serverId);
|
|
if (c == null) throw Exception(t.errors.noClientAvailable);
|
|
return c;
|
|
}
|
|
|
|
MediaServerClient getMediaClientForLibrary(MediaLibrary library) {
|
|
final c = _resolveMediaClient(library.serverId);
|
|
if (c == null) throw Exception(t.errors.noClientAvailable);
|
|
return c;
|
|
}
|
|
|
|
/// 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(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(String? 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(String? serverId) => _resolveMediaClient(serverId);
|
|
}
|
|
|
|
/// 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(String) resolve) {
|
|
if (preferred != null) {
|
|
final c = resolve(preferred);
|
|
if (c != null) return c;
|
|
}
|
|
for (final id in fallbacks) {
|
|
final c = resolve(id);
|
|
if (c != null) return c;
|
|
}
|
|
return null;
|
|
}
|