Saved
This commit is contained in:
parent
676af0210b
commit
eeb4f4681a
10 changed files with 1502 additions and 752 deletions
|
|
@ -5,6 +5,7 @@ using LibationFileManager;
|
|||
using LibationWinForms.BookLiberation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
|
@ -21,6 +22,7 @@ namespace LibationWinForms.ProcessQueue
|
|||
FailedSkip,
|
||||
FailedAbort
|
||||
}
|
||||
|
||||
public enum ProcessBookStatus
|
||||
{
|
||||
Queued,
|
||||
|
|
@ -30,98 +32,56 @@ namespace LibationWinForms.ProcessQueue
|
|||
Failed
|
||||
}
|
||||
|
||||
internal enum QueuePosition
|
||||
{
|
||||
Absent,
|
||||
Completed,
|
||||
Current,
|
||||
Fisrt,
|
||||
OneUp,
|
||||
OneDown,
|
||||
Last
|
||||
}
|
||||
internal enum QueuePositionRequest
|
||||
{
|
||||
Fisrt,
|
||||
OneUp,
|
||||
OneDown,
|
||||
Last
|
||||
}
|
||||
|
||||
internal delegate QueuePosition ProcessControlReorderHandler(ProcessBook sender, QueuePositionRequest arg);
|
||||
internal delegate void ProcessControlEventArgs<T>(ProcessBook sender, T arg);
|
||||
internal delegate void ProcessControlEventArgs(ProcessBook sender, EventArgs arg);
|
||||
|
||||
internal class ProcessBook
|
||||
public class ProcessBook
|
||||
{
|
||||
public event EventHandler Completed;
|
||||
public event ProcessControlEventArgs Cancelled;
|
||||
public event ProcessControlReorderHandler RequestMove;
|
||||
public GridEntry Entry { get; }
|
||||
//public ProcessBookControl BookControl { get; }
|
||||
public event EventHandler DataAvailable;
|
||||
|
||||
private Func<Processable> _makeFirstProc;
|
||||
private Processable _firstProcessable;
|
||||
private bool cancelled = false;
|
||||
private bool running = false;
|
||||
public Processable FirstProcessable => _firstProcessable ??= _makeFirstProc?.Invoke();
|
||||
public Processable CurrentProcessable => _currentProcessable ??= Processes.Dequeue().Invoke();
|
||||
public ProcessBookResult Result { get; private set; } = ProcessBookResult.None;
|
||||
public ProcessBookStatus Status { get; private set; } = ProcessBookStatus.Queued;
|
||||
public string BookText { get; private set; }
|
||||
public Image Cover { get; private set; }
|
||||
public int Progress { get; private set; }
|
||||
public TimeSpan TimeRemaining { get; private set; }
|
||||
public LibraryBook LibraryBook { get; }
|
||||
|
||||
private Processable _currentProcessable;
|
||||
private Func<byte[]> GetCoverArtDelegate;
|
||||
private readonly Queue<Func<Processable>> Processes = new();
|
||||
private readonly LogMe Logger;
|
||||
|
||||
LogMe Logger;
|
||||
|
||||
public ProcessBook(GridEntry entry, LogMe logme)
|
||||
public ProcessBook(LibraryBook libraryBook, Image coverImage, LogMe logme)
|
||||
{
|
||||
Entry = entry;
|
||||
//BookControl = new ProcessBookControl(Entry.Title, Entry.Cover);
|
||||
//BookControl.CancelAction = Cancel;
|
||||
//BookControl.RequestMoveAction = MoveRequested;
|
||||
LibraryBook = libraryBook;
|
||||
Cover = coverImage;
|
||||
Logger = logme;
|
||||
}
|
||||
|
||||
public QueuePosition? MoveRequested(QueuePositionRequest requestedPosition)
|
||||
{
|
||||
return RequestMove?.Invoke(this, requestedPosition);
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
cancelled = true;
|
||||
try
|
||||
{
|
||||
if (FirstProcessable is AudioDecodable audioDecodable)
|
||||
audioDecodable.Cancel();
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "Error while cancelling");
|
||||
}
|
||||
|
||||
if (!running)
|
||||
Cancelled?.Invoke(this, EventArgs.Empty);
|
||||
title = LibraryBook.Book.Title;
|
||||
authorNames = LibraryBook.Book.AuthorNames();
|
||||
narratorNames = LibraryBook.Book.NarratorNames();
|
||||
BookText = $"{title}\r\nBy {authorNames}\r\nNarrated by {narratorNames}";
|
||||
}
|
||||
|
||||
public async Task<ProcessBookResult> ProcessOneAsync()
|
||||
{
|
||||
running = true;
|
||||
ProcessBookResult result = ProcessBookResult.None;
|
||||
try
|
||||
{
|
||||
LinkProcessable(FirstProcessable);
|
||||
|
||||
var statusHandler = await FirstProcessable.ProcessSingleAsync(Entry.LibraryBook, validate: true);
|
||||
LinkProcessable(CurrentProcessable);
|
||||
|
||||
var statusHandler = await CurrentProcessable.ProcessSingleAsync(LibraryBook, validate: true);
|
||||
|
||||
if (statusHandler.IsSuccess)
|
||||
return result = ProcessBookResult.Success;
|
||||
else if (cancelled)
|
||||
return Result = ProcessBookResult.Success;
|
||||
else if (statusHandler.Errors.Contains("Cancelled"))
|
||||
{
|
||||
Logger.Info($"Process was cancelled {Entry.LibraryBook.Book}");
|
||||
return result = ProcessBookResult.Cancelled;
|
||||
Logger.Info($"Process was cancelled {LibraryBook.Book}");
|
||||
return Result = ProcessBookResult.Cancelled;
|
||||
}
|
||||
else if (statusHandler.Errors.Contains("Validation failed"))
|
||||
{
|
||||
Logger.Info($"Validation failed {Entry.LibraryBook.Book}");
|
||||
return result = ProcessBookResult.ValidationFail;
|
||||
Logger.Info($"Validation failed {LibraryBook.Book}");
|
||||
return Result = ProcessBookResult.ValidationFail;
|
||||
}
|
||||
|
||||
foreach (var errorMessage in statusHandler.Errors)
|
||||
|
|
@ -133,51 +93,189 @@ namespace LibationWinForms.ProcessQueue
|
|||
}
|
||||
finally
|
||||
{
|
||||
if (result == ProcessBookResult.None)
|
||||
result = showRetry(Entry.LibraryBook);
|
||||
if (Result == ProcessBookResult.None)
|
||||
Result = showRetry(LibraryBook);
|
||||
|
||||
//BookControl.SetResult(result);
|
||||
Status = Result switch
|
||||
{
|
||||
ProcessBookResult.Success => ProcessBookStatus.Completed,
|
||||
ProcessBookResult.Cancelled => ProcessBookStatus.Cancelled,
|
||||
ProcessBookResult.FailedRetry => ProcessBookStatus.Queued,
|
||||
_ => ProcessBookStatus.Failed,
|
||||
};
|
||||
|
||||
DataAvailable?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
return result;
|
||||
return Result;
|
||||
}
|
||||
|
||||
public void AddPdfProcessable() => AddProcessable<DownloadPdf>();
|
||||
public void AddDownloadDecryptProcessable() => AddProcessable<DownloadDecryptBook>();
|
||||
public void AddConvertMp3Processable() => AddProcessable<ConvertToMp3>();
|
||||
public void Cancel()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CurrentProcessable is AudioDecodable audioDecodable)
|
||||
{
|
||||
//There's some threadding bug that causes this to hang if executed synchronously.
|
||||
Task.Run(audioDecodable.Cancel);
|
||||
DataAvailable?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "Error while cancelling");
|
||||
}
|
||||
}
|
||||
|
||||
public void AddDownloadPdf() => AddProcessable<DownloadPdf>();
|
||||
public void AddDownloadDecryptBook() => AddProcessable<DownloadDecryptBook>();
|
||||
public void AddConvertToMp3() => AddProcessable<ConvertToMp3>();
|
||||
|
||||
private void AddProcessable<T>() where T : Processable, new()
|
||||
{
|
||||
if (FirstProcessable == null)
|
||||
{
|
||||
_makeFirstProc = () => new T();
|
||||
}
|
||||
else
|
||||
Processes.Enqueue(() => new T());
|
||||
Processes.Enqueue(() => new T());
|
||||
}
|
||||
public override string ToString() => LibraryBook.ToString();
|
||||
|
||||
#region Subscribers and Unsubscribers
|
||||
|
||||
private void LinkProcessable(Processable strProc)
|
||||
{
|
||||
strProc.Begin += Processable_Begin;
|
||||
strProc.Completed += Processable_Completed;
|
||||
strProc.StreamingProgressChanged += Streamable_StreamingProgressChanged;
|
||||
strProc.StreamingTimeRemaining += Streamable_StreamingTimeRemaining;
|
||||
|
||||
if (strProc is AudioDecodable audioDecodable)
|
||||
{
|
||||
audioDecodable.RequestCoverArt += AudioDecodable_RequestCoverArt;
|
||||
audioDecodable.TitleDiscovered += AudioDecodable_TitleDiscovered;
|
||||
audioDecodable.AuthorsDiscovered += AudioDecodable_AuthorsDiscovered;
|
||||
audioDecodable.NarratorsDiscovered += AudioDecodable_NarratorsDiscovered;
|
||||
audioDecodable.CoverImageDiscovered += AudioDecodable_CoverImageDiscovered;
|
||||
}
|
||||
}
|
||||
|
||||
private void UnlinkProcessable(Processable strProc)
|
||||
{
|
||||
strProc.Begin -= Processable_Begin;
|
||||
strProc.Completed -= Processable_Completed;
|
||||
strProc.StreamingProgressChanged -= Streamable_StreamingProgressChanged;
|
||||
strProc.StreamingTimeRemaining -= Streamable_StreamingTimeRemaining;
|
||||
|
||||
if (strProc is AudioDecodable audioDecodable)
|
||||
{
|
||||
audioDecodable.RequestCoverArt -= AudioDecodable_RequestCoverArt;
|
||||
audioDecodable.TitleDiscovered -= AudioDecodable_TitleDiscovered;
|
||||
audioDecodable.AuthorsDiscovered -= AudioDecodable_AuthorsDiscovered;
|
||||
audioDecodable.NarratorsDiscovered -= AudioDecodable_NarratorsDiscovered;
|
||||
audioDecodable.CoverImageDiscovered -= AudioDecodable_CoverImageDiscovered;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AudioDecodable event handlers
|
||||
|
||||
private string title;
|
||||
private string authorNames;
|
||||
private string narratorNames;
|
||||
private void AudioDecodable_TitleDiscovered(object sender, string title)
|
||||
{
|
||||
this.title = title;
|
||||
updateBookInfo();
|
||||
}
|
||||
|
||||
private void AudioDecodable_AuthorsDiscovered(object sender, string authors)
|
||||
{
|
||||
authorNames = authors;
|
||||
updateBookInfo();
|
||||
}
|
||||
|
||||
private void AudioDecodable_NarratorsDiscovered(object sender, string narrators)
|
||||
{
|
||||
narratorNames = narrators;
|
||||
updateBookInfo();
|
||||
}
|
||||
|
||||
private void updateBookInfo()
|
||||
{
|
||||
BookText = $"{title}\r\nBy {authorNames}\r\nNarrated by {narratorNames}";
|
||||
DataAvailable?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void AudioDecodable_RequestCoverArt(object sender, Action<byte[]> setCoverArtDelegate)
|
||||
{
|
||||
byte[] coverData = GetCoverArtDelegate();
|
||||
setCoverArtDelegate(coverData);
|
||||
AudioDecodable_CoverImageDiscovered(this, coverData);
|
||||
}
|
||||
|
||||
private void AudioDecodable_CoverImageDiscovered(object sender, byte[] coverArt)
|
||||
{
|
||||
Cover = Dinah.Core.Drawing.ImageReader.ToImage(coverArt);
|
||||
DataAvailable?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Streamable event handlers
|
||||
private void Streamable_StreamingTimeRemaining(object sender, TimeSpan timeRemaining)
|
||||
{
|
||||
TimeRemaining = timeRemaining;
|
||||
DataAvailable?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void Streamable_StreamingProgressChanged(object sender, Dinah.Core.Net.Http.DownloadProgress downloadProgress)
|
||||
{
|
||||
if (!downloadProgress.ProgressPercentage.HasValue)
|
||||
return;
|
||||
|
||||
if (downloadProgress.ProgressPercentage == 0)
|
||||
TimeRemaining = TimeSpan.Zero;
|
||||
else
|
||||
Progress = (int)downloadProgress.ProgressPercentage;
|
||||
|
||||
DataAvailable?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Processable event handlers
|
||||
|
||||
private void Processable_Begin(object sender, LibraryBook libraryBook)
|
||||
{
|
||||
//BookControl.RegisterFileLiberator((Processable)sender, Logger);
|
||||
//BookControl.Processable_Begin(sender, libraryBook);
|
||||
Status = ProcessBookStatus.Working;
|
||||
|
||||
Logger.Info($"{Environment.NewLine}{((Processable)sender).Name} Step, Begin: {libraryBook.Book}");
|
||||
|
||||
GetCoverArtDelegate = () => PictureStorage.GetPictureSynchronously(
|
||||
new PictureDefinition(
|
||||
libraryBook.Book.PictureId,
|
||||
PictureSize._500x500));
|
||||
|
||||
title = libraryBook.Book.Title;
|
||||
authorNames = libraryBook.Book.AuthorNames();
|
||||
narratorNames = libraryBook.Book.NarratorNames();
|
||||
Cover = Dinah.Core.Drawing.ImageReader.ToImage(PictureStorage.GetPicture(
|
||||
new PictureDefinition(
|
||||
libraryBook.Book.PictureId,
|
||||
PictureSize._80x80)).bytes);
|
||||
|
||||
updateBookInfo();
|
||||
}
|
||||
|
||||
private async void Processable_Completed(object sender, LibraryBook e)
|
||||
private async void Processable_Completed(object sender, LibraryBook libraryBook)
|
||||
{
|
||||
((Processable)sender).Begin -= Processable_Begin;
|
||||
|
||||
Logger.Info($"{((Processable)sender).Name} Step, Completed: {libraryBook.Book}");
|
||||
UnlinkProcessable((Processable)sender);
|
||||
|
||||
if (Processes.Count > 0)
|
||||
{
|
||||
var nextProcessFunc = Processes.Dequeue();
|
||||
var nextProcess = nextProcessFunc();
|
||||
LinkProcessable(nextProcess);
|
||||
var result = await nextProcess.ProcessSingleAsync(e, true);
|
||||
_currentProcessable = null;
|
||||
LinkProcessable(CurrentProcessable);
|
||||
var result = await CurrentProcessable.ProcessSingleAsync(libraryBook, validate: true);
|
||||
|
||||
if (result.HasErrors)
|
||||
{
|
||||
|
|
@ -185,16 +283,18 @@ namespace LibationWinForms.ProcessQueue
|
|||
Logger.Error(errorMessage);
|
||||
|
||||
Completed?.Invoke(this, EventArgs.Empty);
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Completed?.Invoke(this, EventArgs.Empty);
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Failure Handler
|
||||
|
||||
private ProcessBookResult showRetry(LibraryBook libraryBook)
|
||||
{
|
||||
Logger.Error("ERROR. All books have not been processed. Most recent book: processing failed");
|
||||
|
|
@ -247,7 +347,7 @@ $@" Title: {libraryBook.Book.Title}
|
|||
}
|
||||
|
||||
|
||||
protected string SkipDialogText => @"
|
||||
private string SkipDialogText => @"
|
||||
An error occurred while trying to process this book.
|
||||
{0}
|
||||
|
||||
|
|
@ -257,8 +357,10 @@ An error occurred while trying to process this book.
|
|||
|
||||
- IGNORE: Permanently ignore this book. Continue processing books. (Will not try this book again later.)
|
||||
".Trim();
|
||||
protected MessageBoxButtons SkipDialogButtons => MessageBoxButtons.AbortRetryIgnore;
|
||||
protected MessageBoxDefaultButton SkipDialogDefaultButton => MessageBoxDefaultButton.Button1;
|
||||
protected DialogResult SkipResult => DialogResult.Ignore;
|
||||
private MessageBoxButtons SkipDialogButtons => MessageBoxButtons.AbortRetryIgnore;
|
||||
private MessageBoxDefaultButton SkipDialogDefaultButton => MessageBoxDefaultButton.Button1;
|
||||
private DialogResult SkipResult => DialogResult.Ignore;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue