diff --git a/Source/AaxDecrypter/AaxcDownloadMultiConverter.cs b/Source/AaxDecrypter/AaxcDownloadMultiConverter.cs index ea11c319..8f4f31f2 100644 --- a/Source/AaxDecrypter/AaxcDownloadMultiConverter.cs +++ b/Source/AaxDecrypter/AaxcDownloadMultiConverter.cs @@ -2,31 +2,65 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using AAXClean; using AAXClean.Codecs; -using Dinah.Core; -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, IDownloadOptions dlOptions) - : base(outFileName, cacheDirectory, dlOptions) - { - Steps = new StepSequence - { - Name = "Download and Convert Aaxc To " + DownloadOptions.OutputFormat, + : base(outFileName, cacheDirectory, dlOptions) { } - ["Step 1: Get Aaxc Metadata"] = Step_GetMetadata, - ["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsMultipleFilesPerChapter, - ["Step 3: Cleanup"] = Step_Cleanup, - }; + public override async Task RunAsync() + { + try + { + Serilog.Log.Information("Begin download and convert Aaxc To {format}", DownloadOptions.OutputFormat); + + //Step 1 + Serilog.Log.Information("Begin Get Aaxc Metadata"); + if (await Task.Run(Step_GetMetadata)) + Serilog.Log.Information("Completed Get Aaxc Metadata"); + else + { + Serilog.Log.Information("Failed to Complete Get Aaxc Metadata"); + return false; + } + + //Step 2 + Serilog.Log.Information("Begin Download Decrypted Audiobook"); + if (await Step_DownloadAudiobookAsMultipleFilesPerChapter()) + Serilog.Log.Information("Completed Download Decrypted Audiobook"); + else + { + Serilog.Log.Information("Failed to Complete Download Decrypted Audiobook"); + return false; + } + + //Step 3 + Serilog.Log.Information("Begin Cleanup"); + if (await Task.Run(Step_Cleanup)) + Serilog.Log.Information("Completed Cleanup"); + else + { + Serilog.Log.Information("Failed to Complete Cleanup"); + return false; + } + + Serilog.Log.Information("Completed download and convert Aaxc To {format}", DownloadOptions.OutputFormat); + return true; + } + catch (Exception ex) + { + Serilog.Log.Error(ex, "Error encountered in download and convert Aaxc To {format}", DownloadOptions.OutputFormat); + return false; + } } /* @@ -53,7 +87,7 @@ The book will be split into the following files: 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() + private async Task Step_DownloadAudiobookAsMultipleFilesPerChapter() { var zeroProgress = Step_DownloadAudiobook_Start(); @@ -86,9 +120,9 @@ That naming may not be desirable for everyone, but it's an easy change to instea AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate; if (DownloadOptions.OutputFormat == OutputFormat.M4b) - result = ConvertToMultiMp4a(splitChapters); + result = await ConvertToMultiMp4a(splitChapters); else - result = ConvertToMultiMp3(splitChapters); + result = await ConvertToMultiMp3(splitChapters); AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate; Step_DownloadAudiobook_End(zeroProgress); @@ -96,10 +130,10 @@ That naming may not be desirable for everyone, but it's an easy change to instea return result == ConversionResult.NoErrorsDetected; } - private ConversionResult ConvertToMultiMp4a(ChapterInfo splitChapters) + private Task ConvertToMultiMp4a(ChapterInfo splitChapters) { var chapterCount = 0; - return AaxFile.ConvertToMultiMp4a + return AaxFile.ConvertToMultiMp4aAsync ( splitChapters, newSplitCallback => Callback(++chapterCount, splitChapters, newSplitCallback), @@ -107,10 +141,10 @@ That naming may not be desirable for everyone, but it's an easy change to instea ); } - private ConversionResult ConvertToMultiMp3(ChapterInfo splitChapters) + private Task ConvertToMultiMp3(ChapterInfo splitChapters) { var chapterCount = 0; - return AaxFile.ConvertToMultiMp3 + return AaxFile.ConvertToMultiMp3Async ( splitChapters, newSplitCallback => Callback(++chapterCount, splitChapters, newSplitCallback), diff --git a/Source/AaxDecrypter/AaxcDownloadSingleConverter.cs b/Source/AaxDecrypter/AaxcDownloadSingleConverter.cs index fbed8894..fcb73ecc 100644 --- a/Source/AaxDecrypter/AaxcDownloadSingleConverter.cs +++ b/Source/AaxDecrypter/AaxcDownloadSingleConverter.cs @@ -1,31 +1,74 @@ using System; using System.IO; +using System.Threading.Tasks; using AAXClean; using AAXClean.Codecs; -using Dinah.Core.StepRunner; using FileManager; namespace AaxDecrypter { public class AaxcDownloadSingleConverter : AaxcDownloadConvertBase { - protected override StepSequence Steps { get; } - public AaxcDownloadSingleConverter(string outFileName, string cacheDirectory, IDownloadOptions dlOptions) - : base(outFileName, cacheDirectory, dlOptions) - { - Steps = new StepSequence - { - Name = "Download and Convert Aaxc To " + DownloadOptions.OutputFormat, + : base(outFileName, cacheDirectory, dlOptions) { } - ["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, - }; + public override async Task RunAsync() + { + try + { + Serilog.Log.Information("Begin download and convert Aaxc To {format}", DownloadOptions.OutputFormat); + + //Step 1 + Serilog.Log.Information("Begin Step 1: Get Aaxc Metadata"); + if (await Task.Run(Step_GetMetadata)) + Serilog.Log.Information("Completed Step 1: Get Aaxc Metadata"); + else + { + Serilog.Log.Information("Failed to Complete Step 1: Get Aaxc Metadata"); + return false; + } + + //Step 2 + Serilog.Log.Information("Begin Step 2: Download Decrypted Audiobook"); + if (await Step_DownloadAudiobookAsSingleFile()) + Serilog.Log.Information("Completed Step 2: Download Decrypted Audiobook"); + else + { + Serilog.Log.Information("Failed to Complete Step 2: Download Decrypted Audiobook"); + return false; + } + + //Step 3 + Serilog.Log.Information("Begin Step 3: Create Cue"); + if (await Task.Run(Step_CreateCue)) + Serilog.Log.Information("Completed Step 3: Create Cue"); + else + { + Serilog.Log.Information("Failed to Complete Step 3: Create Cue"); + return false; + } + + //Step 4 + Serilog.Log.Information("Begin Step 4: Cleanup"); + if (await Task.Run(Step_Cleanup)) + Serilog.Log.Information("Completed Step 4: Cleanup"); + else + { + Serilog.Log.Information("Failed to Complete Step 4: Cleanup"); + return false; + } + + Serilog.Log.Information("Completed download and convert Aaxc To {format}", DownloadOptions.OutputFormat); + return true; + } + catch (Exception ex) + { + Serilog.Log.Error(ex, "Error encountered in download and convert Aaxc To {format}", DownloadOptions.OutputFormat); + return false; + } } - private bool Step_DownloadAudiobookAsSingleFile() + private async Task Step_DownloadAudiobookAsSingleFile() { var zeroProgress = Step_DownloadAudiobook_Start(); @@ -37,8 +80,8 @@ namespace AaxDecrypter AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate; var decryptionResult = DownloadOptions.OutputFormat == OutputFormat.M4b - ? AaxFile.ConvertToMp4a(outputFile, DownloadOptions.ChapterInfo, DownloadOptions.TrimOutputToChapterLength) - : AaxFile.ConvertToMp3(outputFile, DownloadOptions.LameConfig, DownloadOptions.ChapterInfo, DownloadOptions.TrimOutputToChapterLength); + ? await AaxFile.ConvertToMp4aAsync(outputFile, DownloadOptions.ChapterInfo, DownloadOptions.TrimOutputToChapterLength) + : await AaxFile.ConvertToMp3Async(outputFile, DownloadOptions.LameConfig, DownloadOptions.ChapterInfo, DownloadOptions.TrimOutputToChapterLength); AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate; DownloadOptions.ChapterInfo = AaxFile.Chapters; diff --git a/Source/AaxDecrypter/AudiobookDownloadBase.cs b/Source/AaxDecrypter/AudiobookDownloadBase.cs index 08d4f3d1..d6529b39 100644 --- a/Source/AaxDecrypter/AudiobookDownloadBase.cs +++ b/Source/AaxDecrypter/AudiobookDownloadBase.cs @@ -4,7 +4,6 @@ using System.IO; using System.Threading.Tasks; using Dinah.Core; using Dinah.Core.Net.Http; -using Dinah.Core.StepRunner; using FileManager; namespace AaxDecrypter @@ -31,7 +30,6 @@ namespace AaxDecrypter // Don't give the property a 'set'. This should have to be an obvious choice; not accidental protected void SetOutputFileName(string newOutputFileName) => OutputFileName = newOutputFileName; - protected abstract StepSequence Steps { get; } private NetworkFileStreamPersister nfsPersister; private string jsonDownloadState { get; } @@ -64,15 +62,7 @@ namespace AaxDecrypter OnRetrievedCoverArt(coverArt); } - public bool Run() - { - var (IsSuccess, _) = Steps.Run(); - - if (!IsSuccess) - Serilog.Log.Logger.Error("Conversion failed"); - - return IsSuccess; - } + public abstract Task RunAsync(); protected void OnRetrievedTitle(string title) => RetrievedTitle?.Invoke(this, title); diff --git a/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs b/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs index 142ccf44..b9ac5c3b 100644 --- a/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs +++ b/Source/AaxDecrypter/UnencryptedAudiobookDownloader.cs @@ -2,29 +2,63 @@ using System.Threading; using System.Threading.Tasks; using Dinah.Core.Net.Http; -using Dinah.Core.StepRunner; using FileManager; namespace AaxDecrypter { public class UnencryptedAudiobookDownloader : AudiobookDownloadBase { - protected override StepSequence Steps { get; } public UnencryptedAudiobookDownloader(string outFileName, string cacheDirectory, IDownloadOptions dlLic) - : base(outFileName, cacheDirectory, dlLic) - { - Steps = new StepSequence - { - Name = "Download Mp3 Audiobook", + : base(outFileName, cacheDirectory, dlLic) { } - ["Step 1: Get Mp3 Metadata"] = Step_GetMetadata, - ["Step 2: Download Audiobook"] = Step_DownloadAudiobookAsSingleFile, - ["Step 3: Create Cue"] = Step_CreateCue, - ["Step 4: Cleanup"] = Step_Cleanup, - }; + public override async Task RunAsync() + { + try + { + Serilog.Log.Information("Begin download and convert Aaxc To {format}", DownloadOptions.OutputFormat); + + //Step 1 + Serilog.Log.Information("Begin Step 1: Get Mp3 Metadata"); + if (await Task.Run(Step_GetMetadata)) + Serilog.Log.Information("Completed Step 1: Get Mp3 Metadata"); + else + { + Serilog.Log.Information("Failed to Complete Step 1: Get Mp3 Metadata"); + return false; + } + + //Step 2 + Serilog.Log.Information("Begin Step 2: Download Audiobook"); + if (await Task.Run(Step_DownloadAudiobookAsSingleFile)) + Serilog.Log.Information("Completed Step 2: Download Audiobook"); + else + { + Serilog.Log.Information("Failed to Complete Step 2: Download Audiobook"); + return false; + } + + //Step 3 + Serilog.Log.Information("Begin Step 3: Cleanup"); + if (await Task.Run(Step_Cleanup)) + Serilog.Log.Information("Completed Step 3: Cleanup"); + else + { + Serilog.Log.Information("Failed to Complete Step 3: Cleanup"); + return false; + } + + Serilog.Log.Information("Completed download and convert Aaxc To {format}", DownloadOptions.OutputFormat); + return true; + } + catch (Exception ex) + { + Serilog.Log.Error(ex, "Error encountered in download and convert Aaxc To {format}", DownloadOptions.OutputFormat); + return false; + } } + public override Task CancelAsync() { IsCanceled = true; diff --git a/Source/FileLiberator/ConvertToMp3.cs b/Source/FileLiberator/ConvertToMp3.cs index bb191986..70940d05 100644 --- a/Source/FileLiberator/ConvertToMp3.cs +++ b/Source/FileLiberator/ConvertToMp3.cs @@ -43,7 +43,7 @@ namespace FileLiberator var proposedMp3Path = Mp3FileName(m4bPath); if (File.Exists(proposedMp3Path) || !File.Exists(m4bPath)) continue; - m4bBook = new Mp4File(m4bPath, FileAccess.Read); + m4bBook = await Task.Run(() => new Mp4File(m4bPath, FileAccess.Read)); m4bBook.ConversionProgressUpdate += M4bBook_ConversionProgressUpdate; fileSize = m4bBook.InputStream.Length; @@ -55,7 +55,7 @@ namespace FileLiberator using var mp3File = File.OpenWrite(Path.GetTempFileName()); var lameConfig = GetLameOptions(Configuration.Instance); - var result = await Task.Run(() => m4bBook.ConvertToMp3(mp3File, lameConfig)); + var result = await m4bBook.ConvertToMp3Async(mp3File, lameConfig); m4bBook.InputStream.Close(); mp3File.Close(); diff --git a/Source/FileLiberator/DownloadDecryptBook.cs b/Source/FileLiberator/DownloadDecryptBook.cs index 4bea8538..f3686ca5 100644 --- a/Source/FileLiberator/DownloadDecryptBook.cs +++ b/Source/FileLiberator/DownloadDecryptBook.cs @@ -130,7 +130,7 @@ namespace FileLiberator abDownloader.FileCreated += (_, path) => OnFileCreated(libraryBook, path); // REAL WORK DONE HERE - var success = await Task.Run(abDownloader.Run); + var success = await abDownloader.RunAsync(); return success; } diff --git a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs index e34e8ae2..dfae3673 100644 --- a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs +++ b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs @@ -42,8 +42,6 @@ namespace LibationWinForms.ProcessQueue public bool Running => !QueueRunner?.IsCompleted ?? false; public ToolStripButton popoutBtn = new(); - private System.Threading.SynchronizationContext syncContext { get; } = System.Threading.SynchronizationContext.Current; - public ProcessQueueControl() { InitializeComponent(); @@ -103,6 +101,7 @@ namespace LibationWinForms.ProcessQueue procs.Add(pbook); } + Serilog.Log.Logger.Information("Queueing {count} books", procs.Count); AddToQueue(procs); } @@ -121,6 +120,7 @@ namespace LibationWinForms.ProcessQueue procs.Add(pbook); } + Serilog.Log.Logger.Information("Queueing {count} books", procs.Count); AddToQueue(procs); } @@ -138,44 +138,57 @@ namespace LibationWinForms.ProcessQueue procs.Add(pbook); } + Serilog.Log.Logger.Information("Queueing {count} books", procs.Count); AddToQueue(procs); } - private void AddToQueue(IEnumerable pbook) { - syncContext.Post(_ => + BeginInvoke(() => { Queue.Enqueue(pbook); if (!Running) QueueRunner = QueueLoop(); - }, - null); + }); } - DateTime StartingTime; private async Task QueueLoop() { - StartingTime = DateTime.Now; - counterTimer.Start(); - - while (Queue.MoveNext()) + try { - var nextBook = Queue.Current; + Serilog.Log.Logger.Information("Begin processing queue"); - var result = await nextBook.ProcessOneAsync(); + StartingTime = DateTime.Now; + counterTimer.Start(); - if (result == ProcessBookResult.ValidationFail) - Queue.ClearCurrent(); - else if (result == ProcessBookResult.FailedAbort) - Queue.ClearQueue(); - else if (result == ProcessBookResult.FailedSkip) - nextBook.LibraryBook.Book.UpdateBookStatus(DataLayer.LiberatedStatus.Error); + while (Queue.MoveNext()) + { + var nextBook = Queue.Current; + + Serilog.Log.Logger.Information("Begin processing queued item. {item_LibraryBook}", nextBook?.LibraryBook); + + var result = await nextBook.ProcessOneAsync(); + + Serilog.Log.Logger.Information("Completed processing queued item: {item_LibraryBook}\r\nResult: {result}", nextBook?.LibraryBook, result); + + if (result == ProcessBookResult.ValidationFail) + Queue.ClearCurrent(); + else if (result == ProcessBookResult.FailedAbort) + Queue.ClearQueue(); + else if (result == ProcessBookResult.FailedSkip) + nextBook.LibraryBook.Book.UpdateBookStatus(DataLayer.LiberatedStatus.Error); + } + Serilog.Log.Logger.Information("Completed processing queue"); + + Queue_CompletedCountChanged(this, 0); + counterTimer.Stop(); + virtualFlowControl2.VirtualControlCount = Queue.Count; + UpdateAllControls(); + } + catch (Exception ex) + { + Serilog.Log.Logger.Error(ex, "An error was encountered while processing queued items"); } - Queue_CompletedCountChanged(this, 0); - counterTimer.Stop(); - virtualFlowControl2.VirtualControlCount = Queue.Count; - UpdateAllControls(); } public void WriteLine(string text) @@ -282,35 +295,41 @@ namespace LibationWinForms.ProcessQueue /// The nme of the property that needs updating. If null, all properties are updated. private void UpdateControl(int queueIndex, string propertyName = null) { - int i = queueIndex - FirstVisible; - - if (i > NumVisible || i < 0) return; - - var proc = Queue[queueIndex]; - - syncContext.Send(_ => + try { - Panels[i].SuspendLayout(); - if (propertyName is null or nameof(proc.Cover)) - Panels[i].SetCover(proc.Cover); - if (propertyName is null or nameof(proc.BookText)) - Panels[i].SetBookInfo(proc.BookText); + int i = queueIndex - FirstVisible; - if (proc.Result != ProcessBookResult.None) + if (i > NumVisible || i < 0) return; + + var proc = Queue[queueIndex]; + + Invoke(() => { - Panels[i].SetResult(proc.Result); - return; - } + Panels[i].SuspendLayout(); + if (propertyName is null or nameof(proc.Cover)) + Panels[i].SetCover(proc.Cover); + if (propertyName is null or nameof(proc.BookText)) + Panels[i].SetBookInfo(proc.BookText); - if (propertyName is null or nameof(proc.Status)) - Panels[i].SetStatus(proc.Status); - if (propertyName is null or nameof(proc.Progress)) - Panels[i].SetProgrss(proc.Progress); - if (propertyName is null or nameof(proc.TimeRemaining)) - Panels[i].SetRemainingTime(proc.TimeRemaining); - Panels[i].ResumeLayout(); - }, - null); + if (proc.Result != ProcessBookResult.None) + { + Panels[i].SetResult(proc.Result); + return; + } + + if (propertyName is null or nameof(proc.Status)) + Panels[i].SetStatus(proc.Status); + if (propertyName is null or nameof(proc.Progress)) + Panels[i].SetProgrss(proc.Progress); + if (propertyName is null or nameof(proc.TimeRemaining)) + Panels[i].SetRemainingTime(proc.TimeRemaining); + Panels[i].ResumeLayout(); + }); + } + catch (Exception ex) + { + Serilog.Log.Logger.Error(ex, "Error updating the queued item's display."); + } } private void UpdateAllControls() @@ -329,37 +348,44 @@ namespace LibationWinForms.ProcessQueue /// The clicked control to update private async void VirtualFlowControl2_ButtonClicked(int queueIndex, string buttonName, ProcessBookControl panelClicked) { - ProcessBook item = Queue[queueIndex]; - if (buttonName == nameof(panelClicked.cancelBtn)) + try { - if (item is not null) - await item.CancelAsync(); - Queue.RemoveQueued(item); - virtualFlowControl2.VirtualControlCount = Queue.Count; + ProcessBook item = Queue[queueIndex]; + if (buttonName == nameof(panelClicked.cancelBtn)) + { + if (item is not null) + await item.CancelAsync(); + Queue.RemoveQueued(item); + virtualFlowControl2.VirtualControlCount = Queue.Count; + } + else if (buttonName == nameof(panelClicked.moveFirstBtn)) + { + Queue.MoveQueuePosition(item, QueuePosition.Fisrt); + UpdateAllControls(); + } + else if (buttonName == nameof(panelClicked.moveUpBtn)) + { + Queue.MoveQueuePosition(item, QueuePosition.OneUp); + UpdateControl(queueIndex); + if (queueIndex > 0) + UpdateControl(queueIndex - 1); + } + else if (buttonName == nameof(panelClicked.moveDownBtn)) + { + Queue.MoveQueuePosition(item, QueuePosition.OneDown); + UpdateControl(queueIndex); + if (queueIndex + 1 < Queue.Count) + UpdateControl(queueIndex + 1); + } + else if (buttonName == nameof(panelClicked.moveLastBtn)) + { + Queue.MoveQueuePosition(item, QueuePosition.Last); + UpdateAllControls(); + } } - else if (buttonName == nameof(panelClicked.moveFirstBtn)) + catch(Exception ex) { - Queue.MoveQueuePosition(item, QueuePosition.Fisrt); - UpdateAllControls(); - } - else if (buttonName == nameof(panelClicked.moveUpBtn)) - { - Queue.MoveQueuePosition(item, QueuePosition.OneUp); - UpdateControl(queueIndex); - if (queueIndex > 0) - UpdateControl(queueIndex - 1); - } - else if (buttonName == nameof(panelClicked.moveDownBtn)) - { - Queue.MoveQueuePosition(item, QueuePosition.OneDown); - UpdateControl(queueIndex); - if (queueIndex + 1 < Queue.Count) - UpdateControl(queueIndex + 1); - } - else if (buttonName == nameof(panelClicked.moveLastBtn)) - { - Queue.MoveQueuePosition(item, QueuePosition.Last); - UpdateAllControls(); + Serilog.Log.Logger.Error(ex, "Error handling button click from queued item"); } }