feat: remove server switcher
This commit is contained in:
@@ -0,0 +1,364 @@
|
||||
import 'dart:async';
|
||||
|
||||
import '../models/plex_hub.dart';
|
||||
import '../models/plex_library.dart';
|
||||
import '../models/plex_metadata.dart';
|
||||
import '../utils/app_logger.dart';
|
||||
import 'multi_server_manager.dart';
|
||||
|
||||
/// Service for aggregating data from multiple Plex servers
|
||||
class DataAggregationService {
|
||||
final MultiServerManager _serverManager;
|
||||
|
||||
DataAggregationService(this._serverManager);
|
||||
|
||||
/// Fetch libraries from all online servers and tag them with server info
|
||||
Future<List<PlexLibrary>> getLibrariesFromAllServers() async {
|
||||
final clients = _serverManager.onlineClients;
|
||||
|
||||
if (clients.isEmpty) {
|
||||
appLogger.w('No online servers available for fetching libraries');
|
||||
return [];
|
||||
}
|
||||
|
||||
appLogger.d('Fetching libraries from ${clients.length} servers');
|
||||
|
||||
final allLibraries = <PlexLibrary>[];
|
||||
|
||||
// Fetch from all servers in parallel
|
||||
final libraryFutures = clients.entries.map((entry) async {
|
||||
final serverId = entry.key;
|
||||
final client = entry.value;
|
||||
final server = _serverManager.getServer(serverId);
|
||||
|
||||
try {
|
||||
final libraries = await client.getLibraries();
|
||||
|
||||
// Tag each library with server info
|
||||
return libraries.map((lib) {
|
||||
return PlexLibrary(
|
||||
key: lib.key,
|
||||
title: lib.title,
|
||||
type: lib.type,
|
||||
agent: lib.agent,
|
||||
scanner: lib.scanner,
|
||||
language: lib.language,
|
||||
uuid: lib.uuid,
|
||||
updatedAt: lib.updatedAt,
|
||||
createdAt: lib.createdAt,
|
||||
hidden: lib.hidden,
|
||||
serverId: serverId,
|
||||
serverName: server?.name,
|
||||
);
|
||||
}).toList();
|
||||
} catch (e, stackTrace) {
|
||||
appLogger.e(
|
||||
'Failed to fetch libraries from server $serverId',
|
||||
error: e,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
// Mark server as offline
|
||||
_serverManager.updateServerStatus(serverId, false);
|
||||
return <PlexLibrary>[];
|
||||
}
|
||||
});
|
||||
|
||||
final results = await Future.wait(libraryFutures);
|
||||
|
||||
// Flatten results
|
||||
for (final libraries in results) {
|
||||
allLibraries.addAll(libraries);
|
||||
}
|
||||
|
||||
appLogger.i(
|
||||
'Fetched ${allLibraries.length} total libraries from all servers',
|
||||
);
|
||||
|
||||
return allLibraries;
|
||||
}
|
||||
|
||||
/// Fetch "On Deck" (Continue Watching) from all servers and merge by recency
|
||||
Future<List<PlexMetadata>> getOnDeckFromAllServers({int? limit}) async {
|
||||
final clients = _serverManager.onlineClients;
|
||||
|
||||
if (clients.isEmpty) {
|
||||
appLogger.w('No online servers available for fetching on deck');
|
||||
return [];
|
||||
}
|
||||
|
||||
appLogger.d('Fetching on deck from ${clients.length} servers');
|
||||
|
||||
final allOnDeck = <PlexMetadata>[];
|
||||
|
||||
// Fetch from all servers in parallel
|
||||
final onDeckFutures = clients.entries.map((entry) async {
|
||||
final serverId = entry.key;
|
||||
final client = entry.value;
|
||||
final server = _serverManager.getServer(serverId);
|
||||
|
||||
try {
|
||||
final items = await client.getOnDeck();
|
||||
|
||||
// Tag each item with server info
|
||||
return items.map((item) {
|
||||
return item.copyWith(serverId: serverId, serverName: server?.name);
|
||||
}).toList();
|
||||
} catch (e, stackTrace) {
|
||||
appLogger.e(
|
||||
'Failed to fetch on deck from server $serverId',
|
||||
error: e,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
_serverManager.updateServerStatus(serverId, false);
|
||||
return <PlexMetadata>[];
|
||||
}
|
||||
});
|
||||
|
||||
final results = await Future.wait(onDeckFutures);
|
||||
|
||||
// Flatten results
|
||||
for (final items in results) {
|
||||
allOnDeck.addAll(items);
|
||||
}
|
||||
|
||||
// Sort by most recent (lastViewedAt is stored in viewOffset metadata)
|
||||
// For on deck items, we use updatedAt or addedAt as proxy for recency
|
||||
allOnDeck.sort((a, b) {
|
||||
final aTime = a.updatedAt ?? a.addedAt ?? 0;
|
||||
final bTime = b.updatedAt ?? b.addedAt ?? 0;
|
||||
return bTime.compareTo(aTime); // Descending (most recent first)
|
||||
});
|
||||
|
||||
// Apply limit if specified
|
||||
final result = limit != null && limit < allOnDeck.length
|
||||
? allOnDeck.sublist(0, limit)
|
||||
: allOnDeck;
|
||||
|
||||
appLogger.i('Fetched ${result.length} on deck items from all servers');
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Fetch recommendation hubs from all servers
|
||||
Future<List<PlexHub>> getHubsFromAllServers({int? limit}) async {
|
||||
final clients = _serverManager.onlineClients;
|
||||
|
||||
if (clients.isEmpty) {
|
||||
appLogger.w('No online servers available for fetching hubs');
|
||||
return [];
|
||||
}
|
||||
|
||||
appLogger.d('Fetching hubs from ${clients.length} servers');
|
||||
|
||||
final allHubs = <PlexHub>[];
|
||||
|
||||
// Fetch from all servers in parallel
|
||||
final hubFutures = clients.entries.map((entry) async {
|
||||
final serverId = entry.key;
|
||||
final client = entry.value;
|
||||
final server = _serverManager.getServer(serverId);
|
||||
|
||||
try {
|
||||
// Get libraries for this server
|
||||
final libraries = await client.getLibraries();
|
||||
|
||||
// Filter to only visible movie/show libraries
|
||||
final visibleLibraries = libraries.where((library) {
|
||||
if (library.type != 'movie' && library.type != 'show') {
|
||||
return false;
|
||||
}
|
||||
if (library.hidden != null && library.hidden != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}).toList();
|
||||
|
||||
// Fetch hubs from all libraries in parallel
|
||||
final libraryHubFutures = visibleLibraries.map((library) async {
|
||||
try {
|
||||
final libraryHubs = await client.getLibraryHubs(library.key);
|
||||
|
||||
// Tag hubs and their items with server info
|
||||
return libraryHubs.map((hub) {
|
||||
final taggedItems = hub.items.map((item) {
|
||||
return item.copyWith(
|
||||
serverId: serverId,
|
||||
serverName: server?.name,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
return PlexHub(
|
||||
hubKey: hub.hubKey,
|
||||
title: hub.title,
|
||||
type: hub.type,
|
||||
hubIdentifier: hub.hubIdentifier,
|
||||
size: hub.size,
|
||||
more: hub.more,
|
||||
items: taggedItems,
|
||||
serverId: serverId,
|
||||
serverName: server?.name,
|
||||
);
|
||||
}).toList();
|
||||
} catch (e) {
|
||||
appLogger.w(
|
||||
'Failed to fetch hubs for library ${library.title}: $e',
|
||||
);
|
||||
return <PlexHub>[];
|
||||
}
|
||||
});
|
||||
|
||||
final libraryHubResults = await Future.wait(libraryHubFutures);
|
||||
|
||||
// Flatten all library hubs
|
||||
final serverHubs = <PlexHub>[];
|
||||
for (final hubs in libraryHubResults) {
|
||||
serverHubs.addAll(hubs);
|
||||
}
|
||||
|
||||
return serverHubs;
|
||||
} catch (e, stackTrace) {
|
||||
appLogger.e(
|
||||
'Failed to fetch hubs from server $serverId',
|
||||
error: e,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
_serverManager.updateServerStatus(serverId, false);
|
||||
return <PlexHub>[];
|
||||
}
|
||||
});
|
||||
|
||||
final results = await Future.wait(hubFutures);
|
||||
|
||||
// Flatten results
|
||||
for (final hubs in results) {
|
||||
allHubs.addAll(hubs);
|
||||
}
|
||||
|
||||
// Apply limit if specified
|
||||
final result = limit != null && limit < allHubs.length
|
||||
? allHubs.sublist(0, limit)
|
||||
: allHubs;
|
||||
|
||||
appLogger.i('Fetched ${result.length} hubs from all servers');
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Search across all online servers
|
||||
Future<List<PlexMetadata>> searchAcrossServers(
|
||||
String query, {
|
||||
int? limit,
|
||||
}) async {
|
||||
final clients = _serverManager.onlineClients;
|
||||
|
||||
if (clients.isEmpty) {
|
||||
appLogger.w('No online servers available for search');
|
||||
return [];
|
||||
}
|
||||
|
||||
if (query.trim().isEmpty) {
|
||||
return [];
|
||||
}
|
||||
|
||||
appLogger.d('Searching for "$query" across ${clients.length} servers');
|
||||
|
||||
final allResults = <PlexMetadata>[];
|
||||
|
||||
// Search all servers in parallel
|
||||
final searchFutures = clients.entries.map((entry) async {
|
||||
final serverId = entry.key;
|
||||
final client = entry.value;
|
||||
final server = _serverManager.getServer(serverId);
|
||||
|
||||
try {
|
||||
final results = await client.search(query);
|
||||
|
||||
// Tag each result with server info
|
||||
return results.map((item) {
|
||||
return item.copyWith(serverId: serverId, serverName: server?.name);
|
||||
}).toList();
|
||||
} catch (e, stackTrace) {
|
||||
appLogger.e(
|
||||
'Failed to search on server $serverId',
|
||||
error: e,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
_serverManager.updateServerStatus(serverId, false);
|
||||
return <PlexMetadata>[];
|
||||
}
|
||||
});
|
||||
|
||||
final results = await Future.wait(searchFutures);
|
||||
|
||||
// Flatten results
|
||||
for (final items in results) {
|
||||
allResults.addAll(items);
|
||||
}
|
||||
|
||||
// Apply limit if specified
|
||||
final result = limit != null && limit < allResults.length
|
||||
? allResults.sublist(0, limit)
|
||||
: allResults;
|
||||
|
||||
appLogger.i('Found ${result.length} search results across all servers');
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Get libraries for a specific server
|
||||
Future<List<PlexLibrary>> getLibrariesForServer(String serverId) async {
|
||||
final client = _serverManager.getClient(serverId);
|
||||
final server = _serverManager.getServer(serverId);
|
||||
|
||||
if (client == null) {
|
||||
appLogger.w('No client found for server $serverId');
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
final libraries = await client.getLibraries();
|
||||
|
||||
// Tag with server info
|
||||
return libraries.map((lib) {
|
||||
return PlexLibrary(
|
||||
key: lib.key,
|
||||
title: lib.title,
|
||||
type: lib.type,
|
||||
agent: lib.agent,
|
||||
scanner: lib.scanner,
|
||||
language: lib.language,
|
||||
uuid: lib.uuid,
|
||||
updatedAt: lib.updatedAt,
|
||||
createdAt: lib.createdAt,
|
||||
hidden: lib.hidden,
|
||||
serverId: serverId,
|
||||
serverName: server?.name,
|
||||
);
|
||||
}).toList();
|
||||
} catch (e, stackTrace) {
|
||||
appLogger.e(
|
||||
'Failed to fetch libraries for server $serverId',
|
||||
error: e,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
_serverManager.updateServerStatus(serverId, false);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/// Group libraries by server
|
||||
Map<String, List<PlexLibrary>> groupLibrariesByServer(
|
||||
List<PlexLibrary> libraries,
|
||||
) {
|
||||
final grouped = <String, List<PlexLibrary>>{};
|
||||
|
||||
for (final library in libraries) {
|
||||
final serverId = library.serverId;
|
||||
if (serverId != null) {
|
||||
grouped.putIfAbsent(serverId, () => []).add(library);
|
||||
}
|
||||
}
|
||||
|
||||
return grouped;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user