From 2767f046218daa24dbcaeae7843acd07ba5eff7d Mon Sep 17 00:00:00 2001 From: Robert McRackan Date: Mon, 18 Oct 2021 14:41:57 -0400 Subject: [PATCH] split AaxcDownload single and multi --- AaxDecrypter/AaxcDownloadConvertBase.cs | 91 +++++++ AaxDecrypter/AaxcDownloadConverter.cs | 238 ------------------ AaxDecrypter/AaxcDownloadMultiConverter.cs | 130 ++++++++++ AaxDecrypter/AaxcDownloadSingleConverter.cs | 53 ++++ AaxDecrypter/AudiobookDownloadBase.cs | 15 +- .../UnencryptedAudiobookDownloader.cs | 14 +- FileLiberator/DownloadDecryptBook.cs | 12 +- 7 files changed, 293 insertions(+), 260 deletions(-) create mode 100644 AaxDecrypter/AaxcDownloadConvertBase.cs delete mode 100644 AaxDecrypter/AaxcDownloadConverter.cs create mode 100644 AaxDecrypter/AaxcDownloadMultiConverter.cs create mode 100644 AaxDecrypter/AaxcDownloadSingleConverter.cs diff --git a/AaxDecrypter/AaxcDownloadConvertBase.cs b/AaxDecrypter/AaxcDownloadConvertBase.cs new file mode 100644 index 00000000..6e2b4568 --- /dev/null +++ b/AaxDecrypter/AaxcDownloadConvertBase.cs @@ -0,0 +1,91 @@ +using System; +using AAXClean; +using Dinah.Core.Net.Http; + +namespace AaxDecrypter +{ + public abstract class AaxcDownloadConvertBase : AudiobookDownloadBase + { + protected OutputFormat OutputFormat { get; } + + protected AaxFile AaxFile; + + protected AaxcDownloadConvertBase(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat) + : base(outFileName, cacheDirectory, dlLic) + { + OutputFormat = outputFormat; + } + + /// Setting cover art by this method will insert the art into the audiobook metadata + public override void SetCoverArt(byte[] coverArt) + { + base.SetCoverArt(coverArt); + if (coverArt is not null) + AaxFile?.AppleTags.SetCoverArt(coverArt); + } + + protected bool Step_GetMetadata() + { + AaxFile = new AaxFile(InputFileStream); + + OnRetrievedTitle(AaxFile.AppleTags.TitleSansUnabridged); + OnRetrievedAuthors(AaxFile.AppleTags.FirstAuthor ?? "[unknown]"); + OnRetrievedNarrators(AaxFile.AppleTags.Narrator ?? "[unknown]"); + OnRetrievedCoverArt(AaxFile.AppleTags.Cover); + + return !IsCanceled; + } + + protected DownloadProgress Step_DownloadAudiobook_Start() + { + var zeroProgress = new DownloadProgress + { + BytesReceived = 0, + ProgressPercentage = 0, + TotalBytesToReceive = InputFileStream.Length + }; + + OnDecryptProgressUpdate(zeroProgress); + + AaxFile.SetDecryptionKey(DownloadLicense.AudibleKey, DownloadLicense.AudibleIV); + return zeroProgress; + } + + protected void Step_DownloadAudiobook_End(DownloadProgress zeroProgress) + { + AaxFile.Close(); + + CloseInputFileStream(); + + OnDecryptProgressUpdate(zeroProgress); + } + + protected void AaxFile_ConversionProgressUpdate(object sender, ConversionProgressEventArgs e) + { + var duration = AaxFile.Duration; + var remainingSecsToProcess = (duration - e.ProcessPosition).TotalSeconds; + var estTimeRemaining = remainingSecsToProcess / e.ProcessSpeed; + + if (double.IsNormal(estTimeRemaining)) + OnDecryptTimeRemaining(TimeSpan.FromSeconds(estTimeRemaining)); + + var progressPercent = e.ProcessPosition.TotalSeconds / duration.TotalSeconds; + + OnDecryptProgressUpdate( + new DownloadProgress + { + ProgressPercentage = 100 * progressPercent, + BytesReceived = (long)(InputFileStream.Length * progressPercent), + TotalBytesToReceive = InputFileStream.Length + }); + } + + public override void Cancel() + { + IsCanceled = true; + AaxFile?.Cancel(); + AaxFile?.Dispose(); + CloseInputFileStream(); + } + } +} diff --git a/AaxDecrypter/AaxcDownloadConverter.cs b/AaxDecrypter/AaxcDownloadConverter.cs deleted file mode 100644 index e2f71cee..00000000 --- a/AaxDecrypter/AaxcDownloadConverter.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using AAXClean; -using Dinah.Core.Net.Http; -using Dinah.Core.StepRunner; -using FileManager; - -namespace AaxDecrypter -{ - public class AaxcDownloadConverter : AudiobookDownloadBase - { - private static readonly TimeSpan minChapterLength = TimeSpan.FromSeconds(3); - protected override StepSequence Steps { get; } - - private AaxFile aaxFile; - private OutputFormat OutputFormat { get; } - - private List multiPartFilePaths { get; } = new List(); - - public AaxcDownloadConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat, bool splitFileByChapters) - : base(outFileName, cacheDirectory, dlLic) - { - OutputFormat = outputFormat; - - Steps = new StepSequence - { - Name = "Download and Convert Aaxc To " + OutputFormat, - - ["Step 1: Get Aaxc Metadata"] = Step1_GetMetadata, - ["Step 2: Download Decrypted Audiobook"] = splitFileByChapters - ? Step2_DownloadAudiobookAsMultipleFilesPerChapter - : Step2_DownloadAudiobookAsSingleFile, - ["Step 3: Create Cue"] = splitFileByChapters - ? () => true - : Step3_CreateCue, - ["Step 4: Cleanup"] = Step4_Cleanup, - }; - } - - /// - /// Setting cover art by this method will insert the art into the audiobook metadata - /// - public override void SetCoverArt(byte[] coverArt) - { - base.SetCoverArt(coverArt); - - aaxFile?.AppleTags.SetCoverArt(coverArt); - } - - protected override bool Step1_GetMetadata() - { - aaxFile = new AaxFile(InputFileStream); - - OnRetrievedTitle(aaxFile.AppleTags.TitleSansUnabridged); - OnRetrievedAuthors(aaxFile.AppleTags.FirstAuthor ?? "[unknown]"); - OnRetrievedNarrators(aaxFile.AppleTags.Narrator ?? "[unknown]"); - OnRetrievedCoverArt(aaxFile.AppleTags.Cover); - - return !IsCanceled; - } - - private bool Step2_DownloadAudiobookAsSingleFile() - { - var zeroProgress = Step2_Start(); - - FileUtility.SaferDelete(OutputFileName); - - var outputFile = File.Open(OutputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite); - - aaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate; - var decryptionResult = OutputFormat == OutputFormat.M4b ? aaxFile.ConvertToMp4a(outputFile, DownloadLicense.ChapterInfo) : aaxFile.ConvertToMp3(outputFile); - aaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate; - - DownloadLicense.ChapterInfo = aaxFile.Chapters; - - Step2_End(zeroProgress); - - var success = decryptionResult == ConversionResult.NoErrorsDetected && !IsCanceled; - if (success) - base.OnFileCreated(OutputFileName); - - return success; - } - - /* -https://github.com/rmcrackan/Libation/pull/127#issuecomment-939088489 - -If the chapter truly is empty, that is, 0 audio frames in length, then yes it is ignored. -If the chapter is shorter than 3 seconds long but still has some audio frames, those frames are combined with the following chapter and not split into a new file. - -I also implemented file naming by chapter title. When 2 or more consecutive chapters are combined, the first of the combined chapter's title is used in the file name. For example, given an audiobook with the following chapters: - -00:00:00 - 00:00:02 | Part 1 -00:00:02 - 00:35:00 | Chapter 1 -00:35:02 - 01:02:00 | Chapter 2 -01:02:00 - 01:02:02 | Part 2 -01:02:02 - 01:41:00 | Chapter 3 -01:41:00 - 02:05:00 | Chapter 4 - -The book will be split into the following files: - -00:00:00 - 00:35:00 | Book - 01 - Part 1.m4b -00:35:00 - 01:02:00 | Book - 02 - Chapter 2.m4b -01:02:00 - 01:41:00 | Book - 03 - Part 2.m4b -01:41:00 - 02:05:00 | Book - 04 - Chapter 4.m4b - -That naming may not be desirable for everyone, but it's an easy change to instead use the last of the combined chapter's title in the file name. - */ - private bool Step2_DownloadAudiobookAsMultipleFilesPerChapter() - { - var zeroProgress = Step2_Start(); - - var chapters = DownloadLicense.ChapterInfo.Chapters.ToList(); - - //Ensure split files are at least minChapterLength in duration. - var splitChapters = new ChapterInfo(); - - var runningTotal = TimeSpan.Zero; - string title = ""; - - for (int i = 0; i < chapters.Count; i++) - { - if (runningTotal == TimeSpan.Zero) - title = chapters[i].Title; - - runningTotal += chapters[i].Duration; - - if (runningTotal >= minChapterLength) - { - splitChapters.AddChapter(title, runningTotal); - runningTotal = TimeSpan.Zero; - } - } - - // reset, just in case - multiPartFilePaths.Clear(); - - aaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate; - if (OutputFormat == OutputFormat.M4b) - ConvertToMultiMp4b(splitChapters); - else - ConvertToMultiMp3(splitChapters); - aaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate; - - Step2_End(zeroProgress); - - var success = !IsCanceled; - - if (success) - foreach (var path in multiPartFilePaths) - OnFileCreated(path); - - return success; - } - - private DownloadProgress Step2_Start() - { - var zeroProgress = new DownloadProgress - { - BytesReceived = 0, - ProgressPercentage = 0, - TotalBytesToReceive = InputFileStream.Length - }; - - OnDecryptProgressUpdate(zeroProgress); - - aaxFile.SetDecryptionKey(DownloadLicense.AudibleKey, DownloadLicense.AudibleIV); - return zeroProgress; - } - - private void Step2_End(DownloadProgress zeroProgress) - { - aaxFile.Close(); - - CloseInputFileStream(); - - OnDecryptProgressUpdate(zeroProgress); - } - - private void ConvertToMultiMp4b(ChapterInfo splitChapters) - { - var chapterCount = 0; - aaxFile.ConvertToMultiMp4a(splitChapters, newSplitCallback => - createOutputFileStream(++chapterCount, splitChapters, newSplitCallback) - ); - } - - private void ConvertToMultiMp3(ChapterInfo splitChapters) - { - var chapterCount = 0; - aaxFile.ConvertToMultiMp3(splitChapters, newSplitCallback => - { - createOutputFileStream(++chapterCount, splitChapters, newSplitCallback); - newSplitCallback.LameConfig.ID3.Track = chapterCount.ToString(); - }); - } - - private void createOutputFileStream(int currentChapter, ChapterInfo splitChapters, NewSplitCallback newSplitCallback) - { - var fileName = FileUtility.GetMultipartFileName(OutputFileName, currentChapter, splitChapters.Count, newSplitCallback.Chapter.Title); - multiPartFilePaths.Add(fileName); - - FileUtility.SaferDelete(fileName); - - newSplitCallback.OutputFile = File.Open(fileName, FileMode.OpenOrCreate); - } - - private void AaxFile_ConversionProgressUpdate(object sender, ConversionProgressEventArgs e) - { - var duration = aaxFile.Duration; - double remainingSecsToProcess = (duration - e.ProcessPosition).TotalSeconds; - double estTimeRemaining = remainingSecsToProcess / e.ProcessSpeed; - - if (double.IsNormal(estTimeRemaining)) - OnDecryptTimeRemaining(TimeSpan.FromSeconds(estTimeRemaining)); - - double progressPercent = e.ProcessPosition.TotalSeconds / duration.TotalSeconds; - - OnDecryptProgressUpdate( - new DownloadProgress - { - ProgressPercentage = 100 * progressPercent, - BytesReceived = (long)(InputFileStream.Length * progressPercent), - TotalBytesToReceive = InputFileStream.Length - }); - } - - public override void Cancel() - { - IsCanceled = true; - aaxFile?.Cancel(); - aaxFile?.Dispose(); - CloseInputFileStream(); - } - } -} diff --git a/AaxDecrypter/AaxcDownloadMultiConverter.cs b/AaxDecrypter/AaxcDownloadMultiConverter.cs new file mode 100644 index 00000000..cef5b258 --- /dev/null +++ b/AaxDecrypter/AaxcDownloadMultiConverter.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using AAXClean; +using Dinah.Core.StepRunner; +using FileManager; + +namespace AaxDecrypter +{ + public class AaxcDownloadMultiConverter : AaxcDownloadConvertBase + { + protected override StepSequence Steps { get; } + + private static TimeSpan minChapterLength { get; } = TimeSpan.FromSeconds(3); + private List multiPartFilePaths { get; } = new List(); + + public AaxcDownloadMultiConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat) + : base(outFileName, cacheDirectory, dlLic, outputFormat) + { + Steps = new StepSequence + { + Name = "Download and Convert Aaxc To " + OutputFormat, + + ["Step 1: Get Aaxc Metadata"] = Step_GetMetadata, + ["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsMultipleFilesPerChapter, + ["Step 3: Cleanup"] = Step_Cleanup, + }; + } + + /* +https://github.com/rmcrackan/Libation/pull/127#issuecomment-939088489 + +If the chapter truly is empty, that is, 0 audio frames in length, then yes it is ignored. +If the chapter is shorter than 3 seconds long but still has some audio frames, those frames are combined with the following chapter and not split into a new file. + +I also implemented file naming by chapter title. When 2 or more consecutive chapters are combined, the first of the combined chapter's title is used in the file name. For example, given an audiobook with the following chapters: + +00:00:00 - 00:00:02 | Part 1 +00:00:02 - 00:35:00 | Chapter 1 +00:35:02 - 01:02:00 | Chapter 2 +01:02:00 - 01:02:02 | Part 2 +01:02:02 - 01:41:00 | Chapter 3 +01:41:00 - 02:05:00 | Chapter 4 + +The book will be split into the following files: + +00:00:00 - 00:35:00 | Book - 01 - Part 1.m4b +00:35:00 - 01:02:00 | Book - 02 - Chapter 2.m4b +01:02:00 - 01:41:00 | Book - 03 - Part 2.m4b +01:41:00 - 02:05:00 | Book - 04 - Chapter 4.m4b + +That naming may not be desirable for everyone, but it's an easy change to instead use the last of the combined chapter's title in the file name. + */ + private bool Step_DownloadAudiobookAsMultipleFilesPerChapter() + { + var zeroProgress = Step_DownloadAudiobook_Start(); + + var chapters = DownloadLicense.ChapterInfo.Chapters.ToList(); + + // Ensure split files are at least minChapterLength in duration. + var splitChapters = new ChapterInfo(); + + var runningTotal = TimeSpan.Zero; + string title = ""; + + for (int i = 0; i < chapters.Count; i++) + { + if (runningTotal == TimeSpan.Zero) + title = chapters[i].Title; + + runningTotal += chapters[i].Duration; + + if (runningTotal >= minChapterLength) + { + splitChapters.AddChapter(title, runningTotal); + runningTotal = TimeSpan.Zero; + } + } + + // reset, just in case + multiPartFilePaths.Clear(); + + AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate; + if (OutputFormat == OutputFormat.M4b) + ConvertToMultiMp4a(splitChapters); + else + ConvertToMultiMp3(splitChapters); + AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate; + + Step_DownloadAudiobook_End(zeroProgress); + + var success = !IsCanceled; + + if (success) + foreach (var path in multiPartFilePaths) + OnFileCreated(path); + + return success; + } + + private void ConvertToMultiMp4a(ChapterInfo splitChapters) + { + var chapterCount = 0; + AaxFile.ConvertToMultiMp4a(splitChapters, newSplitCallback => + createOutputFileStream(++chapterCount, splitChapters, newSplitCallback) + ); + } + + private void ConvertToMultiMp3(ChapterInfo splitChapters) + { + var chapterCount = 0; + AaxFile.ConvertToMultiMp3(splitChapters, newSplitCallback => + { + createOutputFileStream(++chapterCount, splitChapters, newSplitCallback); + newSplitCallback.LameConfig.ID3.Track = chapterCount.ToString(); + }); + } + + private void createOutputFileStream(int currentChapter, ChapterInfo splitChapters, NewSplitCallback newSplitCallback) + { + var fileName = FileUtility.GetMultipartFileName(OutputFileName, currentChapter, splitChapters.Count, newSplitCallback.Chapter.Title); + multiPartFilePaths.Add(fileName); + + FileUtility.SaferDelete(fileName); + + newSplitCallback.OutputFile = File.Open(fileName, FileMode.OpenOrCreate); + } + } +} diff --git a/AaxDecrypter/AaxcDownloadSingleConverter.cs b/AaxDecrypter/AaxcDownloadSingleConverter.cs new file mode 100644 index 00000000..d4d6b22a --- /dev/null +++ b/AaxDecrypter/AaxcDownloadSingleConverter.cs @@ -0,0 +1,53 @@ +using System; +using System.IO; +using AAXClean; +using Dinah.Core.StepRunner; +using FileManager; + +namespace AaxDecrypter +{ + public class AaxcDownloadSingleConverter : AaxcDownloadConvertBase + { + protected override StepSequence Steps { get; } + + public AaxcDownloadSingleConverter(string outFileName, string cacheDirectory, DownloadLicense dlLic, OutputFormat outputFormat) + : base(outFileName, cacheDirectory, dlLic, outputFormat) + { + Steps = new StepSequence + { + Name = "Download and Convert Aaxc To " + OutputFormat, + + ["Step 1: Get Aaxc Metadata"] = Step_GetMetadata, + ["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsSingleFile, + ["Step 3: Create Cue"] = Step_CreateCue, + ["Step 4: Cleanup"] = Step_Cleanup, + }; + } + + private bool Step_DownloadAudiobookAsSingleFile() + { + var zeroProgress = Step_DownloadAudiobook_Start(); + + FileUtility.SaferDelete(OutputFileName); + + var outputFile = File.Open(OutputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite); + + AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate; + var decryptionResult + = OutputFormat == OutputFormat.M4b + ? AaxFile.ConvertToMp4a(outputFile, DownloadLicense.ChapterInfo) + : AaxFile.ConvertToMp3(outputFile); + AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate; + + DownloadLicense.ChapterInfo = AaxFile.Chapters; + + Step_DownloadAudiobook_End(zeroProgress); + + var success = decryptionResult == ConversionResult.NoErrorsDetected && !IsCanceled; + if (success) + base.OnFileCreated(OutputFileName); + + return success; + } + } +} diff --git a/AaxDecrypter/AudiobookDownloadBase.cs b/AaxDecrypter/AudiobookDownloadBase.cs index 95b7b230..5a7f0c8a 100644 --- a/AaxDecrypter/AudiobookDownloadBase.cs +++ b/AaxDecrypter/AudiobookDownloadBase.cs @@ -34,7 +34,7 @@ namespace AaxDecrypter private string jsonDownloadState => Path.Combine(cacheDir, Path.ChangeExtension(OutputFileName, ".json")); private string tempFile => Path.ChangeExtension(jsonDownloadState, ".tmp"); - public AudiobookDownloadBase(string outFileName, string cacheDirectory, DownloadLicense dlLic) + protected AudiobookDownloadBase(string outFileName, string cacheDirectory, DownloadLicense dlLic) { OutputFileName = ArgumentValidator.EnsureNotNullOrWhiteSpace(outFileName, nameof(outFileName)); @@ -52,14 +52,11 @@ namespace AaxDecrypter } public abstract void Cancel(); - protected abstract bool Step1_GetMetadata(); public virtual void SetCoverArt(byte[] coverArt) { - if (coverArt is null) - return; - - OnRetrievedCoverArt(coverArt); + if (coverArt is not null) + OnRetrievedCoverArt(coverArt); } public bool Run() @@ -93,7 +90,7 @@ namespace AaxDecrypter nfsPersister?.Dispose(); } - protected bool Step3_CreateCue() + protected bool Step_CreateCue() { // not a critical step. its failure should not prevent future steps from running try @@ -105,12 +102,12 @@ namespace AaxDecrypter } catch (Exception ex) { - Serilog.Log.Logger.Error(ex, $"{nameof(Step3_CreateCue)}. FAILED"); + Serilog.Log.Logger.Error(ex, $"{nameof(Step_CreateCue)}. FAILED"); } return !IsCanceled; } - protected bool Step4_Cleanup() + protected bool Step_Cleanup() { FileUtility.SaferDelete(jsonDownloadState); FileUtility.SaferDelete(tempFile); diff --git a/AaxDecrypter/UnencryptedAudiobookDownloader.cs b/AaxDecrypter/UnencryptedAudiobookDownloader.cs index b58ece36..108a662f 100644 --- a/AaxDecrypter/UnencryptedAudiobookDownloader.cs +++ b/AaxDecrypter/UnencryptedAudiobookDownloader.cs @@ -17,10 +17,10 @@ namespace AaxDecrypter { Name = "Download Mp3 Audiobook", - ["Step 1: Get Mp3 Metadata"] = Step1_GetMetadata, - ["Step 2: Download Audiobook"] = Step2_DownloadAudiobookAsSingleFile, - ["Step 3: Create Cue"] = Step3_CreateCue, - ["Step 4: Cleanup"] = Step4_Cleanup, + ["Step 1: Get Mp3 Metadata"] = Step_GetMetadata, + ["Step 2: Download Audiobook"] = Step_DownloadAudiobookAsSingleFile, + ["Step 3: Create Cue"] = Step_CreateCue, + ["Step 4: Cleanup"] = Step_Cleanup, }; } @@ -30,18 +30,18 @@ namespace AaxDecrypter CloseInputFileStream(); } - protected override bool Step1_GetMetadata() + protected bool Step_GetMetadata() { OnRetrievedCoverArt(null); return !IsCanceled; } - private bool Step2_DownloadAudiobookAsSingleFile() + private bool Step_DownloadAudiobookAsSingleFile() { DateTime startTime = DateTime.Now; - //MUST put InputFileStream.Length first, because it starts background downloader. + // MUST put InputFileStream.Length first, because it starts background downloader. while (InputFileStream.Length > InputFileStream.WritePosition && !InputFileStream.IsCancelled) { diff --git a/FileLiberator/DownloadDecryptBook.cs b/FileLiberator/DownloadDecryptBook.cs index 74f904ba..2b2cbdb1 100644 --- a/FileLiberator/DownloadDecryptBook.cs +++ b/FileLiberator/DownloadDecryptBook.cs @@ -120,9 +120,10 @@ namespace FileLiberator var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory; - abDownloader = contentLic.DrmType == AudibleApi.Common.DrmType.Adrm - ? new AaxcDownloadConverter(outFileName, cacheDir, audiobookDlLic, outputFormat, Configuration.Instance.SplitFilesByChapter) - : new UnencryptedAudiobookDownloader(outFileName, cacheDir, audiobookDlLic); + abDownloader + = contentLic.DrmType != AudibleApi.Common.DrmType.Adrm ? new UnencryptedAudiobookDownloader(outFileName, cacheDir, audiobookDlLic) + : Configuration.Instance.SplitFilesByChapter ? new AaxcDownloadMultiConverter(outFileName, cacheDir, audiobookDlLic, outputFormat) + : new AaxcDownloadSingleConverter(outFileName, cacheDir, audiobookDlLic, outputFormat); abDownloader.DecryptProgressUpdate += (_, progress) => OnStreamingProgressChanged(progress); abDownloader.DecryptTimeRemaining += (_, remaining) => OnStreamingTimeRemaining(remaining); abDownloader.RetrievedTitle += (_, title) => OnTitleDiscovered(title); @@ -166,11 +167,10 @@ namespace FileLiberator private void AaxcDownloader_RetrievedCoverArt(object sender, byte[] e) { - if (e is null && Configuration.Instance.AllowLibationFixup) - OnRequestCoverArt(abDownloader.SetCoverArt); - if (e is not null) OnCoverImageDiscovered(e); + else if (Configuration.Instance.AllowLibationFixup) + OnRequestCoverArt(abDownloader.SetCoverArt); } /// Move new files to 'Books' directory