From 915397dee4e9b0c4164ca047e4ea12296cce81ab Mon Sep 17 00:00:00 2001 From: mattsigal Date: Sat, 25 Jul 2026 20:12:14 -0700 Subject: [PATCH 1/2] =?UTF-8?q?=EF=BB=BFFix=20Playlist=20library=20visibil?= =?UTF-8?q?ity=20in=20My=20Media=20navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/repositories/multi_server_repository.dart | 3 ++- lib/data/repositories/user_views_repository.dart | 2 +- lib/data/services/row_data_source.dart | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/data/repositories/multi_server_repository.dart b/lib/data/repositories/multi_server_repository.dart index 1e1456455..ccc95d187 100644 --- a/lib/data/repositories/multi_server_repository.dart +++ b/lib/data/repositories/multi_server_repository.dart @@ -165,7 +165,8 @@ class MultiServerRepository { final results = await Future.wait( sessions.map( (session) => _withTimeout(() async { - final viewsFuture = session.client.userViewsApi.getUserViews(); + final viewsFuture = + session.client.userViewsApi.getUserViews(includeHidden: true); final configFuture = session.client.usersApi .getUserConfiguration() .then>((config) => config.myMediaExcludes.toSet()) diff --git a/lib/data/repositories/user_views_repository.dart b/lib/data/repositories/user_views_repository.dart index 4781fadf3..7dfa62009 100644 --- a/lib/data/repositories/user_views_repository.dart +++ b/lib/data/repositories/user_views_repository.dart @@ -56,7 +56,7 @@ class UserViewsRepository extends ChangeNotifier { ).whenComplete(() => _inFlightViewsIncludingHidden = null); Future> getUserViews() async { - final views = await getAllViews(); + final views = await getAllViewsIncludingHidden(); try { final config = await _getUserConfig(); final excludes = config.myMediaExcludes.toSet(); diff --git a/lib/data/services/row_data_source.dart b/lib/data/services/row_data_source.dart index 35516c640..7f4622b28 100644 --- a/lib/data/services/row_data_source.dart +++ b/lib/data/services/row_data_source.dart @@ -679,7 +679,7 @@ class RowDataSource { String serverId, [ HomeRowType rowType = HomeRowType.libraryTiles, ]) async { - final viewsFuture = _client.userViewsApi.getUserViews(); + final viewsFuture = _client.userViewsApi.getUserViews(includeHidden: true); final configFuture = _client.usersApi .getUserConfiguration() .then>((config) => config.myMediaExcludes.toSet()) From 778dc0a3a7170827d96408922901b79c8b480f07 Mon Sep 17 00:00:00 2001 From: RadicalMuffinMan <103554043+RadicalMuffinMan@users.noreply.github.com> Date: Wed, 29 Jul 2026 11:17:01 -0400 Subject: [PATCH 2/2] keep hidden libraries hidden when the exclude list fails --- .../repositories/multi_server_repository.dart | 18 +------ .../repositories/user_views_repository.dart | 50 +++++++++++++++---- lib/data/services/row_data_source.dart | 19 ++----- 3 files changed, 45 insertions(+), 42 deletions(-) diff --git a/lib/data/repositories/multi_server_repository.dart b/lib/data/repositories/multi_server_repository.dart index ccc95d187..0aca92660 100644 --- a/lib/data/repositories/multi_server_repository.dart +++ b/lib/data/repositories/multi_server_repository.dart @@ -165,24 +165,10 @@ class MultiServerRepository { final results = await Future.wait( sessions.map( (session) => _withTimeout(() async { - final viewsFuture = - session.client.userViewsApi.getUserViews(includeHidden: true); - 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 7dfa62009..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 excludes = await _excludesFrom(_getUserConfig()); + if (excludes == null) return getAllViews(); + final views = await getAllViewsIncludingHidden(); - 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; - } + 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 7f4622b28..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(includeHidden: true); - 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, );