diff --git a/lib/data/repositories/multi_server_repository.dart b/lib/data/repositories/multi_server_repository.dart index 1e1456455..0aca92660 100644 --- a/lib/data/repositories/multi_server_repository.dart +++ b/lib/data/repositories/multi_server_repository.dart @@ -165,23 +165,10 @@ class MultiServerRepository { final results = await Future.wait( sessions.map( (session) => _withTimeout(() async { - final viewsFuture = session.client.userViewsApi.getUserViews(); - final configFuture = session.client.usersApi - .getUserConfiguration() - .then>((config) => config.myMediaExcludes.toSet()) - .catchError((_) => const {}); - - final response = await viewsFuture; - final Set excludes = await configFuture; + final response = await loadVisibleUserViews(session.client); final items = response['Items'] as List? ?? []; - final filteredItems = items.where((item) { - final data = item as Map; - final id = data['Id']?.toString() ?? ''; - return !excludes.contains(id); - }); - - return filteredItems.map((item) { + return items.map((item) { final data = item as Map; final name = data['Name'] as String? ?? ''; return AggregatedLibrary( diff --git a/lib/data/repositories/user_views_repository.dart b/lib/data/repositories/user_views_repository.dart index 4781fadf3..50795a703 100644 --- a/lib/data/repositories/user_views_repository.dart +++ b/lib/data/repositories/user_views_repository.dart @@ -4,8 +4,8 @@ import 'package:server_core/server_core.dart'; import '../models/aggregated_library.dart'; /// The user's own views. Anything hidden from My Media is left out unless -/// [includeHidden] is set, which the rows that should stay independent of that -/// toggle use to see every library the user can reach. +/// [includeHidden] is set, which callers that apply their own exclude +/// filtering pass so the list isn't filtered twice. Future> loadUserViews( MediaServerClient client, { bool includeHidden = false, @@ -33,6 +33,39 @@ Future> loadUserViews( }).toList(); } +/// The My Media exclude list, or null when it can't be read. +Future?> _excludesFrom(Future config) async { + try { + return (await config).myMediaExcludes.toSet(); + } catch (_) { + return null; + } +} + +/// The views response with anything hidden from My Media removed. The server +/// filters by the exclude list itself, so ask for the full list and apply it +/// here instead. If the list can't be read, let the server filter so hidden +/// libraries don't leak through. +Future> loadVisibleUserViews( + MediaServerClient client, +) async { + final viewsFuture = client.userViewsApi.getUserViews(includeHidden: true); + final excludes = await _excludesFrom(client.usersApi.getUserConfiguration()); + final response = await viewsFuture; + + if (excludes == null) return client.userViewsApi.getUserViews(); + if (excludes.isEmpty) return response; + + final items = (response['Items'] as List? ?? []) + .where( + (item) => !excludes.contains( + (item as Map)['Id']?.toString() ?? '', + ), + ) + .toList(); + return {...response, 'Items': items}; +} + class UserViewsRepository extends ChangeNotifier { final MediaServerClient _client; UserConfiguration? _cachedConfig; @@ -56,15 +89,12 @@ class UserViewsRepository extends ChangeNotifier { ).whenComplete(() => _inFlightViewsIncludingHidden = null); Future> getUserViews() async { - final views = await getAllViews(); - try { - final config = await _getUserConfig(); - final excludes = config.myMediaExcludes.toSet(); - if (excludes.isEmpty) return views; - return views.where((v) => !excludes.contains(v.id)).toList(); - } catch (_) { - return views; - } + final excludes = await _excludesFrom(_getUserConfig()); + if (excludes == null) return getAllViews(); + + final views = await getAllViewsIncludingHidden(); + if (excludes.isEmpty) return views; + return views.where((v) => !excludes.contains(v.id)).toList(); } Future _getUserConfig() async { diff --git a/lib/data/services/row_data_source.dart b/lib/data/services/row_data_source.dart index 35516c640..bc6595cbc 100644 --- a/lib/data/services/row_data_source.dart +++ b/lib/data/services/row_data_source.dart @@ -19,6 +19,7 @@ import '../utils/next_up_enrichment.dart'; import '../utils/playlist_utils.dart'; import 'package:flutter/foundation.dart'; import '../repositories/seerr_repository.dart'; +import '../repositories/user_views_repository.dart'; import '../../preference/seerr_preferences.dart'; import '../viewmodels/seerr_discover_view_model.dart'; import 'custom_external_lists_service.dart'; @@ -679,28 +680,14 @@ class RowDataSource { String serverId, [ HomeRowType rowType = HomeRowType.libraryTiles, ]) async { - final viewsFuture = _client.userViewsApi.getUserViews(); - final configFuture = _client.usersApi - .getUserConfiguration() - .then>((config) => config.myMediaExcludes.toSet()) - .catchError((_) => const {}); - - final response = await viewsFuture; - final Set excludes = await configFuture; - final items = response['Items'] as List? ?? []; - - final filteredItems = items.where((item) { - final data = item as Map; - final id = data['Id']?.toString() ?? ''; - return !excludes.contains(id); - }).toList(); + final response = await loadVisibleUserViews(_client); return _buildRow( id: rowType == HomeRowType.libraryTilesSmall ? 'libraryTilesSmall' : 'libraryTiles', title: _l10n.myMedia, - response: {...response, 'Items': filteredItems}, + response: response, serverId: serverId, rowType: rowType, );