Code cleanup and refactoring for clarity

This commit is contained in:
Michael Bucari-Tovo
2025-07-16 11:28:37 -06:00
committed by MBucari
parent a3734c76b1
commit 4b7939541a
10 changed files with 179 additions and 278 deletions

View File

@@ -1,9 +1,7 @@
using DataLayer;
using LibationFileManager;
using LibationUiBase.Forms;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using ApplicationServices;
using System.Threading.Tasks;
@@ -14,24 +12,19 @@ namespace LibationUiBase.ProcessQueue;
public abstract class ProcessQueueViewModelBase : ReactiveObject, ILogForm
{
public abstract void WriteLine(string text);
protected abstract ProcessBookViewModelBase CreateNewProcessBook(LibraryBook libraryBook);
protected abstract ProcessBookViewModelBase CreateNewBook(LibraryBook libraryBook);
public ObservableCollection<LogEntry> LogEntries { get; } = new();
public TrackedQueue<ProcessBookViewModelBase> Queue { get; }
public ProcessBookViewModelBase? SelectedItem { get; set; }
public Task? QueueRunner { get; private set; }
public bool Running => !QueueRunner?.IsCompleted ?? false;
protected readonly LogMe Logger;
protected LogMe Logger { get; }
public ProcessQueueViewModelBase(ICollection<ProcessBookViewModelBase>? underlyingList)
{
Logger = LogMe.RegisterForm(this);
Queue = new(underlyingList);
Queue.QueuededCountChanged += Queue_QueuededCountChanged;
Queue.QueuedCountChanged += Queue_QueuedCountChanged;
Queue.CompletedCountChanged += Queue_CompletedCountChanged;
SpeedLimit = Configuration.Instance.DownloadSpeedLimit / 1024m / 1024;
}
private int _completedCount;
@@ -39,7 +32,6 @@ public abstract class ProcessQueueViewModelBase : ReactiveObject, ILogForm
private int _queuedCount;
private string? _runningTime;
private bool _progressBarVisible;
private decimal _speedLimit;
public int CompletedCount { get => _completedCount; private set { RaiseAndSetIfChanged(ref _completedCount, value); RaisePropertyChanged(nameof(AnyCompleted)); } }
public int QueuedCount { get => _queuedCount; private set { this.RaiseAndSetIfChanged(ref _queuedCount, value); RaisePropertyChanged(nameof(AnyQueued)); } }
@@ -51,37 +43,6 @@ public abstract class ProcessQueueViewModelBase : ReactiveObject, ILogForm
public bool AnyErrors => ErrorCount > 0;
public double Progress => 100d * Queue.Completed.Count / Queue.Count;
public decimal SpeedLimit
{
get
{
return _speedLimit;
}
set
{
var newValue = Math.Min(999 * 1024 * 1024, (long)(value * 1024 * 1024));
var config = Configuration.Instance;
config.DownloadSpeedLimit = newValue;
_speedLimit
= config.DownloadSpeedLimit <= newValue ? value
: value == 0.01m ? config.DownloadSpeedLimit / 1024m / 1024
: 0;
config.DownloadSpeedLimit = (long)(_speedLimit * 1024 * 1024);
SpeedLimitIncrement = _speedLimit > 100 ? 10
: _speedLimit > 10 ? 1
: _speedLimit > 1 ? 0.1m
: 0.01m;
RaisePropertyChanged(nameof(SpeedLimitIncrement));
RaisePropertyChanged(nameof(SpeedLimit));
}
}
public decimal SpeedLimitIncrement { get; private set; }
private void Queue_CompletedCountChanged(object? sender, int e)
{
int errCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.FailedAbort or ProcessBookResult.FailedSkip or ProcessBookResult.FailedRetry or ProcessBookResult.ValidationFail);
@@ -91,7 +52,8 @@ public abstract class ProcessQueueViewModelBase : ReactiveObject, ILogForm
CompletedCount = completeCount;
RaisePropertyChanged(nameof(Progress));
}
private void Queue_QueuededCountChanged(object? sender, int cueCount)
private void Queue_QueuedCountChanged(object? sender, int cueCount)
{
QueuedCount = cueCount;
RaisePropertyChanged(nameof(Progress));
@@ -101,7 +63,7 @@ public abstract class ProcessQueueViewModelBase : ReactiveObject, ILogForm
public bool QueueDownloadPdf(IList<LibraryBook> libraryBooks)
{
var needsPdf = libraryBooks.Where(lb => !lb.AbsentFromLastScan && lb.Book.UserDefinedItem.PdfStatus is LiberatedStatus.NotLiberated).ToArray();
var needsPdf = libraryBooks.Where(lb => lb.NeedsPdfDownload()).ToArray();
if (needsPdf.Length > 0)
{
Serilog.Log.Logger.Information("Begin download {count} pdfs", needsPdf.Length);
@@ -132,14 +94,14 @@ public abstract class ProcessQueueViewModelBase : ReactiveObject, ILogForm
if (item.AbsentFromLastScan)
return false;
else if (item.Book.UserDefinedItem.BookStatus is LiberatedStatus.NotLiberated or LiberatedStatus.PartialDownload)
else if (item.NeedsBookDownload())
{
RemoveCompleted(item);
Serilog.Log.Logger.Information("Begin single library book backup of {libraryBook}", item);
AddDownloadDecrypt([item]);
return true;
}
else if (item.Book.UserDefinedItem.PdfStatus is LiberatedStatus.NotLiberated)
else if (item.NeedsPdfDownload())
{
RemoveCompleted(item);
Serilog.Log.Logger.Information("Begin single pdf backup of {libraryBook}", item);
@@ -149,10 +111,7 @@ public abstract class ProcessQueueViewModelBase : ReactiveObject, ILogForm
}
else
{
var toLiberate
= libraryBooks
.Where(x => !x.AbsentFromLastScan && x.Book.UserDefinedItem.BookStatus is LiberatedStatus.NotLiberated or LiberatedStatus.PartialDownload || x.Book.UserDefinedItem.PdfStatus is LiberatedStatus.NotLiberated)
.ToArray();
var toLiberate = libraryBooks.UnLiberated().ToArray();
if (toLiberate.Length > 0)
{
@@ -164,16 +123,10 @@ public abstract class ProcessQueueViewModelBase : ReactiveObject, ILogForm
return false;
}
private bool isBookInQueue(LibraryBook libraryBook)
{
var entry = Queue.FirstOrDefault(b => b?.LibraryBook?.Book?.AudibleProductId == libraryBook.Book.AudibleProductId);
if (entry == null)
return false;
else if (entry.Status is ProcessBookStatus.Cancelled or ProcessBookStatus.Failed)
return !Queue.RemoveCompleted(entry);
else
return true;
}
private bool IsBookInQueue(LibraryBook libraryBook)
=> Queue.FirstOrDefault(b => b?.LibraryBook?.Book?.AudibleProductId == libraryBook.Book.AudibleProductId) is not ProcessBookViewModelBase entry ? false
: entry.Status is ProcessBookStatus.Cancelled or ProcessBookStatus.Failed ? !Queue.RemoveCompleted(entry)
: true;
private bool RemoveCompleted(LibraryBook libraryBook)
=> Queue.FirstOrDefault(b => b?.LibraryBook?.Book?.AudibleProductId == libraryBook.Book.AudibleProductId) is ProcessBookViewModelBase entry
@@ -182,69 +135,43 @@ public abstract class ProcessQueueViewModelBase : ReactiveObject, ILogForm
private void AddDownloadPdf(IEnumerable<LibraryBook> entries)
{
List<ProcessBookViewModelBase> procs = new();
foreach (var entry in entries)
{
if (isBookInQueue(entry))
continue;
var pbook = CreateNewBook(entry);
pbook.AddDownloadPdf();
procs.Add(pbook);
}
Serilog.Log.Logger.Information("Queueing {count} books", procs.Count);
var procs = entries.Where(e => !IsBookInQueue(e)).Select(Create).ToArray();
Serilog.Log.Logger.Information("Queueing {count} books for PDF-only download", procs.Length);
AddToQueue(procs);
ProcessBookViewModelBase Create(LibraryBook entry)
=> CreateNewProcessBook(entry).AddDownloadPdf();
}
private void AddDownloadDecrypt(IEnumerable<LibraryBook> entries)
{
List<ProcessBookViewModelBase> procs = new();
foreach (var entry in entries)
{
if (isBookInQueue(entry))
continue;
var pbook = CreateNewBook(entry);
pbook.AddDownloadDecryptBook();
pbook.AddDownloadPdf();
procs.Add(pbook);
}
Serilog.Log.Logger.Information("Queueing {count} books", procs.Count);
var procs = entries.Where(e => !IsBookInQueue(e)).Select(Create).ToArray();
Serilog.Log.Logger.Information("Queueing {count} books ofr download/decrypt", procs.Length);
AddToQueue(procs);
ProcessBookViewModelBase Create(LibraryBook entry)
=> CreateNewProcessBook(entry).AddDownloadDecryptBook().AddDownloadPdf();
}
private void AddConvertMp3(IEnumerable<LibraryBook> entries)
{
List<ProcessBookViewModelBase> procs = new();
foreach (var entry in entries)
{
if (isBookInQueue(entry))
continue;
var pbook = CreateNewBook(entry);
pbook.AddConvertToMp3();
procs.Add(pbook);
}
Serilog.Log.Logger.Information("Queueing {count} books", procs.Count);
var procs = entries.Where(e => !IsBookInQueue(e)).Select(Create).ToArray();
Serilog.Log.Logger.Information("Queueing {count} books for mp3 conversion", procs.Length);
AddToQueue(procs);
ProcessBookViewModelBase Create(LibraryBook entry)
=> CreateNewProcessBook(entry).AddConvertToMp3();
}
private void AddToQueue(IEnumerable<ProcessBookViewModelBase> pbook)
{
Invoke(() =>
{
Queue.Enqueue(pbook);
if (!Running)
QueueRunner = QueueLoop();
});
Queue.Enqueue(pbook);
if (!Running)
QueueRunner = Task.Run(QueueLoop);
}
#endregion
private DateTime StartingTime;
private async Task QueueLoop()
{
try
@@ -253,12 +180,11 @@ public abstract class ProcessQueueViewModelBase : ReactiveObject, ILogForm
RunningTime = string.Empty;
ProgressBarVisible = true;
StartingTime = DateTime.Now;
using var counterTimer = new System.Threading.Timer(CounterTimer_Tick, null, 0, 500);
var startingTime = DateTime.Now;
bool shownServiceOutageMessage = false;
using var counterTimer = new System.Threading.Timer(_ => RunningTime = timeToStr(DateTime.Now - startingTime), null, 0, 500);
while (Queue.MoveNext())
{
if (Queue.Current is not ProcessBookViewModelBase nextBook)
@@ -267,11 +193,11 @@ public abstract class ProcessQueueViewModelBase : ReactiveObject, ILogForm
continue;
}
Serilog.Log.Logger.Information("Begin processing queued item. {item_LibraryBook}", nextBook.LibraryBook);
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);
Serilog.Log.Logger.Information("Completed processing queued item: '{item_LibraryBook}' with result: {result}", nextBook.LibraryBook, result);
if (result == ProcessBookResult.ValidationFail)
Queue.ClearCurrent();
@@ -281,11 +207,11 @@ public abstract class ProcessQueueViewModelBase : ReactiveObject, ILogForm
nextBook.LibraryBook.UpdateBookStatus(LiberatedStatus.Error);
else if (result == ProcessBookResult.LicenseDeniedPossibleOutage && !shownServiceOutageMessage)
{
await MessageBoxBase.Show(@$"
You were denied a content license for {nextBook.LibraryBook.Book.TitleWithSubtitle}
await MessageBoxBase.Show($"""
You were denied a content license for {nextBook.LibraryBook.Book.TitleWithSubtitle}
This error appears to be caused by a temporary interruption of service that sometimes affects Libation's users. This type of error usually resolves itself in 1 to 2 days, and in the meantime you should still be able to access your books through Audible's website or app.
",
This error appears to be caused by a temporary interruption of service that sometimes affects Libation's users. This type of error usually resolves itself in 1 to 2 days, and in the meantime you should still be able to access your books through Audible's website or app.
""",
"Possible Interruption of Service",
MessageBoxButtons.OK,
MessageBoxIcon.Asterisk);
@@ -301,24 +227,9 @@ This error appears to be caused by a temporary interruption of service that some
{
Serilog.Log.Logger.Error(ex, "An error was encountered while processing queued items");
}
}
private void CounterTimer_Tick(object? state)
{
string timeToStr(TimeSpan time)
{
string minsSecs = $"{time:mm\\:ss}";
if (time.TotalHours >= 1)
return $"{time.TotalHours:F0}:{minsSecs}";
return minsSecs;
}
RunningTime = timeToStr(DateTime.Now - StartingTime);
=> time.TotalHours < 1 ? $"{time:mm\\:ss}"
: $"{time.TotalHours:F0}:{time:mm\\:ss}";
}
}
public class LogEntry
{
public DateTime LogDate { get; init; }
public string LogDateString => LogDate.ToShortTimeString();
public string? LogMessage { get; init; }
}