Add logging and error handling to Process Queue. and Processables

This commit is contained in:
Michael Bucari-Tovo 2022-06-23 15:38:39 -06:00
parent 9ff2a83ba3
commit 314f4850bc
7 changed files with 264 additions and 137 deletions

View File

@ -2,31 +2,65 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using AAXClean; using AAXClean;
using AAXClean.Codecs; using AAXClean.Codecs;
using Dinah.Core;
using Dinah.Core.StepRunner;
using FileManager; using FileManager;
namespace AaxDecrypter namespace AaxDecrypter
{ {
public class AaxcDownloadMultiConverter : AaxcDownloadConvertBase public class AaxcDownloadMultiConverter : AaxcDownloadConvertBase
{ {
protected override StepSequence Steps { get; }
private static TimeSpan minChapterLength { get; } = TimeSpan.FromSeconds(3); private static TimeSpan minChapterLength { get; } = TimeSpan.FromSeconds(3);
private List<string> multiPartFilePaths { get; } = new List<string>(); private List<string> multiPartFilePaths { get; } = new List<string>();
public AaxcDownloadMultiConverter(string outFileName, string cacheDirectory, IDownloadOptions dlOptions) public AaxcDownloadMultiConverter(string outFileName, string cacheDirectory, IDownloadOptions dlOptions)
: base(outFileName, cacheDirectory, dlOptions) : base(outFileName, cacheDirectory, dlOptions) { }
{
Steps = new StepSequence
{
Name = "Download and Convert Aaxc To " + DownloadOptions.OutputFormat,
["Step 1: Get Aaxc Metadata"] = Step_GetMetadata, public override async Task<bool> RunAsync()
["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsMultipleFilesPerChapter, {
["Step 3: Cleanup"] = Step_Cleanup, 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. 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<bool> Step_DownloadAudiobookAsMultipleFilesPerChapter()
{ {
var zeroProgress = Step_DownloadAudiobook_Start(); 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; AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
if (DownloadOptions.OutputFormat == OutputFormat.M4b) if (DownloadOptions.OutputFormat == OutputFormat.M4b)
result = ConvertToMultiMp4a(splitChapters); result = await ConvertToMultiMp4a(splitChapters);
else else
result = ConvertToMultiMp3(splitChapters); result = await ConvertToMultiMp3(splitChapters);
AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate; AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate;
Step_DownloadAudiobook_End(zeroProgress); 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; return result == ConversionResult.NoErrorsDetected;
} }
private ConversionResult ConvertToMultiMp4a(ChapterInfo splitChapters) private Task<ConversionResult> ConvertToMultiMp4a(ChapterInfo splitChapters)
{ {
var chapterCount = 0; var chapterCount = 0;
return AaxFile.ConvertToMultiMp4a return AaxFile.ConvertToMultiMp4aAsync
( (
splitChapters, splitChapters,
newSplitCallback => Callback(++chapterCount, splitChapters, newSplitCallback), 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<ConversionResult> ConvertToMultiMp3(ChapterInfo splitChapters)
{ {
var chapterCount = 0; var chapterCount = 0;
return AaxFile.ConvertToMultiMp3 return AaxFile.ConvertToMultiMp3Async
( (
splitChapters, splitChapters,
newSplitCallback => Callback(++chapterCount, splitChapters, newSplitCallback), newSplitCallback => Callback(++chapterCount, splitChapters, newSplitCallback),

View File

@ -1,31 +1,74 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks;
using AAXClean; using AAXClean;
using AAXClean.Codecs; using AAXClean.Codecs;
using Dinah.Core.StepRunner;
using FileManager; using FileManager;
namespace AaxDecrypter namespace AaxDecrypter
{ {
public class AaxcDownloadSingleConverter : AaxcDownloadConvertBase public class AaxcDownloadSingleConverter : AaxcDownloadConvertBase
{ {
protected override StepSequence Steps { get; }
public AaxcDownloadSingleConverter(string outFileName, string cacheDirectory, IDownloadOptions dlOptions) public AaxcDownloadSingleConverter(string outFileName, string cacheDirectory, IDownloadOptions dlOptions)
: base(outFileName, cacheDirectory, dlOptions) : base(outFileName, cacheDirectory, dlOptions) { }
{
Steps = new StepSequence
{
Name = "Download and Convert Aaxc To " + DownloadOptions.OutputFormat,
["Step 1: Get Aaxc Metadata"] = Step_GetMetadata, public override async Task<bool> RunAsync()
["Step 2: Download Decrypted Audiobook"] = Step_DownloadAudiobookAsSingleFile, {
["Step 3: Create Cue"] = Step_CreateCue, try
["Step 4: Cleanup"] = Step_Cleanup, {
}; 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;
} }
private bool Step_DownloadAudiobookAsSingleFile() //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 async Task<bool> Step_DownloadAudiobookAsSingleFile()
{ {
var zeroProgress = Step_DownloadAudiobook_Start(); var zeroProgress = Step_DownloadAudiobook_Start();
@ -37,8 +80,8 @@ namespace AaxDecrypter
AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate; AaxFile.ConversionProgressUpdate += AaxFile_ConversionProgressUpdate;
var decryptionResult var decryptionResult
= DownloadOptions.OutputFormat == OutputFormat.M4b = DownloadOptions.OutputFormat == OutputFormat.M4b
? AaxFile.ConvertToMp4a(outputFile, DownloadOptions.ChapterInfo, DownloadOptions.TrimOutputToChapterLength) ? await AaxFile.ConvertToMp4aAsync(outputFile, DownloadOptions.ChapterInfo, DownloadOptions.TrimOutputToChapterLength)
: AaxFile.ConvertToMp3(outputFile, DownloadOptions.LameConfig, DownloadOptions.ChapterInfo, DownloadOptions.TrimOutputToChapterLength); : await AaxFile.ConvertToMp3Async(outputFile, DownloadOptions.LameConfig, DownloadOptions.ChapterInfo, DownloadOptions.TrimOutputToChapterLength);
AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate; AaxFile.ConversionProgressUpdate -= AaxFile_ConversionProgressUpdate;
DownloadOptions.ChapterInfo = AaxFile.Chapters; DownloadOptions.ChapterInfo = AaxFile.Chapters;

View File

@ -4,7 +4,6 @@ using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dinah.Core; using Dinah.Core;
using Dinah.Core.Net.Http; using Dinah.Core.Net.Http;
using Dinah.Core.StepRunner;
using FileManager; using FileManager;
namespace AaxDecrypter 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 // 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 void SetOutputFileName(string newOutputFileName) => OutputFileName = newOutputFileName;
protected abstract StepSequence Steps { get; }
private NetworkFileStreamPersister nfsPersister; private NetworkFileStreamPersister nfsPersister;
private string jsonDownloadState { get; } private string jsonDownloadState { get; }
@ -64,15 +62,7 @@ namespace AaxDecrypter
OnRetrievedCoverArt(coverArt); OnRetrievedCoverArt(coverArt);
} }
public bool Run() public abstract Task<bool> RunAsync();
{
var (IsSuccess, _) = Steps.Run();
if (!IsSuccess)
Serilog.Log.Logger.Error("Conversion failed");
return IsSuccess;
}
protected void OnRetrievedTitle(string title) protected void OnRetrievedTitle(string title)
=> RetrievedTitle?.Invoke(this, title); => RetrievedTitle?.Invoke(this, title);

View File

@ -2,29 +2,63 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dinah.Core.Net.Http; using Dinah.Core.Net.Http;
using Dinah.Core.StepRunner;
using FileManager; using FileManager;
namespace AaxDecrypter namespace AaxDecrypter
{ {
public class UnencryptedAudiobookDownloader : AudiobookDownloadBase public class UnencryptedAudiobookDownloader : AudiobookDownloadBase
{ {
protected override StepSequence Steps { get; }
public UnencryptedAudiobookDownloader(string outFileName, string cacheDirectory, IDownloadOptions dlLic) public UnencryptedAudiobookDownloader(string outFileName, string cacheDirectory, IDownloadOptions dlLic)
: base(outFileName, cacheDirectory, dlLic) : base(outFileName, cacheDirectory, dlLic) { }
{
Steps = new StepSequence
{
Name = "Download Mp3 Audiobook",
["Step 1: Get Mp3 Metadata"] = Step_GetMetadata, public override async Task<bool> RunAsync()
["Step 2: Download Audiobook"] = Step_DownloadAudiobookAsSingleFile, {
["Step 3: Create Cue"] = Step_CreateCue, try
["Step 4: Cleanup"] = Step_Cleanup, {
}; 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() public override Task CancelAsync()
{ {
IsCanceled = true; IsCanceled = true;

View File

@ -43,7 +43,7 @@ namespace FileLiberator
var proposedMp3Path = Mp3FileName(m4bPath); var proposedMp3Path = Mp3FileName(m4bPath);
if (File.Exists(proposedMp3Path) || !File.Exists(m4bPath)) continue; 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; m4bBook.ConversionProgressUpdate += M4bBook_ConversionProgressUpdate;
fileSize = m4bBook.InputStream.Length; fileSize = m4bBook.InputStream.Length;
@ -55,7 +55,7 @@ namespace FileLiberator
using var mp3File = File.OpenWrite(Path.GetTempFileName()); using var mp3File = File.OpenWrite(Path.GetTempFileName());
var lameConfig = GetLameOptions(Configuration.Instance); var lameConfig = GetLameOptions(Configuration.Instance);
var result = await Task.Run(() => m4bBook.ConvertToMp3(mp3File, lameConfig)); var result = await m4bBook.ConvertToMp3Async(mp3File, lameConfig);
m4bBook.InputStream.Close(); m4bBook.InputStream.Close();
mp3File.Close(); mp3File.Close();

View File

@ -130,7 +130,7 @@ namespace FileLiberator
abDownloader.FileCreated += (_, path) => OnFileCreated(libraryBook, path); abDownloader.FileCreated += (_, path) => OnFileCreated(libraryBook, path);
// REAL WORK DONE HERE // REAL WORK DONE HERE
var success = await Task.Run(abDownloader.Run); var success = await abDownloader.RunAsync();
return success; return success;
} }

View File

@ -42,8 +42,6 @@ namespace LibationWinForms.ProcessQueue
public bool Running => !QueueRunner?.IsCompleted ?? false; public bool Running => !QueueRunner?.IsCompleted ?? false;
public ToolStripButton popoutBtn = new(); public ToolStripButton popoutBtn = new();
private System.Threading.SynchronizationContext syncContext { get; } = System.Threading.SynchronizationContext.Current;
public ProcessQueueControl() public ProcessQueueControl()
{ {
InitializeComponent(); InitializeComponent();
@ -103,6 +101,7 @@ namespace LibationWinForms.ProcessQueue
procs.Add(pbook); procs.Add(pbook);
} }
Serilog.Log.Logger.Information("Queueing {count} books", procs.Count);
AddToQueue(procs); AddToQueue(procs);
} }
@ -121,6 +120,7 @@ namespace LibationWinForms.ProcessQueue
procs.Add(pbook); procs.Add(pbook);
} }
Serilog.Log.Logger.Information("Queueing {count} books", procs.Count);
AddToQueue(procs); AddToQueue(procs);
} }
@ -138,24 +138,26 @@ namespace LibationWinForms.ProcessQueue
procs.Add(pbook); procs.Add(pbook);
} }
Serilog.Log.Logger.Information("Queueing {count} books", procs.Count);
AddToQueue(procs); AddToQueue(procs);
} }
private void AddToQueue(IEnumerable<ProcessBook> pbook) private void AddToQueue(IEnumerable<ProcessBook> pbook)
{ {
syncContext.Post(_ => BeginInvoke(() =>
{ {
Queue.Enqueue(pbook); Queue.Enqueue(pbook);
if (!Running) if (!Running)
QueueRunner = QueueLoop(); QueueRunner = QueueLoop();
}, });
null);
} }
DateTime StartingTime; DateTime StartingTime;
private async Task QueueLoop() private async Task QueueLoop()
{ {
try
{
Serilog.Log.Logger.Information("Begin processing queue");
StartingTime = DateTime.Now; StartingTime = DateTime.Now;
counterTimer.Start(); counterTimer.Start();
@ -163,8 +165,12 @@ namespace LibationWinForms.ProcessQueue
{ {
var nextBook = Queue.Current; var nextBook = Queue.Current;
Serilog.Log.Logger.Information("Begin processing queued item. {item_LibraryBook}", nextBook?.LibraryBook);
var result = await nextBook.ProcessOneAsync(); 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) if (result == ProcessBookResult.ValidationFail)
Queue.ClearCurrent(); Queue.ClearCurrent();
else if (result == ProcessBookResult.FailedAbort) else if (result == ProcessBookResult.FailedAbort)
@ -172,11 +178,18 @@ namespace LibationWinForms.ProcessQueue
else if (result == ProcessBookResult.FailedSkip) else if (result == ProcessBookResult.FailedSkip)
nextBook.LibraryBook.Book.UpdateBookStatus(DataLayer.LiberatedStatus.Error); nextBook.LibraryBook.Book.UpdateBookStatus(DataLayer.LiberatedStatus.Error);
} }
Serilog.Log.Logger.Information("Completed processing queue");
Queue_CompletedCountChanged(this, 0); Queue_CompletedCountChanged(this, 0);
counterTimer.Stop(); counterTimer.Stop();
virtualFlowControl2.VirtualControlCount = Queue.Count; virtualFlowControl2.VirtualControlCount = Queue.Count;
UpdateAllControls(); UpdateAllControls();
} }
catch (Exception ex)
{
Serilog.Log.Logger.Error(ex, "An error was encountered while processing queued items");
}
}
public void WriteLine(string text) public void WriteLine(string text)
{ {
@ -281,6 +294,8 @@ namespace LibationWinForms.ProcessQueue
/// <param name="queueIndex">index of the <see cref="ProcessBook"/> within the <see cref="Queue"/></param> /// <param name="queueIndex">index of the <see cref="ProcessBook"/> within the <see cref="Queue"/></param>
/// <param name="propertyName">The nme of the property that needs updating. If null, all properties are updated.</param> /// <param name="propertyName">The nme of the property that needs updating. If null, all properties are updated.</param>
private void UpdateControl(int queueIndex, string propertyName = null) private void UpdateControl(int queueIndex, string propertyName = null)
{
try
{ {
int i = queueIndex - FirstVisible; int i = queueIndex - FirstVisible;
@ -288,7 +303,7 @@ namespace LibationWinForms.ProcessQueue
var proc = Queue[queueIndex]; var proc = Queue[queueIndex];
syncContext.Send(_ => Invoke(() =>
{ {
Panels[i].SuspendLayout(); Panels[i].SuspendLayout();
if (propertyName is null or nameof(proc.Cover)) if (propertyName is null or nameof(proc.Cover))
@ -309,8 +324,12 @@ namespace LibationWinForms.ProcessQueue
if (propertyName is null or nameof(proc.TimeRemaining)) if (propertyName is null or nameof(proc.TimeRemaining))
Panels[i].SetRemainingTime(proc.TimeRemaining); Panels[i].SetRemainingTime(proc.TimeRemaining);
Panels[i].ResumeLayout(); Panels[i].ResumeLayout();
}, });
null); }
catch (Exception ex)
{
Serilog.Log.Logger.Error(ex, "Error updating the queued item's display.");
}
} }
private void UpdateAllControls() private void UpdateAllControls()
@ -328,6 +347,8 @@ namespace LibationWinForms.ProcessQueue
/// <param name="queueIndex">index of the <see cref="ProcessBook"/> within <see cref="Queue"/></param> /// <param name="queueIndex">index of the <see cref="ProcessBook"/> within <see cref="Queue"/></param>
/// <param name="panelClicked">The clicked control to update</param> /// <param name="panelClicked">The clicked control to update</param>
private async void VirtualFlowControl2_ButtonClicked(int queueIndex, string buttonName, ProcessBookControl panelClicked) private async void VirtualFlowControl2_ButtonClicked(int queueIndex, string buttonName, ProcessBookControl panelClicked)
{
try
{ {
ProcessBook item = Queue[queueIndex]; ProcessBook item = Queue[queueIndex];
if (buttonName == nameof(panelClicked.cancelBtn)) if (buttonName == nameof(panelClicked.cancelBtn))
@ -362,6 +383,11 @@ namespace LibationWinForms.ProcessQueue
UpdateAllControls(); UpdateAllControls();
} }
} }
catch(Exception ex)
{
Serilog.Log.Logger.Error(ex, "Error handling button click from queued item");
}
}
/// <summary> /// <summary>
/// View needs updating /// View needs updating