From 805f42b1cccf490f37c4a52136aec86d64b490e5 Mon Sep 17 00:00:00 2001 From: Mbucari <37587114+Mbucari@users.noreply.github.com> Date: Sat, 22 Nov 2025 12:32:00 -0700 Subject: [PATCH] Add warnings for inaccessable InProgress directory (#1446) --- .../UnencryptedAudiobookDownloader.cs | 2 +- Source/AppScaffolding/LibationScaffolding.cs | 11 ++++-- Source/FileLiberator/DownloadDecryptBook.cs | 13 ++++--- .../LibationFileManager/AudibleFileStorage.cs | 37 +++++++++++++++++-- .../ProcessQueue/ProcessQueueViewModel.cs | 20 ++++++++++ 5 files changed, 68 insertions(+), 15 deletions(-) diff --git a/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs b/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs index 1c6f09e8..9b9a4b4e 100644 --- a/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs +++ b/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs @@ -1,6 +1,6 @@ using FileManager; using System.Threading.Tasks; - +#nullable enable namespace AaxDecrypter { public class UnencryptedAudiobookDownloader : AudiobookDownloadBase diff --git a/Source/AppScaffolding/LibationScaffolding.cs b/Source/AppScaffolding/LibationScaffolding.cs index 7ab25d22..3e1d0696 100644 --- a/Source/AppScaffolding/LibationScaffolding.cs +++ b/Source/AppScaffolding/LibationScaffolding.cs @@ -243,6 +243,7 @@ namespace AppScaffolding //Log.Logger.Here().Debug("Begin Libation. Debug with line numbers"); } +#nullable enable private static void logStartupState(Configuration config) { #if DEBUG @@ -256,9 +257,11 @@ namespace AppScaffolding // begin logging session with a form feed Log.Logger.Information("\r\n\f"); - static int fileCount(FileManager.LongPath longPath) + static int fileCount(FileManager.LongPath? longPath) { - try { return FileManager.FileUtility.SaferEnumerateFiles(longPath).Count(); } + if (longPath is null) + return -1; + try { return FileManager.FileUtility.SaferEnumerateFiles(longPath).Count(); } catch { return -1; } } @@ -298,8 +301,8 @@ namespace AppScaffolding if (InteropFactory.InteropFunctionsType is null) Serilog.Log.Logger.Warning("WARNING: OSInteropProxy.InteropFunctionsType is null"); } - - private static void wireUpSystemEvents(Configuration configuration) +#nullable restore + private static void wireUpSystemEvents(Configuration configuration) { LibraryCommands.LibrarySizeChanged += (object _, List libraryBooks) => SearchEngineCommands.FullReIndex(libraryBooks); diff --git a/Source/FileLiberator/DownloadDecryptBook.cs b/Source/FileLiberator/DownloadDecryptBook.cs index 48ab08c8..302267c4 100644 --- a/Source/FileLiberator/DownloadDecryptBook.cs +++ b/Source/FileLiberator/DownloadDecryptBook.cs @@ -129,20 +129,21 @@ namespace FileLiberator private async Task DownloadAudiobookAsync(AudibleApi.Api api, DownloadOptions dlOptions, CancellationToken cancellationToken) { - var outpoutDir = AudibleFileStorage.DecryptInProgressDirectory; - var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory; + //Directories are validated prior to beginning download/decrypt + var outputDir = AudibleFileStorage.DecryptInProgressDirectory!; + var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory!; var result = new AudiobookDecryptResult(false, [], []); try { if (dlOptions.DrmType is not DrmType.Adrm and not DrmType.Widevine) - abDownloader = new UnencryptedAudiobookDownloader(outpoutDir, cacheDir, dlOptions); + abDownloader = new UnencryptedAudiobookDownloader(outputDir, cacheDir, dlOptions); else { AaxcDownloadConvertBase converter = dlOptions.Config.SplitFilesByChapter && dlOptions.ChapterInfo.Count > 1 ? - new AaxcDownloadMultiConverter(outpoutDir, cacheDir, dlOptions) : - new AaxcDownloadSingleConverter(outpoutDir, cacheDir, dlOptions); + new AaxcDownloadMultiConverter(outputDir, cacheDir, dlOptions) : + new AaxcDownloadSingleConverter(outputDir, cacheDir, dlOptions); if (dlOptions.Config.AllowLibationFixup) converter.RetrievedMetadata += Converter_RetrievedMetadata; @@ -176,7 +177,7 @@ namespace FileLiberator void AbDownloader_TempFileCreated(object? sender, TempFile e) { - if (Path.GetDirectoryName(e.FilePath) == outpoutDir) + if (Path.GetDirectoryName(e.FilePath) == outputDir) { result.ResultFiles.Add(e); } diff --git a/Source/LibationFileManager/AudibleFileStorage.cs b/Source/LibationFileManager/AudibleFileStorage.cs index 566469dd..4ab1afb9 100644 --- a/Source/LibationFileManager/AudibleFileStorage.cs +++ b/Source/LibationFileManager/AudibleFileStorage.cs @@ -19,8 +19,32 @@ namespace LibationFileManager protected abstract List GetFilePathsCustom(string productId); #region static - public static LongPath DownloadsInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DownloadsInProgress")).FullName; - public static LongPath DecryptInProgressDirectory => Directory.CreateDirectory(Path.Combine(Configuration.Instance.InProgress, "DecryptInProgress")).FullName; + + /* + * Operations like LibraryCommands.GetCounts() hit the file system hard. + * Since failing to create a directory and exception handling is expensive, + * only retry creating InProgress subdirectories every RetryInProgressInterval. + */ + private static DateTime lastInProgressFail; + private static readonly TimeSpan RetryInProgressInterval = TimeSpan.FromSeconds(2); + + private static DirectoryInfo? CreateInProgressDirectory(string subDirectory) + { + try + { + return (DateTime.UtcNow - lastInProgressFail) < RetryInProgressInterval ? null + : new DirectoryInfo(Configuration.Instance.InProgress).CreateSubdirectoryEx(subDirectory); + } + catch (Exception ex) + { + Serilog.Log.Error(ex, "Error creating subdirectory in {@InProgress}", Configuration.Instance.InProgress); + lastInProgressFail = DateTime.UtcNow; + return null; + } + } + + public static LongPath? DownloadsInProgressDirectory => CreateInProgressDirectory("DownloadsInProgress")?.FullName; + public static LongPath? DecryptInProgressDirectory => CreateInProgressDirectory("DecryptInProgress")?.FullName; static AudibleFileStorage() { @@ -28,7 +52,9 @@ namespace LibationFileManager //Do not clean DownloadsInProgressDirectory. Those files are resumable. try { - foreach (var tempFile in FileUtility.SaferEnumerateFiles(DecryptInProgressDirectory)) + if (DecryptInProgressDirectory is not LongPath decryptDir) + return; + foreach (var tempFile in FileUtility.SaferEnumerateFiles(decryptDir)) FileUtility.SaferDelete(tempFile); } catch (Exception ex) @@ -114,9 +140,12 @@ namespace LibationFileManager protected override List GetFilePathsCustom(string productId) { + if (DownloadsInProgressDirectory is not LongPath dlFolder) + return []; + var regex = GetBookSearchRegex(productId); return FileUtility - .SaferEnumerateFiles(DownloadsInProgressDirectory, "*.*", SearchOption.AllDirectories) + .SaferEnumerateFiles(dlFolder, "*.*", SearchOption.AllDirectories) .Where(s => regex.IsMatch(s)).ToList(); } diff --git a/Source/LibationUiBase/ProcessQueue/ProcessQueueViewModel.cs b/Source/LibationUiBase/ProcessQueue/ProcessQueueViewModel.cs index 94320d15..ba1ca98b 100644 --- a/Source/LibationUiBase/ProcessQueue/ProcessQueueViewModel.cs +++ b/Source/LibationUiBase/ProcessQueue/ProcessQueueViewModel.cs @@ -184,6 +184,26 @@ public class ProcessQueueViewModel : ReactiveObject MessageBoxIcon.Error); return false; } + else if (AudibleFileStorage.DownloadsInProgressDirectory is null) + { + Serilog.Log.Logger.Error("Failed to create DownloadsInProgressDirectory in {@InProgress}", Configuration.Instance.InProgress); + MessageBoxBase.Show( + $"Libation was unable to create the \"Downloads In Progress\" folder in:\n{Configuration.Instance.InProgress}\n\nPlease change the In Progress location in the settings menu.", + "Failed to Create Downloads In Progress Directory", + MessageBoxButtons.OK, + MessageBoxIcon.Error); + return false; + } + else if (AudibleFileStorage.DecryptInProgressDirectory is null) + { + Serilog.Log.Logger.Error("Failed to create DecryptInProgressDirectory in {@InProgress}", Configuration.Instance.InProgress); + MessageBoxBase.Show( + $"Libation was unable to create the \"Decrypt In Progress\" folder in:\n{Configuration.Instance.InProgress}\n\nPlease change the In Progress location in the settings menu.", + "Failed to Create Decrypt In Progress Directory", + MessageBoxButtons.OK, + MessageBoxIcon.Error); + return false; + } return true; }