From 849f5f508ecc7916a021e71cec159419de77173c Mon Sep 17 00:00:00 2001 From: rmcrackan Date: Fri, 1 May 2026 09:38:29 -0400 Subject: [PATCH] trying again to fix the intermittent cover image issue --- Source/AppScaffolding/AppScaffolding.csproj | 2 +- Source/FileLiberator/DownloadDecryptBook.cs | 4 +- .../LibationFileManager/WindowsDirectory.cs | 46 +++++++++++++++++-- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/Source/AppScaffolding/AppScaffolding.csproj b/Source/AppScaffolding/AppScaffolding.csproj index 9ae20167..153ca05e 100644 --- a/Source/AppScaffolding/AppScaffolding.csproj +++ b/Source/AppScaffolding/AppScaffolding.csproj @@ -2,7 +2,7 @@ net10.0 - 13.3.5.1 + 13.3.5.2 enable diff --git a/Source/FileLiberator/DownloadDecryptBook.cs b/Source/FileLiberator/DownloadDecryptBook.cs index 5a9a0ad8..4e68aa29 100644 --- a/Source/FileLiberator/DownloadDecryptBook.cs +++ b/Source/FileLiberator/DownloadDecryptBook.cs @@ -107,7 +107,9 @@ public class DownloadDecryptBook : AudioDecodable, IProcessable WindowsDirectory.SetCoverAsFolderIcon(libraryBook.Book.PictureId, finalStorageDir, cancellationToken), cancellationToken); + // Same picture key as DownloadCoverArt so we hit the same Amazon asset (and Images cache) the parallel task likely just populated. + var folderIconPictureId = libraryBook.Book.PictureLarge ?? libraryBook.Book.PictureId; + await Task.Run(() => WindowsDirectory.SetCoverAsFolderIcon(folderIconPictureId, finalStorageDir, cancellationToken), cancellationToken); Serilog.Log.Verbose("Updating liberated status for {@Book}", libraryBook.LogFriendly()); await libraryBook.UpdateBookStatusAsync(LiberatedStatus.Liberated, Configuration.LibationVersion, audioFormat, audioVersion); diff --git a/Source/LibationFileManager/WindowsDirectory.cs b/Source/LibationFileManager/WindowsDirectory.cs index f5639485..344899b3 100644 --- a/Source/LibationFileManager/WindowsDirectory.cs +++ b/Source/LibationFileManager/WindowsDirectory.cs @@ -37,11 +37,7 @@ public static class WindowsDirectory continue; } - Serilog.Log.Logger.Warning( - "Could not set Explorer folder icon after {MaxAttempts} attempts: the 300x300 cover image never became available (empty or missing). The audiobook download itself is unaffected. Check your network to Amazon images, disk space under Libation's Images folder, or try liberating again. {@DebugInfo}", - FolderIconMaxAttempts, new { directory, pictureId }); - TryDeleteFolderIcon(directory); - return; + break; } InteropFactory.Create().SetFolderIcon(imageJpegBytes: jpegBytes, directory: directory); @@ -56,6 +52,14 @@ public static class WindowsDirectory continue; } + if (TrySetFolderIconUsingPictureSize(pictureId, directory, PictureSize.Native, cancellationToken)) + { + Serilog.Log.Logger.Information( + "Set Explorer folder icon using full-size cover after 300x300 failed (decode, ICO conversion, or writing desktop.ini/Icon.ico). {@DebugInfo}", + new { directory, pictureId }); + return; + } + Serilog.Log.Logger.Error(ex, "Could not set Explorer folder icon after {MaxAttempts} attempts (decode, ICO conversion, or writing desktop.ini/Icon.ico failed). The audiobook download itself should still be fine; try liberating again, or check folder permissions if the library is on removable media. {@DebugInfo}", FolderIconMaxAttempts, new { directory, pictureId }); @@ -63,6 +67,38 @@ public static class WindowsDirectory return; } } + + // 300x300 never returned usable bytes — Native is a separate CDN URL and is often already cached by DownloadCoverArt in the same session. + if (TrySetFolderIconUsingPictureSize(pictureId, directory, PictureSize.Native, cancellationToken)) + { + Serilog.Log.Logger.Information( + "Set Explorer folder icon using full-size cover after 300x300 was empty or missing. {@DebugInfo}", + new { directory, pictureId }); + return; + } + + Serilog.Log.Logger.Warning( + "Could not set Explorer folder icon: neither 300x300 nor full-size cover became available. The audiobook download itself is unaffected. Check your network to Amazon images, disk space under Libation's Images folder, or try liberating again. {@DebugInfo}", + new { directory, pictureId }); + TryDeleteFolderIcon(directory); + } + + static bool TrySetFolderIconUsingPictureSize(string pictureId, string directory, PictureSize size, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + try + { + var jpegBytes = PictureStorage.GetPictureSynchronously(new(pictureId, size), cancellationToken); + if (jpegBytes.Length == 0) + return false; + InteropFactory.Create().SetFolderIcon(imageJpegBytes: jpegBytes, directory: directory); + return true; + } + catch (Exception ex) + { + Serilog.Log.Logger.Debug(ex, "Folder icon: could not set using {PictureSize}. {@DebugInfo}", size, new { directory, pictureId }); + return false; + } } static void DelayBetweenFolderIconRetries(CancellationToken cancellationToken, int attemptAfterFailure)