@@ -3051,6 +3051,9 @@ private async Task<int> UpdateMissingArtistImagesFromFoldersAsync(Guid folderId,
30513051 g => g . Key ,
30523052 g => ( IReadOnlyList < string ? > ) g . Select ( x => x . DirectoryPath ) . Distinct ( ) . ToList ( ) ) ;
30533053
3054+ // Cache processed directories to avoid redundant GetFiles calls when multiple artists share a directory.
3055+ var directoryCache = new Dictionary < string , string ? > ( StringComparer . OrdinalIgnoreCase ) ;
3056+
30543057 // Collect events to fire after SaveChangesAsync succeeds, so UI state and DB stay in sync.
30553058 var pendingEvents = new List < ArtistMetadataUpdatedEventArgs > ( ) ;
30563059 var updatedCount = 0 ;
@@ -3059,7 +3062,7 @@ private async Task<int> UpdateMissingArtistImagesFromFoldersAsync(Guid folderId,
30593062 cancellationToken . ThrowIfCancellationRequested ( ) ;
30603063
30613064 var dirs = songDirsByArtistId . TryGetValue ( artist . Id , out var d ) ? d : Array . Empty < string ? > ( ) ;
3062- var localImageBytes = await FindArtistImageInDirectoriesAsync ( dirs , artist . Name , cancellationToken ) . ConfigureAwait ( false ) ;
3065+ var localImageBytes = await FindArtistImageInDirectoriesAsync ( dirs , artist . Name , cancellationToken , directoryCache ) . ConfigureAwait ( false ) ;
30633066 if ( localImageBytes == null ) continue ;
30643067
30653068 try
@@ -3257,7 +3260,7 @@ private static int GetCoverArtPriority(string fileNameWithoutExt) =>
32573260 /// Root directories are excluded from pass A to avoid scanning drive roots.
32583261 /// </summary>
32593262 private async Task < byte [ ] ? > FindArtistImageInDirectoriesAsync (
3260- IReadOnlyList < string ? > songDirectories , string artistName , CancellationToken ct )
3263+ IReadOnlyList < string ? > songDirectories , string artistName , CancellationToken ct , Dictionary < string , string ? > ? directoryCache = null )
32613264 {
32623265 if ( songDirectories . Count == 0 ) return null ;
32633266
@@ -3279,19 +3282,28 @@ private static int GetCoverArtPriority(string fileNameWithoutExt) =>
32793282
32803283 // Search Pass A first (artist-level folders), then Pass B (album folders).
32813284 // Navidrome logic: artist.* in parent folder (A) > artist.* in song folder (B).
3282- return await ScanDirectoriesForArtistImageAsync ( passADirs , artistName , ct ) . ConfigureAwait ( false ) ??
3283- await ScanDirectoriesForArtistImageAsync ( passBDirs , artistName , ct ) . ConfigureAwait ( false ) ;
3285+ return await ScanDirectoriesForArtistImageAsync ( passADirs , artistName , ct , directoryCache ) . ConfigureAwait ( false ) ??
3286+ await ScanDirectoriesForArtistImageAsync ( passBDirs , artistName , ct , directoryCache ) . ConfigureAwait ( false ) ;
32843287 }
32853288
32863289 /// <summary>
32873290 /// Helper method to scan a set of directories for a valid artist image file.
32883291 /// </summary>
3289- private async Task < byte [ ] ? > ScanDirectoriesForArtistImageAsync ( IEnumerable < string > directories , string artistName , CancellationToken ct )
3292+ private async Task < byte [ ] ? > ScanDirectoriesForArtistImageAsync ( IEnumerable < string > directories , string artistName , CancellationToken ct , Dictionary < string , string ? > ? directoryCache = null )
32903293 {
32913294 foreach ( var dir in directories )
32923295 {
32933296 ct . ThrowIfCancellationRequested ( ) ;
3294- var imagePath = FindArtistImageInDirectory ( dir ) ;
3297+
3298+ if ( directoryCache == null || ! directoryCache . TryGetValue ( dir , out var imagePath ) )
3299+ {
3300+ imagePath = FindArtistImageInDirectory ( dir ) ;
3301+ if ( directoryCache != null )
3302+ {
3303+ directoryCache [ dir ] = imagePath ;
3304+ }
3305+ }
3306+
32953307 if ( imagePath == null ) continue ;
32963308
32973309 try
@@ -4648,7 +4660,7 @@ await ImageStorageHelper.SaveImageBytesAsync(_fileSystem,
46484660 // Skip if a local folder image or custom image is already set.
46494661 if ( ! string . IsNullOrEmpty ( finalImageUrl ) && ! localImageFound )
46504662 {
4651- if ( artist . LocalImageCachePath == null || ! artist . LocalImageCachePath . Contains ( ".custom." ) )
4663+ if ( artist . LocalImageCachePath == null || ( ! artist . LocalImageCachePath . Contains ( ".custom." ) && ! artist . LocalImageCachePath . Contains ( ".local." ) ) )
46524664 {
46534665 var downloadedPath =
46544666 await DownloadAndCacheArtistImageAsync ( artist , new Uri ( finalImageUrl ) , cancellationToken ) . ConfigureAwait ( false ) ;
0 commit comments