Files
plezy/lib/utils/provider_extensions.dart
T

137 lines
6.0 KiB
Dart
Raw Normal View History

2025-10-30 18:49:59 +01:00
import 'package:flutter/material.dart';
2026-06-01 11:06:03 +02:00
import '../media/ids.dart';
2025-10-30 18:49:59 +01:00
import 'package:provider/provider.dart';
2026-04-26 02:34:09 +02:00
import '../media/media_item.dart';
import '../media/media_library.dart';
import '../media/media_server_client.dart';
2025-12-04 08:36:07 +01:00
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';
2025-10-30 18:49:59 +01:00
extension ProviderExtensions on BuildContext {
2025-12-15 02:29:03 +01:00
UserProfileProvider get userProfile => Provider.of<UserProfileProvider>(this, listen: false);
2025-10-30 18:49:59 +01:00
2026-04-25 03:16:11 +02:00
/// Internal: resolve a [PlexClient] from a serverId or fall back to the
2026-04-26 02:34:09 +02:00
/// first online server. Returns null if neither yields a Plex client.
/// Non-Plex servers (Jellyfin) are skipped — these helpers exist for
2026-06-01 07:33:12 +02:00
/// Plex-only flows that have no neutral equivalent (DVR tuning, match).
/// Backend-agnostic flows use the [_resolveMediaClient]
2026-04-26 02:34:09 +02:00
/// helpers below.
2026-06-01 11:06:03 +02:00
PlexClient? _resolveClient(ServerId? serverId) {
2026-04-25 03:16:11 +02:00
final provider = Provider.of<MultiServerProvider>(this, listen: false);
2026-04-26 02:34:09 +02:00
return _resolvePrioritized(serverId, provider.onlineServerIds, provider.getPlexClientForServer);
2026-04-25 03:16:11 +02:00
}
2026-04-25 03:16:11 +02:00
/// 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.
2026-06-01 11:06:03 +02:00
PlexClient _requireClient(ServerId? serverId, {bool fallback = true}) {
2026-04-25 03:16:11 +02:00
final provider = Provider.of<MultiServerProvider>(this, listen: false);
if (serverId != null) {
2026-04-26 02:34:09 +02:00
final client = provider.getPlexClientForServer(serverId);
2026-04-25 03:16:11 +02:00
if (client != null) return client;
if (!fallback) {
2026-04-26 02:34:09 +02:00
appLogger.e('No Plex client found for server $serverId');
2026-04-25 03:16:11 +02:00
throw Exception(t.errors.noClientAvailable);
}
}
2026-04-26 02:34:09 +02:00
final client = _resolveClient(null);
2026-04-25 03:16:11 +02:00
if (client == null) {
throw Exception(t.errors.noClientAvailable);
}
2026-04-25 03:16:11 +02:00
return client;
}
2026-06-01 11:06:03 +02:00
PlexClient getPlexClientForServer(ServerId serverId) => _requireClient(serverId, fallback: false);
2026-04-25 03:16:11 +02:00
2026-06-01 11:06:03 +02:00
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);
}
2025-12-14 21:03:11 +01:00
2026-06-01 11:06:03 +02:00
PlexClient getPlexClientWithFallback(ServerId? serverId) => _requireClient(serverId);
2026-04-26 02:34:09 +02:00
// ── 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]
2026-06-01 07:33:12 +02:00
// when you specifically need a [PlexClient] (Plex-only flows like Live TV or
// match/fix-match).
2026-04-26 02:34:09 +02:00
2026-06-01 11:06:03 +02:00
MediaServerClient? _resolveMediaClient(ServerId? serverId) {
2026-04-26 02:34:09 +02:00
final provider = Provider.of<MultiServerProvider>(this, listen: false);
return _resolvePrioritized(serverId, provider.onlineServerIds, provider.getClientForServer);
}
2026-06-01 11:06:03 +02:00
MediaServerClient? tryGetMediaClientForServer(ServerId? serverId) {
2026-04-26 02:34:09 +02:00
if (serverId == null) return null;
final provider = Provider.of<MultiServerProvider?>(this, listen: false);
return provider?.getClientForServer(serverId);
2026-04-26 02:34:09 +02:00
}
/// 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.
2026-06-01 11:06:03 +02:00
MediaServerClient getMediaClientForServer(ServerId serverId) {
final provider = Provider.of<MultiServerProvider>(this, listen: false);
final c = provider.getClientForServer(serverId);
2026-04-26 02:34:09 +02:00
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);
2026-04-26 02:34:09 +02:00
}
2025-12-14 21:03:11 +01:00
2026-04-26 02:34:09 +02:00
/// Get a [MediaServerClient] for a [MediaItem], or null in offline mode /
/// when the server isn't online.
MediaServerClient? getMediaClientForItemOrNull(MediaItem item, {bool isOffline = false}) {
2026-04-25 03:16:11 +02:00
if (isOffline) return null;
2026-06-01 11:06:03 +02:00
return tryGetMediaClientForServer(serverIdOrNull(item.serverId));
2025-12-14 21:03:11 +01:00
}
2026-04-26 02:34:09 +02:00
/// Get a [MediaServerClient] for [serverId], falling back to the first
/// online server when not found. Throws if no client is available.
2026-06-01 11:06:03 +02:00
MediaServerClient getMediaClientWithFallback(ServerId? serverId) {
2026-04-26 02:34:09 +02:00
final c = _resolveMediaClient(serverId);
if (c == null) throw Exception(t.errors.noClientAvailable);
return c;
}
2025-12-14 21:03:11 +01:00
2026-04-26 02:34:09 +02:00
/// 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);
}
2026-04-26 02:34:09 +02:00
}
2026-04-26 02:34:09 +02:00
/// 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.
2026-06-01 11:06:03 +02:00
T? _resolvePrioritized<T>(String? preferred, Iterable<String> fallbacks, T? Function(ServerId) resolve) {
2026-04-26 02:34:09 +02:00
if (preferred != null) {
2026-06-01 11:06:03 +02:00
final c = resolve(ServerId(preferred));
2026-04-26 02:34:09 +02:00
if (c != null) return c;
}
for (final id in fallbacks) {
2026-06-01 11:06:03 +02:00
final c = resolve(ServerId(id));
2026-04-26 02:34:09 +02:00
if (c != null) return c;
}
return null;
2025-10-30 18:49:59 +01:00
}