From 51a8b971f6bc9506da3741d2e80d65730e5d8dd3 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Fri, 8 May 2026 10:33:49 +0200 Subject: [PATCH] fix(jellyfin): keep poster fallback after recycling --- lib/widgets/media_card.dart | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/lib/widgets/media_card.dart b/lib/widgets/media_card.dart index 29430740..03b8361d 100644 --- a/lib/widgets/media_card.dart +++ b/lib/widgets/media_card.dart @@ -27,6 +27,20 @@ import 'media_progress_bar.dart'; import 'media_card_list_layout.dart'; import 'optimized_media_image.dart'; +const _failedPosterUrlCacheLimit = 512; +final _failedPosterUrls = {}; + +bool _hasFailedPosterUrl(String? url) => url != null && _failedPosterUrls.contains(url); + +void _rememberFailedPosterUrl(String? url) { + if (url == null || url.isEmpty) return; + _failedPosterUrls.remove(url); + _failedPosterUrls.add(url); + if (_failedPosterUrls.length > _failedPosterUrlCacheLimit) { + _failedPosterUrls.remove(_failedPosterUrls.first); + } +} + class MediaCard extends StatefulWidget { /// Either a [MediaItem] or a [MediaPlaylist]. Typed as [Object] because Dart /// has no nominal union type — runtime `is` checks select the variant. @@ -644,8 +658,10 @@ Widget _buildPosterImage( final hideSpoilers = SettingsService.instanceOrNull!.read(SettingsService.hideSpoilers); final shouldBlur = hideSpoilers && item.shouldHideSpoiler && episodePosterMode == EpisodePosterMode.episodeThumbnail; - posterUrl = item.posterThumb(mode: episodePosterMode, mixedHubContext: mixedHubContext); + final primaryPosterUrl = item.posterThumb(mode: episodePosterMode, mixedHubContext: mixedHubContext); final posterFallbackUrl = item.posterThumbFallback(mode: episodePosterMode, mixedHubContext: mixedHubContext); + final useRememberedFallback = posterFallbackUrl != null && _hasFailedPosterUrl(primaryPosterUrl); + posterUrl = useRememberedFallback ? posterFallbackUrl : primaryPosterUrl; final mediaClient = isOffline ? null : context.tryGetMediaClientWithFallback(item.serverId); Widget image; @@ -667,15 +683,18 @@ Widget _buildPosterImage( width: knownWidth ?? double.infinity, height: knownHeight ?? double.infinity, fit: BoxFit.cover, - errorWidget: posterFallbackUrl == null + errorWidget: posterFallbackUrl == null || useRememberedFallback ? null - : (_, _, _) => OptimizedMediaImage.poster( - client: mediaClient, - imagePath: posterFallbackUrl, - width: knownWidth ?? double.infinity, - height: knownHeight ?? double.infinity, - fit: BoxFit.cover, - ), + : (_, _, _) { + _rememberFailedPosterUrl(primaryPosterUrl); + return OptimizedMediaImage.poster( + client: mediaClient, + imagePath: posterFallbackUrl, + width: knownWidth ?? double.infinity, + height: knownHeight ?? double.infinity, + fit: BoxFit.cover, + ); + }, localFilePath: localPosterPath, ); }