diff --git a/LibationLauncher/LibationLauncher.csproj b/LibationLauncher/LibationLauncher.csproj
index 33159e83..7cda4a7d 100644
--- a/LibationLauncher/LibationLauncher.csproj
+++ b/LibationLauncher/LibationLauncher.csproj
@@ -13,7 +13,7 @@
win-x64
- 5.4.9.120
+ 5.4.9.140
diff --git a/LibationWinForms/AsyncNotifyPropertyChanged.cs b/LibationWinForms/AsyncNotifyPropertyChanged.cs
index 9de0911d..1c8c8775 100644
--- a/LibationWinForms/AsyncNotifyPropertyChanged.cs
+++ b/LibationWinForms/AsyncNotifyPropertyChanged.cs
@@ -4,34 +4,34 @@ using System.Threading;
namespace LibationWinForms
{
- public abstract class AsyncNotifyPropertyChanged : INotifyPropertyChanged
- {
- public event PropertyChangedEventHandler PropertyChanged;
- private int InstanceThreadId { get; } = Thread.CurrentThread.ManagedThreadId;
- private bool InvokeRequired => Thread.CurrentThread.ManagedThreadId != InstanceThreadId;
- private SynchronizationContext SyncContext { get; } = SynchronizationContext.Current;
+ public abstract class AsyncNotifyPropertyChanged : INotifyPropertyChanged
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+ private int InstanceThreadId { get; } = Thread.CurrentThread.ManagedThreadId;
+ private bool InvokeRequired => Thread.CurrentThread.ManagedThreadId != InstanceThreadId;
+ private SynchronizationContext SyncContext { get; } = SynchronizationContext.Current;
- protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
- {
- var propertyChangedArgs = new PropertyChangedEventArgs(propertyName);
+ protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
+ {
+ var propertyChangedArgs = new PropertyChangedEventArgs(propertyName);
- if (InvokeRequired)
- {
- SyncContext.Post(
- PostPropertyChangedCallback,
- new AsyncCompletedEventArgs(null, false, propertyChangedArgs));
- }
- else
- {
- OnPropertyChanged(propertyChangedArgs);
- }
- }
- private void PostPropertyChangedCallback(object asyncArgs)
- {
- var e = asyncArgs as AsyncCompletedEventArgs;
+ if (InvokeRequired)
+ {
+ SyncContext.Post(
+ PostPropertyChangedCallback,
+ new AsyncCompletedEventArgs(null, false, propertyChangedArgs));
+ }
+ else
+ {
+ OnPropertyChanged(propertyChangedArgs);
+ }
+ }
+ private void PostPropertyChangedCallback(object asyncArgs)
+ {
+ var e = asyncArgs as AsyncCompletedEventArgs;
- OnPropertyChanged(e.UserState as PropertyChangedEventArgs);
- }
- private void OnPropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
- }
+ OnPropertyChanged(e.UserState as PropertyChangedEventArgs);
+ }
+ private void OnPropertyChanged(PropertyChangedEventArgs e) => PropertyChanged?.Invoke(this, e);
+ }
}
diff --git a/LibationWinForms/BookLiberation/AutomatedBackupsForm.cs b/LibationWinForms/BookLiberation/AutomatedBackupsForm.cs
index 95261858..85842e28 100644
--- a/LibationWinForms/BookLiberation/AutomatedBackupsForm.cs
+++ b/LibationWinForms/BookLiberation/AutomatedBackupsForm.cs
@@ -1,34 +1,34 @@
-using System;
+using Dinah.Core.Windows.Forms;
+using System;
using System.Windows.Forms;
-using Dinah.Core.Windows.Forms;
namespace LibationWinForms.BookLiberation
{
- public partial class AutomatedBackupsForm : Form
- {
- public bool KeepGoingChecked => keepGoingCb.Checked;
+ public partial class AutomatedBackupsForm : Form
+ {
+ public bool KeepGoingChecked => keepGoingCb.Checked;
- public bool KeepGoing => keepGoingCb.Enabled && keepGoingCb.Checked;
+ public bool KeepGoing => keepGoingCb.Enabled && keepGoingCb.Checked;
- public AutomatedBackupsForm()
- {
- InitializeComponent();
- }
+ public AutomatedBackupsForm()
+ {
+ InitializeComponent();
+ }
- public void WriteLine(string text)
- {
- if (!IsDisposed)
- logTb.UIThread(() => logTb.AppendText($"{DateTime.Now} {text}{Environment.NewLine}"));
- }
+ public void WriteLine(string text)
+ {
+ if (!IsDisposed)
+ logTb.UIThread(() => logTb.AppendText($"{DateTime.Now} {text}{Environment.NewLine}"));
+ }
public void FinalizeUI()
- {
- keepGoingCb.Enabled = false;
+ {
+ keepGoingCb.Enabled = false;
- if (!IsDisposed)
- logTb.AppendText("");
- }
+ if (!IsDisposed)
+ logTb.AppendText("");
+ }
- private void AutomatedBackupsForm_FormClosing(object sender, FormClosingEventArgs e) => keepGoingCb.Checked = false;
- }
+ private void AutomatedBackupsForm_FormClosing(object sender, FormClosingEventArgs e) => keepGoingCb.Checked = false;
+ }
}
diff --git a/LibationWinForms/BookLiberation/DecryptForm.cs b/LibationWinForms/BookLiberation/DecryptForm.cs
index e6919b16..baaca4c7 100644
--- a/LibationWinForms/BookLiberation/DecryptForm.cs
+++ b/LibationWinForms/BookLiberation/DecryptForm.cs
@@ -1,54 +1,54 @@
-using System;
+using Dinah.Core.Windows.Forms;
+using System;
using System.Windows.Forms;
-using Dinah.Core.Windows.Forms;
namespace LibationWinForms.BookLiberation
{
- public partial class DecryptForm : Form
- {
+ public partial class DecryptForm : Form
+ {
public DecryptForm() => InitializeComponent();
- // book info
- private string title;
- private string authorNames;
- private string narratorNames;
+ // book info
+ private string title;
+ private string authorNames;
+ private string narratorNames;
- public void SetTitle(string actionName, string title)
- {
- this.UIThread(() => this.Text = actionName + " " + title);
- this.title = title;
- updateBookInfo();
- }
- public void SetAuthorNames(string authorNames)
- {
- this.authorNames = authorNames;
- updateBookInfo();
- }
- public void SetNarratorNames(string narratorNames)
- {
- this.narratorNames = narratorNames;
- updateBookInfo();
- }
+ public void SetTitle(string actionName, string title)
+ {
+ this.UIThread(() => this.Text = actionName + " " + title);
+ this.title = title;
+ updateBookInfo();
+ }
+ public void SetAuthorNames(string authorNames)
+ {
+ this.authorNames = authorNames;
+ updateBookInfo();
+ }
+ public void SetNarratorNames(string narratorNames)
+ {
+ this.narratorNames = narratorNames;
+ updateBookInfo();
+ }
- // thread-safe UI updates
- private void updateBookInfo()
- => bookInfoLbl.UIThread(() => bookInfoLbl.Text = $"{title}\r\nBy {authorNames}\r\nNarrated by {narratorNames}");
+ // thread-safe UI updates
+ private void updateBookInfo()
+ => bookInfoLbl.UIThread(() => bookInfoLbl.Text = $"{title}\r\nBy {authorNames}\r\nNarrated by {narratorNames}");
- public void SetCoverImage(System.Drawing.Image coverImage)
- => pictureBox1.UIThread(() => pictureBox1.Image = coverImage);
+ public void SetCoverImage(System.Drawing.Image coverImage)
+ => pictureBox1.UIThread(() => pictureBox1.Image = coverImage);
- public void UpdateProgress(int percentage)
- {
- if (percentage == 0)
- updateRemainingTime(0);
- else
- progressBar1.UIThread(() => progressBar1.Value = percentage);
- }
+ public void UpdateProgress(int percentage)
+ {
+ if (percentage == 0)
+ updateRemainingTime(0);
+ else
+ progressBar1.UIThread(() => progressBar1.Value = percentage);
+ }
- public void UpdateRemainingTime(TimeSpan remaining)
- => updateRemainingTime((int)remaining.TotalSeconds);
+ public void UpdateRemainingTime(TimeSpan remaining)
+ => updateRemainingTime((int)remaining.TotalSeconds);
- private void updateRemainingTime(int remaining)
- => remainingTimeLbl.UIThread(() => remainingTimeLbl.Text = $"ETA:\r\n{remaining} sec");
- }
+ private void updateRemainingTime(int remaining)
+ => remainingTimeLbl.UIThread(() => remainingTimeLbl.Text = $"ETA:\r\n{remaining} sec");
+ }
}
diff --git a/LibationWinForms/BookLiberation/DownloadForm.cs b/LibationWinForms/BookLiberation/DownloadForm.cs
index ffdf28e0..e8bcde73 100644
--- a/LibationWinForms/BookLiberation/DownloadForm.cs
+++ b/LibationWinForms/BookLiberation/DownloadForm.cs
@@ -1,59 +1,59 @@
-using System;
+using Dinah.Core.Windows.Forms;
+using System;
using System.Windows.Forms;
-using Dinah.Core.Windows.Forms;
namespace LibationWinForms.BookLiberation
{
- public partial class DownloadForm : Form
- {
- public DownloadForm()
- {
- InitializeComponent();
+ public partial class DownloadForm : Form
+ {
+ public DownloadForm()
+ {
+ InitializeComponent();
- progressLbl.Text = "";
- filenameLbl.Text = "";
- }
+ progressLbl.Text = "";
+ filenameLbl.Text = "";
+ }
- // thread-safe UI updates
- public void UpdateFilename(string title) => filenameLbl.UIThread(() => filenameLbl.Text = title);
+ // thread-safe UI updates
+ public void UpdateFilename(string title) => filenameLbl.UIThread(() => filenameLbl.Text = title);
- public void DownloadProgressChanged(long BytesReceived, long? TotalBytesToReceive)
- {
- // this won't happen with download file. it will happen with download string
- if (!TotalBytesToReceive.HasValue || TotalBytesToReceive.Value <= 0)
- return;
+ public void DownloadProgressChanged(long BytesReceived, long? TotalBytesToReceive)
+ {
+ // this won't happen with download file. it will happen with download string
+ if (!TotalBytesToReceive.HasValue || TotalBytesToReceive.Value <= 0)
+ return;
- progressLbl.UIThread(() => progressLbl.Text = $"{BytesReceived:#,##0} of {TotalBytesToReceive.Value:#,##0}");
+ progressLbl.UIThread(() => progressLbl.Text = $"{BytesReceived:#,##0} of {TotalBytesToReceive.Value:#,##0}");
- var d = double.Parse(BytesReceived.ToString()) / double.Parse(TotalBytesToReceive.Value.ToString()) * 100.0;
- var i = int.Parse(Math.Truncate(d).ToString());
- progressBar1.UIThread(() => progressBar1.Value = i);
+ var d = double.Parse(BytesReceived.ToString()) / double.Parse(TotalBytesToReceive.Value.ToString()) * 100.0;
+ var i = int.Parse(Math.Truncate(d).ToString());
+ progressBar1.UIThread(() => progressBar1.Value = i);
- lastDownloadProgress = DateTime.Now;
- }
+ lastDownloadProgress = DateTime.Now;
+ }
- #region timer
- private Timer timer { get; } = new Timer { Interval = 1000 };
- private void DownloadForm_Load(object sender, EventArgs e)
- {
- timer.Tick += new EventHandler(timer_Tick);
- timer.Start();
- }
- private DateTime lastDownloadProgress = DateTime.Now;
- private void timer_Tick(object sender, EventArgs e)
- {
- // if no update in the last 30 seconds, display frozen label
- lastUpdateLbl.UIThread(() => lastUpdateLbl.Visible = lastDownloadProgress.AddSeconds(30) < DateTime.Now);
- if (lastUpdateLbl.Visible)
- {
- var diff = DateTime.Now - lastDownloadProgress;
- var min = (int)diff.TotalMinutes;
- var minText = min > 0 ? $"{min}min " : "";
+ #region timer
+ private Timer timer { get; } = new Timer { Interval = 1000 };
+ private void DownloadForm_Load(object sender, EventArgs e)
+ {
+ timer.Tick += new EventHandler(timer_Tick);
+ timer.Start();
+ }
+ private DateTime lastDownloadProgress = DateTime.Now;
+ private void timer_Tick(object sender, EventArgs e)
+ {
+ // if no update in the last 30 seconds, display frozen label
+ lastUpdateLbl.UIThread(() => lastUpdateLbl.Visible = lastDownloadProgress.AddSeconds(30) < DateTime.Now);
+ if (lastUpdateLbl.Visible)
+ {
+ var diff = DateTime.Now - lastDownloadProgress;
+ var min = (int)diff.TotalMinutes;
+ var minText = min > 0 ? $"{min}min " : "";
- lastUpdateLbl.UIThread(() => lastUpdateLbl.Text = $"Frozen? Last download activity: {minText}{diff.Seconds}sec ago");
- }
- }
- private void DownloadForm_FormClosing(object sender, FormClosingEventArgs e) => timer.Stop();
- #endregion
- }
+ lastUpdateLbl.UIThread(() => lastUpdateLbl.Text = $"Frozen? Last download activity: {minText}{diff.Seconds}sec ago");
+ }
+ }
+ private void DownloadForm_FormClosing(object sender, FormClosingEventArgs e) => timer.Stop();
+ #endregion
+ }
}
diff --git a/LibationWinForms/BookLiberation/ProcessorAutomationController.cs b/LibationWinForms/BookLiberation/ProcessorAutomationController.cs
index f378bae7..7496d81a 100644
--- a/LibationWinForms/BookLiberation/ProcessorAutomationController.cs
+++ b/LibationWinForms/BookLiberation/ProcessorAutomationController.cs
@@ -1,505 +1,506 @@
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-using DataLayer;
+using DataLayer;
using Dinah.Core;
using Dinah.Core.ErrorHandling;
using Dinah.Core.Windows.Forms;
using FileLiberator;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
namespace LibationWinForms.BookLiberation
{
- // decouple serilog and form. include convenience factory method
- public class LogMe
- {
- public event EventHandler LogInfo;
- public event EventHandler LogErrorString;
- public event EventHandler<(Exception, string)> LogError;
+ // decouple serilog and form. include convenience factory method
+ public class LogMe
+ {
+ public event EventHandler LogInfo;
+ public event EventHandler LogErrorString;
+ public event EventHandler<(Exception, string)> LogError;
- private LogMe()
- {
- LogInfo += (_, text) => Serilog.Log.Logger.Information($"Automated backup: {text}");
- LogErrorString += (_, text) => Serilog.Log.Logger.Error(text);
- LogError += (_, tuple) => Serilog.Log.Logger.Error(tuple.Item1, tuple.Item2 ?? "Automated backup: error");
- }
+ private LogMe()
+ {
+ LogInfo += (_, text) => Serilog.Log.Logger.Information($"Automated backup: {text}");
+ LogErrorString += (_, text) => Serilog.Log.Logger.Error(text);
+ LogError += (_, tuple) => Serilog.Log.Logger.Error(tuple.Item1, tuple.Item2 ?? "Automated backup: error");
+ }
- public static LogMe RegisterForm(AutomatedBackupsForm form = null)
- {
- var logMe = new LogMe();
+ public static LogMe RegisterForm(AutomatedBackupsForm form = null)
+ {
+ var logMe = new LogMe();
- if (form is null)
- return logMe;
+ if (form is null)
+ return logMe;
- logMe.LogInfo += (_, text) => form?.WriteLine(text);
+ logMe.LogInfo += (_, text) => form?.WriteLine(text);
- logMe.LogErrorString += (_, text) => form?.WriteLine(text);
-
- logMe.LogError += (_, tuple) =>
- {
- form?.WriteLine(tuple.Item2 ?? "Automated backup: error");
- form?.WriteLine("ERROR: " + tuple.Item1.Message);
- };
-
- return logMe;
- }
-
- public void Info(string text) => LogInfo?.Invoke(this, text);
- public void Error(string text) => LogErrorString?.Invoke(this, text);
- public void Error(Exception ex, string text = null) => LogError?.Invoke(this, (ex, text));
- }
-
- public static class ProcessorAutomationController
- {
- public static async Task BackupSingleBookAsync(LibraryBook libraryBook, EventHandler completedAction = null)
- {
- Serilog.Log.Logger.Information("Begin backup single {@DebugInfo}", new { libraryBook?.Book?.AudibleProductId });
-
- var backupBook = getWiredUpBackupBook(completedAction);
-
- (Action unsubscribeEvents, LogMe logMe) = attachToBackupsForm(backupBook);
-
- // continue even if libraryBook is null. we'll display even that in the processing box
- await new BackupSingle(logMe, backupBook, libraryBook).RunBackupAsync();
-
- unsubscribeEvents();
- }
-
- public static async Task BackupAllBooksAsync(EventHandler completedAction = null)
- {
- Serilog.Log.Logger.Information("Begin " + nameof(BackupAllBooksAsync));
-
- var backupBook = getWiredUpBackupBook(completedAction);
- var automatedBackupsForm = new AutomatedBackupsForm();
-
- (Action unsubscribeEvents, LogMe logMe) = attachToBackupsForm(backupBook, automatedBackupsForm);
-
- await new BackupLoop(logMe, backupBook, automatedBackupsForm).RunBackupAsync();
-
- unsubscribeEvents();
- }
-
- public static async Task ConvertAllBooksAsync()
- {
- Serilog.Log.Logger.Information("Begin " + nameof(ConvertAllBooksAsync));
-
- var convertBook = new ConvertToMp3();
- convertBook.Begin += (_, l) => wireUpEvents(convertBook, l, "Converting");
-
- var automatedBackupsForm = new AutomatedBackupsForm();
-
- var logMe = LogMe.RegisterForm(automatedBackupsForm);
-
- void statusUpdate(object _, string str) => logMe.Info("- " + str);
- void convertBookBegin(object _, LibraryBook libraryBook) => logMe.Info($"Convert Step, Begin: {libraryBook.Book}");
- void convertBookCompleted(object _, LibraryBook libraryBook) => logMe.Info($"Convert Step, Completed: {libraryBook.Book}{Environment.NewLine}");
- convertBook.Begin += convertBookBegin;
- convertBook.StatusUpdate += statusUpdate;
- convertBook.Completed += convertBookCompleted;
-
- await new BackupLoop(logMe, convertBook, automatedBackupsForm).RunBackupAsync();
-
- convertBook.Begin -= convertBookBegin;
- convertBook.StatusUpdate -= statusUpdate;
- convertBook.Completed -= convertBookCompleted;
- }
-
- private static BackupBook getWiredUpBackupBook(EventHandler completedAction)
- {
- var backupBook = new BackupBook();
-
- backupBook.DownloadDecryptBook.Begin += (_, l) => wireUpEvents(backupBook.DownloadDecryptBook, l);
- backupBook.DownloadPdf.Begin += (_, __) => wireUpEvents(backupBook.DownloadPdf);
-
- if (completedAction != null)
- {
- backupBook.DownloadDecryptBook.Completed += completedAction;
- backupBook.DownloadPdf.Completed += completedAction;
- }
-
- return backupBook;
- }
-
- private static (Action unsubscribeEvents, LogMe) attachToBackupsForm(BackupBook backupBook, AutomatedBackupsForm automatedBackupsForm = null)
- {
- #region create logger
- var logMe = LogMe.RegisterForm(automatedBackupsForm);
- #endregion
-
- #region define how model actions will affect form behavior
- void statusUpdate(object _, string str) => logMe.Info("- " + str);
- void decryptBookBegin(object _, LibraryBook libraryBook) => logMe.Info($"Decrypt Step, Begin: {libraryBook.Book}");
- // extra line after book is completely finished
- void decryptBookCompleted(object _, LibraryBook libraryBook) => logMe.Info($"Decrypt Step, Completed: {libraryBook.Book}{Environment.NewLine}");
- void downloadPdfBegin(object _, LibraryBook libraryBook) => logMe.Info($"PDF Step, Begin: {libraryBook.Book}");
- // extra line after book is completely finished
- void downloadPdfCompleted(object _, LibraryBook libraryBook) => logMe.Info($"PDF Step, Completed: {libraryBook.Book}{Environment.NewLine}");
- #endregion
-
- #region subscribe new form to model's events
- backupBook.DownloadDecryptBook.Begin += decryptBookBegin;
- backupBook.DownloadDecryptBook.StatusUpdate += statusUpdate;
- backupBook.DownloadDecryptBook.Completed += decryptBookCompleted;
- backupBook.DownloadPdf.Begin += downloadPdfBegin;
- backupBook.DownloadPdf.StatusUpdate += statusUpdate;
- backupBook.DownloadPdf.Completed += downloadPdfCompleted;
- #endregion
-
- #region when form closes, unsubscribe from model's events
- // unsubscribe so disposed forms aren't still trying to receive notifications
- Action unsubscribe = () =>
- {
- backupBook.DownloadDecryptBook.Begin -= decryptBookBegin;
- backupBook.DownloadDecryptBook.StatusUpdate -= statusUpdate;
- backupBook.DownloadDecryptBook.Completed -= decryptBookCompleted;
- backupBook.DownloadPdf.Begin -= downloadPdfBegin;
- backupBook.DownloadPdf.StatusUpdate -= statusUpdate;
- backupBook.DownloadPdf.Completed -= downloadPdfCompleted;
- };
- #endregion
-
- return (unsubscribe, logMe);
- }
-
- public static async Task BackupAllPdfsAsync(EventHandler completedAction = null)
- {
- Serilog.Log.Logger.Information("Begin " + nameof(BackupAllPdfsAsync));
-
- var downloadPdf = getWiredUpDownloadPdf(completedAction);
-
- (AutomatedBackupsForm automatedBackupsForm, LogMe logMe) = attachToBackupsForm(downloadPdf);
- await new BackupLoop(logMe, downloadPdf, automatedBackupsForm).RunBackupAsync();
- }
-
- private static DownloadPdf getWiredUpDownloadPdf(EventHandler completedAction)
- {
- var downloadPdf = new DownloadPdf();
-
- downloadPdf.Begin += (_, __) => wireUpEvents(downloadPdf);
-
- if (completedAction != null)
- downloadPdf.Completed += completedAction;
-
- return downloadPdf;
- }
-
- public static void DownloadFile(string url, string destination, bool showDownloadCompletedDialog = false)
- {
- var downloadDialog = new DownloadForm();
- downloadDialog.UpdateFilename(destination);
- downloadDialog.Show();
-
- new System.Threading.Thread(() =>
- {
- var downloadFile = new DownloadFile();
-
- downloadFile.DownloadProgressChanged += (_, progress) => downloadDialog.UIThread(() =>
- downloadDialog.DownloadProgressChanged(progress.BytesReceived, progress.TotalBytesToReceive)
- );
- downloadFile.DownloadCompleted += (_, __) => downloadDialog.UIThread(() =>
- {
- downloadDialog.Close();
- if (showDownloadCompletedDialog)
- MessageBox.Show("File downloaded");
- });
-
- downloadFile.PerformDownloadFileAsync(url, destination).GetAwaiter().GetResult();
- })
- { IsBackground = true }
- .Start();
- }
-
- // subscribed to Begin event because a new form should be created+processed+closed on each iteration
- private static void wireUpEvents(IDownloadableProcessable downloadable)
- {
- #region create form
- var downloadDialog = new DownloadForm();
- #endregion
-
- // extra complexity for wiring up download form:
- // case 1: download is needed
- // dialog created. subscribe to events
- // downloadable.DownloadBegin fires. shows dialog
- // downloadable.DownloadCompleted fires. closes dialog. which fires FormClosing, FormClosed, Disposed
- // Disposed unsubscribe from events
- // case 2: download is not needed
- // dialog created. subscribe to events
- // dialog is never shown nor closed
- // downloadable.Completed fires. disposes dialog and unsubscribes from events
-
- #region define how model actions will affect form behavior
- void downloadBegin(object _, string str)
- {
- downloadDialog.UpdateFilename(str);
- downloadDialog.Show();
- }
-
- // close form on DOWNLOAD completed, not final Completed. Else for BackupBook this form won't close until DECRYPT is also complete
- void fileDownloadCompleted(object _, string __) => downloadDialog.Close();
-
- void downloadProgressChanged(object _, Dinah.Core.Net.Http.DownloadProgress progress)
- => downloadDialog.DownloadProgressChanged(progress.BytesReceived, progress.TotalBytesToReceive);
-
- void unsubscribe(object _ = null, EventArgs __ = null)
- {
- downloadable.DownloadBegin -= downloadBegin;
- downloadable.DownloadCompleted -= fileDownloadCompleted;
- downloadable.DownloadProgressChanged -= downloadProgressChanged;
- downloadable.Completed -= dialogDispose;
- }
-
- // unless we dispose, if the form is created but un-used/never-shown then weird UI stuff can happen
- // also, since event unsubscribe occurs on FormClosing and an unused form is never closed, then the events will never be unsubscribed
- void dialogDispose(object _, object __)
- {
- if (!downloadDialog.IsDisposed)
- downloadDialog.Dispose();
- }
- #endregion
-
- #region subscribe new form to model's events
- downloadable.DownloadBegin += downloadBegin;
- downloadable.DownloadCompleted += fileDownloadCompleted;
- downloadable.DownloadProgressChanged += downloadProgressChanged;
- downloadable.Completed += dialogDispose;
- #endregion
-
- #region when form closes, unsubscribe from model's events
- // unsubscribe so disposed forms aren't still trying to receive notifications
- // FormClosing is more UI safe but won't fire unless the form is shown and closed
- // if form was shown, Disposed will fire for FormClosing, FormClosed, and Disposed
- // if not shown, it will still fire for Disposed
- downloadDialog.Disposed += unsubscribe;
- #endregion
- }
-
- // subscribed to Begin event because a new form should be created+processed+closed on each iteration
- private static void wireUpEvents(IDecryptable decryptBook, LibraryBook libraryBook, string actionName = "Decrypting")
- {
- #region create form
- var decryptDialog = new DecryptForm();
- #endregion
-
- #region Set initially displayed book properties from library info.
- decryptDialog.SetTitle(actionName, libraryBook.Book.Title);
- decryptDialog.SetAuthorNames(string.Join(", ", libraryBook.Book.Authors));
- decryptDialog.SetNarratorNames(string.Join(", ", libraryBook.Book.NarratorNames));
- decryptDialog.SetCoverImage(
- WindowsDesktopUtilities.WinAudibleImageServer.GetImage(
- libraryBook.Book.PictureId,
- FileManager.PictureSize._80x80
- ));
- #endregion
-
- #region define how model actions will affect form behavior
- void decryptBegin(object _, string __) => decryptDialog.Show();
-
- void titleDiscovered(object _, string title) => decryptDialog.SetTitle(actionName, title);
- void authorsDiscovered(object _, string authors) => decryptDialog.SetAuthorNames(authors);
- void narratorsDiscovered(object _, string narrators) => decryptDialog.SetNarratorNames(narrators);
- void coverImageFilepathDiscovered(object _, byte[] coverBytes) => decryptDialog.SetCoverImage(Dinah.Core.Drawing.ImageReader.ToImage(coverBytes));
- void updateProgress(object _, int percentage) => decryptDialog.UpdateProgress(percentage);
- void updateRemainingTime(object _, TimeSpan remaining) => decryptDialog.UpdateRemainingTime(remaining);
- void decryptCompleted(object _, string __) => decryptDialog.Close();
-
- void requestCoverArt(object _, Action setCoverArtDelegate)
- {
- var picDef = new FileManager.PictureDefinition(libraryBook.Book.PictureId, FileManager.PictureSize._500x500);
- (bool isDefault, byte[] picture) = FileManager.PictureStorage.GetPicture(picDef);
-
- if (isDefault)
- {
- void pictureCached(object _, string pictureId)
- {
- if (pictureId == libraryBook.Book.PictureId)
- {
- FileManager.PictureStorage.PictureCached -= pictureCached;
-
- var picDef = new FileManager.PictureDefinition(libraryBook.Book.PictureId, FileManager.PictureSize._500x500);
- (_, picture) = FileManager.PictureStorage.GetPicture(picDef);
-
- setCoverArtDelegate(picture);
- }
- };
- FileManager.PictureStorage.PictureCached += pictureCached;
- }
- else
- setCoverArtDelegate(picture);
- }
- #endregion
-
- #region subscribe new form to model's events
- decryptBook.DecryptBegin += decryptBegin;
-
- decryptBook.TitleDiscovered += titleDiscovered;
- decryptBook.AuthorsDiscovered += authorsDiscovered;
- decryptBook.NarratorsDiscovered += narratorsDiscovered;
- decryptBook.CoverImageFilepathDiscovered += coverImageFilepathDiscovered;
- decryptBook.UpdateProgress += updateProgress;
- decryptBook.UpdateRemainingTime += updateRemainingTime;
- decryptBook.RequestCoverArt += requestCoverArt;
-
- decryptBook.DecryptCompleted += decryptCompleted;
- #endregion
-
- #region when form closes, unsubscribe from model's events
- // unsubscribe so disposed forms aren't still trying to receive notifications
- decryptDialog.FormClosing += (_, __) =>
- {
- decryptBook.DecryptBegin -= decryptBegin;
-
- decryptBook.TitleDiscovered -= titleDiscovered;
- decryptBook.AuthorsDiscovered -= authorsDiscovered;
- decryptBook.NarratorsDiscovered -= narratorsDiscovered;
- decryptBook.CoverImageFilepathDiscovered -= coverImageFilepathDiscovered;
- decryptBook.UpdateProgress -= updateProgress;
- decryptBook.UpdateRemainingTime -= updateRemainingTime;
- decryptBook.RequestCoverArt -= requestCoverArt;
-
- decryptBook.DecryptCompleted -= decryptCompleted;
- decryptBook.Cancel();
- };
- #endregion
- }
-
- private static (AutomatedBackupsForm, LogMe) attachToBackupsForm(IDownloadableProcessable downloadable)
- {
- #region create form and logger
- var automatedBackupsForm = new AutomatedBackupsForm();
- var logMe = LogMe.RegisterForm(automatedBackupsForm);
- #endregion
-
- #region define how model actions will affect form behavior
- void begin(object _, LibraryBook libraryBook) => logMe.Info($"Begin: {libraryBook.Book}");
- void statusUpdate(object _, string str) => logMe.Info("- " + str);
- // extra line after book is completely finished
- void completed(object _, LibraryBook libraryBook) => logMe.Info($"Completed: {libraryBook.Book}{Environment.NewLine}");
- #endregion
-
- #region subscribe new form to model's events
- downloadable.Begin += begin;
- downloadable.StatusUpdate += statusUpdate;
- downloadable.Completed += completed;
- #endregion
-
- #region when form closes, unsubscribe from model's events
- // unsubscribe so disposed forms aren't still trying to receive notifications
- automatedBackupsForm.FormClosing += (_, __) =>
- {
- downloadable.Begin -= begin;
- downloadable.StatusUpdate -= statusUpdate;
- downloadable.Completed -= completed;
- };
- #endregion
-
- return (automatedBackupsForm, logMe);
- }
- }
-
- abstract class BackupRunner
- {
- protected LogMe LogMe { get; }
- protected IProcessable Processable { get; }
- protected AutomatedBackupsForm AutomatedBackupsForm { get; }
-
- protected BackupRunner(LogMe logMe, IProcessable processable, AutomatedBackupsForm automatedBackupsForm = null)
- {
- LogMe = logMe;
- Processable = processable;
- AutomatedBackupsForm = automatedBackupsForm;
- }
-
- protected abstract Task RunAsync();
-
- protected abstract string SkipDialogText { get; }
- protected abstract MessageBoxButtons SkipDialogButtons { get; }
- protected abstract DialogResult CreateSkipFileResult { get; }
-
- public async Task RunBackupAsync()
- {
- AutomatedBackupsForm?.Show();
-
- try
- {
- await RunAsync();
- }
- catch (Exception ex)
- {
- LogMe.Error(ex);
- }
-
- AutomatedBackupsForm?.FinalizeUI();
- LogMe.Info("DONE");
- }
-
- protected async Task ProcessOneAsync(Func> func, LibraryBook libraryBook)
- {
- string logMessage;
-
- try
- {
- var statusHandler = await func(libraryBook);
-
- if (statusHandler.IsSuccess)
- return true;
-
- foreach (var errorMessage in statusHandler.Errors)
- LogMe.Error(errorMessage);
-
- logMessage = statusHandler.Errors.Aggregate((a, b) => $"{a}\r\n{b}");
- }
- catch (Exception ex)
- {
- LogMe.Error(ex);
-
- logMessage = ex.Message + "\r\n|\r\n" + ex.StackTrace;
- }
-
- LogMe.Error("ERROR. All books have not been processed. Most recent book: processing failed");
-
- string details;
- try
- {
- static string trunc(string str)
- => string.IsNullOrWhiteSpace(str) ? "[empty]"
- : (str.Length > 50) ? $"{str.Truncate(47)}..."
- : str;
-
- details =
+ logMe.LogErrorString += (_, text) => form?.WriteLine(text);
+
+ logMe.LogError += (_, tuple) =>
+ {
+ form?.WriteLine(tuple.Item2 ?? "Automated backup: error");
+ form?.WriteLine("ERROR: " + tuple.Item1.Message);
+ };
+
+ return logMe;
+ }
+
+ public void Info(string text) => LogInfo?.Invoke(this, text);
+ public void Error(string text) => LogErrorString?.Invoke(this, text);
+ public void Error(Exception ex, string text = null) => LogError?.Invoke(this, (ex, text));
+ }
+
+ public static class ProcessorAutomationController
+ {
+ public static async Task BackupSingleBookAsync(LibraryBook libraryBook, EventHandler completedAction = null)
+ {
+ Serilog.Log.Logger.Information("Begin backup single {@DebugInfo}", new { libraryBook?.Book?.AudibleProductId });
+
+ var backupBook = getWiredUpBackupBook(completedAction);
+
+ (Action unsubscribeEvents, LogMe logMe) = attachToBackupsForm(backupBook);
+
+ // continue even if libraryBook is null. we'll display even that in the processing box
+ await new BackupSingle(logMe, backupBook, libraryBook).RunBackupAsync();
+
+ unsubscribeEvents();
+ }
+
+ public static async Task BackupAllBooksAsync(EventHandler completedAction = null)
+ {
+ Serilog.Log.Logger.Information("Begin " + nameof(BackupAllBooksAsync));
+
+ var backupBook = getWiredUpBackupBook(completedAction);
+ var automatedBackupsForm = new AutomatedBackupsForm();
+
+ (Action unsubscribeEvents, LogMe logMe) = attachToBackupsForm(backupBook, automatedBackupsForm);
+
+ await new BackupLoop(logMe, backupBook, automatedBackupsForm).RunBackupAsync();
+
+ unsubscribeEvents();
+ }
+
+ public static async Task ConvertAllBooksAsync()
+ {
+ Serilog.Log.Logger.Information("Begin " + nameof(ConvertAllBooksAsync));
+
+ var convertBook = new ConvertToMp3();
+ convertBook.Begin += (_, l) => wireUpEvents(convertBook, l, "Converting");
+
+ var automatedBackupsForm = new AutomatedBackupsForm();
+
+ var logMe = LogMe.RegisterForm(automatedBackupsForm);
+
+ void statusUpdate(object _, string str) => logMe.Info("- " + str);
+ void convertBookBegin(object _, LibraryBook libraryBook) => logMe.Info($"Convert Step, Begin: {libraryBook.Book}");
+ void convertBookCompleted(object _, LibraryBook libraryBook) => logMe.Info($"Convert Step, Completed: {libraryBook.Book}{Environment.NewLine}");
+ convertBook.Begin += convertBookBegin;
+ convertBook.StatusUpdate += statusUpdate;
+ convertBook.Completed += convertBookCompleted;
+
+ await new BackupLoop(logMe, convertBook, automatedBackupsForm).RunBackupAsync();
+
+ convertBook.Begin -= convertBookBegin;
+ convertBook.StatusUpdate -= statusUpdate;
+ convertBook.Completed -= convertBookCompleted;
+ }
+
+ private static BackupBook getWiredUpBackupBook(EventHandler completedAction)
+ {
+ var backupBook = new BackupBook();
+
+ backupBook.DownloadDecryptBook.Begin += (_, l) => wireUpEvents(backupBook.DownloadDecryptBook, l);
+ backupBook.DownloadPdf.Begin += (_, __) => wireUpEvents(backupBook.DownloadPdf);
+
+ if (completedAction != null)
+ {
+ backupBook.DownloadDecryptBook.Completed += completedAction;
+ backupBook.DownloadPdf.Completed += completedAction;
+ }
+
+ return backupBook;
+ }
+
+ private static (Action unsubscribeEvents, LogMe) attachToBackupsForm(BackupBook backupBook, AutomatedBackupsForm automatedBackupsForm = null)
+ {
+ #region create logger
+ var logMe = LogMe.RegisterForm(automatedBackupsForm);
+ #endregion
+
+ #region define how model actions will affect form behavior
+ void statusUpdate(object _, string str) => logMe.Info("- " + str);
+ void decryptBookBegin(object _, LibraryBook libraryBook) => logMe.Info($"Decrypt Step, Begin: {libraryBook.Book}");
+ // extra line after book is completely finished
+ void decryptBookCompleted(object _, LibraryBook libraryBook) => logMe.Info($"Decrypt Step, Completed: {libraryBook.Book}{Environment.NewLine}");
+ void downloadPdfBegin(object _, LibraryBook libraryBook) => logMe.Info($"PDF Step, Begin: {libraryBook.Book}");
+ // extra line after book is completely finished
+ void downloadPdfCompleted(object _, LibraryBook libraryBook) => logMe.Info($"PDF Step, Completed: {libraryBook.Book}{Environment.NewLine}");
+ #endregion
+
+ #region subscribe new form to model's events
+ backupBook.DownloadDecryptBook.Begin += decryptBookBegin;
+ backupBook.DownloadDecryptBook.StatusUpdate += statusUpdate;
+ backupBook.DownloadDecryptBook.Completed += decryptBookCompleted;
+ backupBook.DownloadPdf.Begin += downloadPdfBegin;
+ backupBook.DownloadPdf.StatusUpdate += statusUpdate;
+ backupBook.DownloadPdf.Completed += downloadPdfCompleted;
+ #endregion
+
+ #region when form closes, unsubscribe from model's events
+ // unsubscribe so disposed forms aren't still trying to receive notifications
+ Action unsubscribe = () =>
+ {
+ backupBook.DownloadDecryptBook.Begin -= decryptBookBegin;
+ backupBook.DownloadDecryptBook.StatusUpdate -= statusUpdate;
+ backupBook.DownloadDecryptBook.Completed -= decryptBookCompleted;
+ backupBook.DownloadPdf.Begin -= downloadPdfBegin;
+ backupBook.DownloadPdf.StatusUpdate -= statusUpdate;
+ backupBook.DownloadPdf.Completed -= downloadPdfCompleted;
+ };
+ #endregion
+
+ return (unsubscribe, logMe);
+ }
+
+ public static async Task BackupAllPdfsAsync(EventHandler completedAction = null)
+ {
+ Serilog.Log.Logger.Information("Begin " + nameof(BackupAllPdfsAsync));
+
+ var downloadPdf = getWiredUpDownloadPdf(completedAction);
+
+ (AutomatedBackupsForm automatedBackupsForm, LogMe logMe) = attachToBackupsForm(downloadPdf);
+ await new BackupLoop(logMe, downloadPdf, automatedBackupsForm).RunBackupAsync();
+ }
+
+ private static DownloadPdf getWiredUpDownloadPdf(EventHandler completedAction)
+ {
+ var downloadPdf = new DownloadPdf();
+
+ downloadPdf.Begin += (_, __) => wireUpEvents(downloadPdf);
+
+ if (completedAction != null)
+ downloadPdf.Completed += completedAction;
+
+ return downloadPdf;
+ }
+
+ public static void DownloadFile(string url, string destination, bool showDownloadCompletedDialog = false)
+ {
+ var downloadDialog = new DownloadForm();
+ downloadDialog.UpdateFilename(destination);
+ downloadDialog.Show();
+
+ new System.Threading.Thread(() =>
+ {
+ var downloadFile = new DownloadFile();
+
+ downloadFile.DownloadProgressChanged += (_, progress) => downloadDialog.UIThread(() =>
+ downloadDialog.DownloadProgressChanged(progress.BytesReceived, progress.TotalBytesToReceive)
+ );
+ downloadFile.DownloadCompleted += (_, __) => downloadDialog.UIThread(() =>
+ {
+ downloadDialog.Close();
+ if (showDownloadCompletedDialog)
+ MessageBox.Show("File downloaded");
+ });
+
+ downloadFile.PerformDownloadFileAsync(url, destination).GetAwaiter().GetResult();
+ })
+ { IsBackground = true }
+ .Start();
+ }
+
+ // subscribed to Begin event because a new form should be created+processed+closed on each iteration
+ private static void wireUpEvents(IDownloadableProcessable downloadable)
+ {
+ #region create form
+ var downloadDialog = new DownloadForm();
+ #endregion
+
+ // extra complexity for wiring up download form:
+ // case 1: download is needed
+ // dialog created. subscribe to events
+ // downloadable.DownloadBegin fires. shows dialog
+ // downloadable.DownloadCompleted fires. closes dialog. which fires FormClosing, FormClosed, Disposed
+ // Disposed unsubscribe from events
+ // case 2: download is not needed
+ // dialog created. subscribe to events
+ // dialog is never shown nor closed
+ // downloadable.Completed fires. disposes dialog and unsubscribes from events
+
+ #region define how model actions will affect form behavior
+ void downloadBegin(object _, string str)
+ {
+ downloadDialog.UpdateFilename(str);
+ downloadDialog.Show();
+ }
+
+ // close form on DOWNLOAD completed, not final Completed. Else for BackupBook this form won't close until DECRYPT is also complete
+ void fileDownloadCompleted(object _, string __) => downloadDialog.Close();
+
+ void downloadProgressChanged(object _, Dinah.Core.Net.Http.DownloadProgress progress)
+ => downloadDialog.DownloadProgressChanged(progress.BytesReceived, progress.TotalBytesToReceive);
+
+ void unsubscribe(object _ = null, EventArgs __ = null)
+ {
+ downloadable.DownloadBegin -= downloadBegin;
+ downloadable.DownloadCompleted -= fileDownloadCompleted;
+ downloadable.DownloadProgressChanged -= downloadProgressChanged;
+ downloadable.Completed -= dialogDispose;
+ }
+
+ // unless we dispose, if the form is created but un-used/never-shown then weird UI stuff can happen
+ // also, since event unsubscribe occurs on FormClosing and an unused form is never closed, then the events will never be unsubscribed
+ void dialogDispose(object _, object __)
+ {
+ if (!downloadDialog.IsDisposed)
+ downloadDialog.Dispose();
+ }
+ #endregion
+
+ #region subscribe new form to model's events
+ downloadable.DownloadBegin += downloadBegin;
+ downloadable.DownloadCompleted += fileDownloadCompleted;
+ downloadable.DownloadProgressChanged += downloadProgressChanged;
+ downloadable.Completed += dialogDispose;
+ #endregion
+
+ #region when form closes, unsubscribe from model's events
+ // unsubscribe so disposed forms aren't still trying to receive notifications
+ // FormClosing is more UI safe but won't fire unless the form is shown and closed
+ // if form was shown, Disposed will fire for FormClosing, FormClosed, and Disposed
+ // if not shown, it will still fire for Disposed
+ downloadDialog.Disposed += unsubscribe;
+ #endregion
+ }
+
+ // subscribed to Begin event because a new form should be created+processed+closed on each iteration
+ private static void wireUpEvents(IDecryptable decryptBook, LibraryBook libraryBook, string actionName = "Decrypting")
+ {
+ #region create form
+ var decryptDialog = new DecryptForm();
+ #endregion
+
+ #region Set initially displayed book properties from library info.
+ decryptDialog.SetTitle(actionName, libraryBook.Book.Title);
+ decryptDialog.SetAuthorNames(string.Join(", ", libraryBook.Book.Authors));
+ decryptDialog.SetNarratorNames(string.Join(", ", libraryBook.Book.NarratorNames));
+ decryptDialog.SetCoverImage(
+ WindowsDesktopUtilities.WinAudibleImageServer.GetImage(
+ libraryBook.Book.PictureId,
+ FileManager.PictureSize._80x80
+ ));
+ #endregion
+
+ #region define how model actions will affect form behavior
+ void decryptBegin(object _, string __) => decryptDialog.Show();
+
+ void titleDiscovered(object _, string title) => decryptDialog.SetTitle(actionName, title);
+ void authorsDiscovered(object _, string authors) => decryptDialog.SetAuthorNames(authors);
+ void narratorsDiscovered(object _, string narrators) => decryptDialog.SetNarratorNames(narrators);
+ void coverImageFilepathDiscovered(object _, byte[] coverBytes) => decryptDialog.SetCoverImage(Dinah.Core.Drawing.ImageReader.ToImage(coverBytes));
+ void updateProgress(object _, int percentage) => decryptDialog.UpdateProgress(percentage);
+ void updateRemainingTime(object _, TimeSpan remaining) => decryptDialog.UpdateRemainingTime(remaining);
+ void decryptCompleted(object _, string __) => decryptDialog.Close();
+
+ void requestCoverArt(object _, Action setCoverArtDelegate)
+ {
+ var picDef = new FileManager.PictureDefinition(libraryBook.Book.PictureId, FileManager.PictureSize._500x500);
+ (bool isDefault, byte[] picture) = FileManager.PictureStorage.GetPicture(picDef);
+
+ if (isDefault)
+ {
+ void pictureCached(object _, string pictureId)
+ {
+ if (pictureId == libraryBook.Book.PictureId)
+ {
+ FileManager.PictureStorage.PictureCached -= pictureCached;
+
+ var picDef = new FileManager.PictureDefinition(libraryBook.Book.PictureId, FileManager.PictureSize._500x500);
+ (_, picture) = FileManager.PictureStorage.GetPicture(picDef);
+
+ setCoverArtDelegate(picture);
+ }
+ };
+ FileManager.PictureStorage.PictureCached += pictureCached;
+ }
+ else
+ setCoverArtDelegate(picture);
+ }
+ #endregion
+
+ #region subscribe new form to model's events
+ decryptBook.DecryptBegin += decryptBegin;
+
+ decryptBook.TitleDiscovered += titleDiscovered;
+ decryptBook.AuthorsDiscovered += authorsDiscovered;
+ decryptBook.NarratorsDiscovered += narratorsDiscovered;
+ decryptBook.CoverImageFilepathDiscovered += coverImageFilepathDiscovered;
+ decryptBook.UpdateProgress += updateProgress;
+ decryptBook.UpdateRemainingTime += updateRemainingTime;
+ decryptBook.RequestCoverArt += requestCoverArt;
+
+ decryptBook.DecryptCompleted += decryptCompleted;
+ #endregion
+
+ #region when form closes, unsubscribe from model's events
+ // unsubscribe so disposed forms aren't still trying to receive notifications
+ decryptDialog.FormClosing += (_, __) =>
+ {
+ decryptBook.DecryptBegin -= decryptBegin;
+
+ decryptBook.TitleDiscovered -= titleDiscovered;
+ decryptBook.AuthorsDiscovered -= authorsDiscovered;
+ decryptBook.NarratorsDiscovered -= narratorsDiscovered;
+ decryptBook.CoverImageFilepathDiscovered -= coverImageFilepathDiscovered;
+ decryptBook.UpdateProgress -= updateProgress;
+ decryptBook.UpdateRemainingTime -= updateRemainingTime;
+ decryptBook.RequestCoverArt -= requestCoverArt;
+
+ decryptBook.DecryptCompleted -= decryptCompleted;
+ decryptBook.Cancel();
+ };
+ #endregion
+ }
+
+ private static (AutomatedBackupsForm, LogMe) attachToBackupsForm(IDownloadableProcessable downloadable)
+ {
+ #region create form and logger
+ var automatedBackupsForm = new AutomatedBackupsForm();
+ var logMe = LogMe.RegisterForm(automatedBackupsForm);
+ #endregion
+
+ #region define how model actions will affect form behavior
+ void begin(object _, LibraryBook libraryBook) => logMe.Info($"Begin: {libraryBook.Book}");
+ void statusUpdate(object _, string str) => logMe.Info("- " + str);
+ // extra line after book is completely finished
+ void completed(object _, LibraryBook libraryBook) => logMe.Info($"Completed: {libraryBook.Book}{Environment.NewLine}");
+ #endregion
+
+ #region subscribe new form to model's events
+ downloadable.Begin += begin;
+ downloadable.StatusUpdate += statusUpdate;
+ downloadable.Completed += completed;
+ #endregion
+
+ #region when form closes, unsubscribe from model's events
+ // unsubscribe so disposed forms aren't still trying to receive notifications
+ automatedBackupsForm.FormClosing += (_, __) =>
+ {
+ downloadable.Begin -= begin;
+ downloadable.StatusUpdate -= statusUpdate;
+ downloadable.Completed -= completed;
+ };
+ #endregion
+
+ return (automatedBackupsForm, logMe);
+ }
+ }
+
+ internal abstract class BackupRunner
+ {
+ protected LogMe LogMe { get; }
+ protected IProcessable Processable { get; }
+ protected AutomatedBackupsForm AutomatedBackupsForm { get; }
+
+ protected BackupRunner(LogMe logMe, IProcessable processable, AutomatedBackupsForm automatedBackupsForm = null)
+ {
+ LogMe = logMe;
+ Processable = processable;
+ AutomatedBackupsForm = automatedBackupsForm;
+ }
+
+ protected abstract Task RunAsync();
+
+ protected abstract string SkipDialogText { get; }
+ protected abstract MessageBoxButtons SkipDialogButtons { get; }
+ protected abstract DialogResult CreateSkipFileResult { get; }
+
+ public async Task RunBackupAsync()
+ {
+ AutomatedBackupsForm?.Show();
+
+ try
+ {
+ await RunAsync();
+ }
+ catch (Exception ex)
+ {
+ LogMe.Error(ex);
+ }
+
+ AutomatedBackupsForm?.FinalizeUI();
+ LogMe.Info("DONE");
+ }
+
+ protected async Task ProcessOneAsync(Func> func, LibraryBook libraryBook)
+ {
+ string logMessage;
+
+ try
+ {
+ var statusHandler = await func(libraryBook);
+
+ if (statusHandler.IsSuccess)
+ return true;
+
+ foreach (var errorMessage in statusHandler.Errors)
+ LogMe.Error(errorMessage);
+
+ logMessage = statusHandler.Errors.Aggregate((a, b) => $"{a}\r\n{b}");
+ }
+ catch (Exception ex)
+ {
+ LogMe.Error(ex);
+
+ logMessage = ex.Message + "\r\n|\r\n" + ex.StackTrace;
+ }
+
+ LogMe.Error("ERROR. All books have not been processed. Most recent book: processing failed");
+
+ string details;
+ try
+ {
+ static string trunc(string str)
+ => string.IsNullOrWhiteSpace(str) ? "[empty]"
+ : (str.Length > 50) ? $"{str.Truncate(47)}..."
+ : str;
+
+ details =
$@" Title: {libraryBook.Book.Title}
ID: {libraryBook.Book.AudibleProductId}
Author: {trunc(libraryBook.Book.AuthorNames)}
Narr: {trunc(libraryBook.Book.NarratorNames)}";
- }
- catch
- {
- details = "[Error retrieving details]";
- }
+ }
+ catch
+ {
+ details = "[Error retrieving details]";
+ }
- var dialogResult = MessageBox.Show(string.Format(SkipDialogText, details), "Skip importing this book?", SkipDialogButtons, MessageBoxIcon.Question);
+ var dialogResult = MessageBox.Show(string.Format(SkipDialogText, details), "Skip importing this book?", SkipDialogButtons, MessageBoxIcon.Question);
- if (dialogResult == DialogResult.Abort)
- return false;
+ if (dialogResult == DialogResult.Abort)
+ return false;
- if (dialogResult == CreateSkipFileResult)
- {
- ApplicationServices.LibraryCommands.UpdateBook(libraryBook, LiberatedStatus.Error, null);
- var path = FileManager.AudibleFileStorage.Audio.CreateSkipFile(libraryBook.Book.Title, libraryBook.Book.AudibleProductId, logMessage);
- LogMe.Info($@"
+ if (dialogResult == CreateSkipFileResult)
+ {
+ ApplicationServices.LibraryCommands.UpdateBook(libraryBook, LiberatedStatus.Error, null);
+ var path = FileManager.AudibleFileStorage.Audio.CreateSkipFile(libraryBook.Book.Title, libraryBook.Book.AudibleProductId, logMessage);
+ LogMe.Info($@"
Created new 'skip' file
[{libraryBook.Book.AudibleProductId}] {libraryBook.Book.Title}
{path}
".Trim());
- }
+ }
- return true;
- }
- }
- class BackupSingle : BackupRunner
- {
- private LibraryBook _libraryBook { get; }
+ return true;
+ }
+ }
- protected override string SkipDialogText => @"
+ internal class BackupSingle : BackupRunner
+ {
+ private LibraryBook _libraryBook { get; }
+
+ protected override string SkipDialogText => @"
An error occurred while trying to process this book. Skip this book permanently?
{0}
@@ -507,24 +508,25 @@ An error occurred while trying to process this book. Skip this book permanently?
- Click NO to skip the book this time only. We'll try again later.
".Trim();
- protected override MessageBoxButtons SkipDialogButtons => MessageBoxButtons.YesNo;
- protected override DialogResult CreateSkipFileResult => DialogResult.Yes;
+ protected override MessageBoxButtons SkipDialogButtons => MessageBoxButtons.YesNo;
+ protected override DialogResult CreateSkipFileResult => DialogResult.Yes;
- public BackupSingle(LogMe logMe, IProcessable processable, LibraryBook libraryBook)
- : base(logMe, processable)
- {
- _libraryBook = libraryBook;
- }
+ public BackupSingle(LogMe logMe, IProcessable processable, LibraryBook libraryBook)
+ : base(logMe, processable)
+ {
+ _libraryBook = libraryBook;
+ }
- protected override async Task RunAsync()
- {
- if (_libraryBook is not null)
- await ProcessOneAsync(Processable.ProcessSingleAsync, _libraryBook);
- }
- }
- class BackupLoop : BackupRunner
- {
- protected override string SkipDialogText => @"
+ protected override async Task RunAsync()
+ {
+ if (_libraryBook is not null)
+ await ProcessOneAsync(Processable.ProcessSingleAsync, _libraryBook);
+ }
+ }
+
+ internal class BackupLoop : BackupRunner
+ {
+ protected override string SkipDialogText => @"
An error occurred while trying to process this book.
{0}
@@ -534,33 +536,33 @@ 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 override MessageBoxButtons SkipDialogButtons => MessageBoxButtons.AbortRetryIgnore;
- protected override DialogResult CreateSkipFileResult => DialogResult.Ignore;
+ protected override MessageBoxButtons SkipDialogButtons => MessageBoxButtons.AbortRetryIgnore;
+ protected override DialogResult CreateSkipFileResult => DialogResult.Ignore;
- public BackupLoop(LogMe logMe, IProcessable processable, AutomatedBackupsForm automatedBackupsForm)
- : base(logMe, processable, automatedBackupsForm) { }
+ public BackupLoop(LogMe logMe, IProcessable processable, AutomatedBackupsForm automatedBackupsForm)
+ : base(logMe, processable, automatedBackupsForm) { }
- protected override async Task RunAsync()
- {
- // support for 'skip this time only' requires state. iterators provide this state for free. therefore: use foreach/iterator here
- foreach (var libraryBook in Processable.GetValidLibraryBooks())
- {
- var keepGoing = await ProcessOneAsync(Processable.ProcessBookAsync_NoValidation, libraryBook);
- if (!keepGoing)
- return;
+ protected override async Task RunAsync()
+ {
+ // support for 'skip this time only' requires state. iterators provide this state for free. therefore: use foreach/iterator here
+ foreach (var libraryBook in Processable.GetValidLibraryBooks())
+ {
+ var keepGoing = await ProcessOneAsync(Processable.ProcessBookAsync_NoValidation, libraryBook);
+ if (!keepGoing)
+ return;
- if (AutomatedBackupsForm.IsDisposed)
- break;
+ if (AutomatedBackupsForm.IsDisposed)
+ break;
- if (!AutomatedBackupsForm.KeepGoing)
- {
- if (!AutomatedBackupsForm.KeepGoingChecked)
- LogMe.Info("'Keep going' is unchecked");
- return;
- }
- }
+ if (!AutomatedBackupsForm.KeepGoing)
+ {
+ if (!AutomatedBackupsForm.KeepGoingChecked)
+ LogMe.Info("'Keep going' is unchecked");
+ return;
+ }
+ }
- LogMe.Info("Done. All books have been processed");
- }
- }
+ LogMe.Info("Done. All books have been processed");
+ }
+ }
}
diff --git a/LibationWinForms/Dialogs/AccountsDialog.cs b/LibationWinForms/Dialogs/AccountsDialog.cs
index 0884bcfc..cfc5988e 100644
--- a/LibationWinForms/Dialogs/AccountsDialog.cs
+++ b/LibationWinForms/Dialogs/AccountsDialog.cs
@@ -1,21 +1,21 @@
-using System;
+using AudibleApi;
+using InternalUtilities;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
-using AudibleApi;
-using InternalUtilities;
namespace LibationWinForms.Dialogs
{
public partial class AccountsDialog : Form
{
- const string COL_Delete = nameof(DeleteAccount);
- const string COL_LibraryScan = nameof(LibraryScan);
- const string COL_AccountId = nameof(AccountId);
- const string COL_AccountName = nameof(AccountName);
- const string COL_Locale = nameof(Locale);
+ private const string COL_Delete = nameof(DeleteAccount);
+ private const string COL_LibraryScan = nameof(LibraryScan);
+ private const string COL_AccountId = nameof(AccountId);
+ private const string COL_AccountName = nameof(AccountName);
+ private const string COL_Locale = nameof(Locale);
- Form1 _parent { get; }
+ private Form1 _parent { get; }
public AccountsDialog(Form1 parent)
{
@@ -100,7 +100,7 @@ namespace LibationWinForms.Dialogs
this.Close();
}
- class AccountDto
+ private class AccountDto
{
public string AccountId { get; set; }
public string AccountName { get; set; }
diff --git a/LibationWinForms/Dialogs/AccountsDialog.resx b/LibationWinForms/Dialogs/AccountsDialog.resx
index 18b23138..f1117452 100644
--- a/LibationWinForms/Dialogs/AccountsDialog.resx
+++ b/LibationWinForms/Dialogs/AccountsDialog.resx
@@ -1,64 +1,5 @@
-
diff --git a/LibationWinForms/Dialogs/BookDetailsDialog.cs b/LibationWinForms/Dialogs/BookDetailsDialog.cs
index 27c5ab21..9a415200 100644
--- a/LibationWinForms/Dialogs/BookDetailsDialog.cs
+++ b/LibationWinForms/Dialogs/BookDetailsDialog.cs
@@ -3,25 +3,25 @@ using System.Windows.Forms;
namespace LibationWinForms.Dialogs
{
- public partial class BookDetailsDialog : Form
- {
- public string NewTags { get; private set; }
+ public partial class BookDetailsDialog : Form
+ {
+ public string NewTags { get; private set; }
- public BookDetailsDialog()
- {
- InitializeComponent();
- }
- public BookDetailsDialog(string title, string rawTags) : this()
- {
- this.Text = $"Edit Tags - {title}";
+ public BookDetailsDialog()
+ {
+ InitializeComponent();
+ }
+ public BookDetailsDialog(string title, string rawTags) : this()
+ {
+ this.Text = $"Edit Tags - {title}";
- this.newTagsTb.Text = rawTags;
- }
+ this.newTagsTb.Text = rawTags;
+ }
- private void SaveBtn_Click(object sender, EventArgs e)
- {
- NewTags = this.newTagsTb.Text;
- DialogResult = DialogResult.OK;
- }
- }
+ private void SaveBtn_Click(object sender, EventArgs e)
+ {
+ NewTags = this.newTagsTb.Text;
+ DialogResult = DialogResult.OK;
+ }
+ }
}
diff --git a/LibationWinForms/Dialogs/BookDetailsDialog.resx b/LibationWinForms/Dialogs/BookDetailsDialog.resx
index 1af7de15..e8ae276d 100644
--- a/LibationWinForms/Dialogs/BookDetailsDialog.resx
+++ b/LibationWinForms/Dialogs/BookDetailsDialog.resx
@@ -1,64 +1,5 @@
-
diff --git a/LibationWinForms/Dialogs/DirectoryOrCustomSelectControl.cs b/LibationWinForms/Dialogs/DirectoryOrCustomSelectControl.cs
index 5684d485..c0b5b28c 100644
--- a/LibationWinForms/Dialogs/DirectoryOrCustomSelectControl.cs
+++ b/LibationWinForms/Dialogs/DirectoryOrCustomSelectControl.cs
@@ -1,9 +1,7 @@
-using System;
+using FileManager;
+using System;
using System.Collections.Generic;
-using System.Linq;
using System.Windows.Forms;
-using Dinah.Core;
-using FileManager;
namespace LibationWinForms.Dialogs
{
@@ -58,7 +56,7 @@ namespace LibationWinForms.Dialogs
= knownDir != Configuration.KnownDirectories.None
// this could be a well known dir which isn't an option in this particular dropdown. This will always be true of LibationFiles
&& this.directorySelectControl.SelectDirectory(knownDir);
-
+
customDirectoryRb.Checked = !isKnown;
knownDirectoryRb.Checked = isKnown;
this.customTb.Text = isKnown ? "" : customDir;
diff --git a/LibationWinForms/Dialogs/DirectoryOrCustomSelectControl.resx b/LibationWinForms/Dialogs/DirectoryOrCustomSelectControl.resx
index f298a7be..e8ae276d 100644
--- a/LibationWinForms/Dialogs/DirectoryOrCustomSelectControl.resx
+++ b/LibationWinForms/Dialogs/DirectoryOrCustomSelectControl.resx
@@ -1,4 +1,5 @@
-
+
+
diff --git a/LibationWinForms/Dialogs/DirectorySelectControl.cs b/LibationWinForms/Dialogs/DirectorySelectControl.cs
index ab8def7a..639886ba 100644
--- a/LibationWinForms/Dialogs/DirectorySelectControl.cs
+++ b/LibationWinForms/Dialogs/DirectorySelectControl.cs
@@ -1,9 +1,9 @@
-using System;
+using Dinah.Core;
+using FileManager;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
-using Dinah.Core;
-using FileManager;
namespace LibationWinForms.Dialogs
{
@@ -13,7 +13,7 @@ namespace LibationWinForms.Dialogs
{
public string Description { get; }
public Configuration.KnownDirectories Value { get; }
- private DirectorySelectControl _parentControl;
+ private readonly DirectorySelectControl _parentControl;
public string FullPath => _parentControl.AddSubDirectoryToPath(Configuration.GetKnownDirectoryPath(Value));
diff --git a/LibationWinForms/Dialogs/DirectorySelectControl.resx b/LibationWinForms/Dialogs/DirectorySelectControl.resx
index f298a7be..e8ae276d 100644
--- a/LibationWinForms/Dialogs/DirectorySelectControl.resx
+++ b/LibationWinForms/Dialogs/DirectorySelectControl.resx
@@ -1,4 +1,5 @@
-
+
+
diff --git a/LibationWinForms/Dialogs/EditQuickFilters.cs b/LibationWinForms/Dialogs/EditQuickFilters.cs
index abb183ce..999e9b2f 100644
--- a/LibationWinForms/Dialogs/EditQuickFilters.cs
+++ b/LibationWinForms/Dialogs/EditQuickFilters.cs
@@ -1,101 +1,100 @@
-using System;
+using FileManager;
+using System;
using System.Linq;
using System.Windows.Forms;
-using FileManager;
namespace LibationWinForms.Dialogs
{
public partial class EditQuickFilters : Form
- {
- const string BLACK_UP_POINTING_TRIANGLE = "\u25B2";
- const string BLACK_DOWN_POINTING_TRIANGLE = "\u25BC";
+ {
+ private const string BLACK_UP_POINTING_TRIANGLE = "\u25B2";
+ private const string BLACK_DOWN_POINTING_TRIANGLE = "\u25BC";
+ private const string COL_Original = nameof(Original);
+ private const string COL_Delete = nameof(Delete);
+ private const string COL_Filter = nameof(Filter);
+ private const string COL_MoveUp = nameof(MoveUp);
+ private const string COL_MoveDown = nameof(MoveDown);
- const string COL_Original = nameof(Original);
- const string COL_Delete = nameof(Delete);
- const string COL_Filter = nameof(Filter);
- const string COL_MoveUp = nameof(MoveUp);
- const string COL_MoveDown = nameof(MoveDown);
+ private Form1 _parent { get; }
- Form1 _parent { get; }
+ public EditQuickFilters(Form1 parent)
+ {
+ _parent = parent;
- public EditQuickFilters(Form1 parent)
- {
- _parent = parent;
+ InitializeComponent();
- InitializeComponent();
+ dataGridView1.Columns[COL_Filter].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
- dataGridView1.Columns[COL_Filter].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
+ populateGridValues();
+ }
- populateGridValues();
- }
+ private void populateGridValues()
+ {
+ var filters = QuickFilters.Filters;
+ if (!filters.Any())
+ return;
- private void populateGridValues()
- {
- var filters = QuickFilters.Filters;
- if (!filters.Any())
- return;
+ foreach (var filter in filters)
+ dataGridView1.Rows.Add(filter, "X", filter, BLACK_UP_POINTING_TRIANGLE, BLACK_DOWN_POINTING_TRIANGLE);
+ }
- foreach (var filter in filters)
- dataGridView1.Rows.Add(filter, "X", filter, BLACK_UP_POINTING_TRIANGLE, BLACK_DOWN_POINTING_TRIANGLE);
- }
+ private void dataGridView1_DefaultValuesNeeded(object sender, DataGridViewRowEventArgs e)
+ {
+ e.Row.Cells[COL_Delete].Value = "X";
+ e.Row.Cells[COL_MoveUp].Value = BLACK_UP_POINTING_TRIANGLE;
+ e.Row.Cells[COL_MoveDown].Value = BLACK_DOWN_POINTING_TRIANGLE;
+ }
- private void dataGridView1_DefaultValuesNeeded(object sender, DataGridViewRowEventArgs e)
- {
- e.Row.Cells[COL_Delete].Value = "X";
- e.Row.Cells[COL_MoveUp].Value = BLACK_UP_POINTING_TRIANGLE;
- e.Row.Cells[COL_MoveDown].Value = BLACK_DOWN_POINTING_TRIANGLE;
- }
+ private void saveBtn_Click(object sender, EventArgs e)
+ {
+ var list = dataGridView1.Rows
+ .OfType()
+ .Select(r => r.Cells[COL_Filter].Value?.ToString())
+ .ToList();
+ QuickFilters.ReplaceAll(list);
- private void saveBtn_Click(object sender, EventArgs e)
- {
- var list = dataGridView1.Rows
- .OfType()
- .Select(r => r.Cells[COL_Filter].Value?.ToString())
- .ToList();
- QuickFilters.ReplaceAll(list);
+ _parent.UpdateFilterDropDown();
+ this.DialogResult = DialogResult.OK;
+ this.Close();
+ }
- _parent.UpdateFilterDropDown();
- this.DialogResult = DialogResult.OK;
- this.Close();
- }
+ private void cancelBtn_Click(object sender, EventArgs e)
+ {
+ this.DialogResult = DialogResult.Cancel;
+ this.Close();
+ }
- private void cancelBtn_Click(object sender, EventArgs e)
- {
- this.DialogResult = DialogResult.Cancel;
- this.Close();
- }
+ private void DataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
+ {
+ var dgv = (DataGridView)sender;
- private void DataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
- {
- var dgv = (DataGridView)sender;
-
- var col = dgv.Columns[e.ColumnIndex];
- if (col is DataGridViewButtonColumn && e.RowIndex >= 0)
- {
- var row = dgv.Rows[e.RowIndex];
- switch (col.Name)
- {
- case COL_Delete:
- // if final/edit row: do nothing
- if (e.RowIndex < dgv.RowCount - 1)
- dgv.Rows.Remove(row);
- break;
- case COL_MoveUp:
- // if top: do nothing
- if (e.RowIndex < 1)
- break;
- dgv.Rows.Remove(row);
- dgv.Rows.Insert(e.RowIndex - 1, row);
- break;
- case COL_MoveDown:
- // if final/edit row or bottom filter row: do nothing
- if (e.RowIndex >= dgv.RowCount - 2)
- break;
- dgv.Rows.Remove(row);
- dgv.Rows.Insert(e.RowIndex + 1, row);
- break;
- }
- }
- }
- }
+ var col = dgv.Columns[e.ColumnIndex];
+ if (col is DataGridViewButtonColumn && e.RowIndex >= 0)
+ {
+ var row = dgv.Rows[e.RowIndex];
+ switch (col.Name)
+ {
+ case COL_Delete:
+ // if final/edit row: do nothing
+ if (e.RowIndex < dgv.RowCount - 1)
+ dgv.Rows.Remove(row);
+ break;
+ case COL_MoveUp:
+ // if top: do nothing
+ if (e.RowIndex < 1)
+ break;
+ dgv.Rows.Remove(row);
+ dgv.Rows.Insert(e.RowIndex - 1, row);
+ break;
+ case COL_MoveDown:
+ // if final/edit row or bottom filter row: do nothing
+ if (e.RowIndex >= dgv.RowCount - 2)
+ break;
+ dgv.Rows.Remove(row);
+ dgv.Rows.Insert(e.RowIndex + 1, row);
+ break;
+ }
+ }
+ }
+ }
}
diff --git a/LibationWinForms/Dialogs/EditQuickFilters.resx b/LibationWinForms/Dialogs/EditQuickFilters.resx
index 714d166d..9c876821 100644
--- a/LibationWinForms/Dialogs/EditQuickFilters.resx
+++ b/LibationWinForms/Dialogs/EditQuickFilters.resx
@@ -1,64 +1,5 @@
-
diff --git a/LibationWinForms/Dialogs/IndexLibraryDialog.cs b/LibationWinForms/Dialogs/IndexLibraryDialog.cs
index e534cc28..0026fe79 100644
--- a/LibationWinForms/Dialogs/IndexLibraryDialog.cs
+++ b/LibationWinForms/Dialogs/IndexLibraryDialog.cs
@@ -1,8 +1,8 @@
-using System;
-using System.Windows.Forms;
-using ApplicationServices;
+using ApplicationServices;
using InternalUtilities;
using LibationWinForms.Login;
+using System;
+using System.Windows.Forms;
namespace LibationWinForms.Dialogs
{
diff --git a/LibationWinForms/Dialogs/IndexLibraryDialog.resx b/LibationWinForms/Dialogs/IndexLibraryDialog.resx
index 1af7de15..e8ae276d 100644
--- a/LibationWinForms/Dialogs/IndexLibraryDialog.resx
+++ b/LibationWinForms/Dialogs/IndexLibraryDialog.resx
@@ -1,64 +1,5 @@
-
diff --git a/LibationWinForms/Dialogs/LibationFilesDialog.cs b/LibationWinForms/Dialogs/LibationFilesDialog.cs
index c6c67383..09ec8096 100644
--- a/LibationWinForms/Dialogs/LibationFilesDialog.cs
+++ b/LibationWinForms/Dialogs/LibationFilesDialog.cs
@@ -1,6 +1,6 @@
-using System;
+using FileManager;
+using System;
using System.Windows.Forms;
-using FileManager;
namespace LibationWinForms.Dialogs
{
diff --git a/LibationWinForms/Dialogs/Login/AudibleLoginDialog.cs b/LibationWinForms/Dialogs/Login/AudibleLoginDialog.cs
index 426bae71..68fb2a99 100644
--- a/LibationWinForms/Dialogs/Login/AudibleLoginDialog.cs
+++ b/LibationWinForms/Dialogs/Login/AudibleLoginDialog.cs
@@ -1,7 +1,7 @@
-using System;
-using System.Windows.Forms;
-using Dinah.Core;
+using Dinah.Core;
using InternalUtilities;
+using System;
+using System.Windows.Forms;
namespace LibationWinForms.Dialogs.Login
{
diff --git a/LibationWinForms/Dialogs/Login/MfaDialog.cs b/LibationWinForms/Dialogs/Login/MfaDialog.cs
index add77588..e369b367 100644
--- a/LibationWinForms/Dialogs/Login/MfaDialog.cs
+++ b/LibationWinForms/Dialogs/Login/MfaDialog.cs
@@ -1,11 +1,5 @@
using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
-using System.Drawing;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using System.Windows.Forms;
namespace LibationWinForms.Dialogs.Login
@@ -14,7 +8,7 @@ namespace LibationWinForms.Dialogs.Login
{
private RadioButton[] radioButtons { get; }
- AudibleApi.MfaConfig _mfaConfig { get; }
+ private AudibleApi.MfaConfig _mfaConfig { get; }
public MfaDialog(AudibleApi.MfaConfig mfaConfig)
{
@@ -32,7 +26,8 @@ namespace LibationWinForms.Dialogs.Login
setRadioButton(1, this.radioButton2);
setRadioButton(2, this.radioButton3);
- Serilog.Log.Logger.Information("{@DebugInfo}", new {
+ Serilog.Log.Logger.Information("{@DebugInfo}", new
+ {
paramButtonCount = mfaConfig.Buttons.Count,
visibleRadioButtonCount = radioButtons.Count(rb => rb.Visible)
});
@@ -65,7 +60,8 @@ namespace LibationWinForms.Dialogs.Login
{
var selected = radioButtons.FirstOrDefault(rb => rb.Checked);
- Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new {
+ Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new
+ {
rb1_visible = radioButton1.Visible,
rb1_checked = radioButton1.Checked,
diff --git a/LibationWinForms/Dialogs/Login/WinformResponder.cs b/LibationWinForms/Dialogs/Login/WinformResponder.cs
index 28507f4d..d57a01d7 100644
--- a/LibationWinForms/Dialogs/Login/WinformResponder.cs
+++ b/LibationWinForms/Dialogs/Login/WinformResponder.cs
@@ -1,5 +1,4 @@
-using System;
-using AudibleApi;
+using AudibleApi;
using InternalUtilities;
using LibationWinForms.Dialogs.Login;
diff --git a/LibationWinForms/Dialogs/MessageBoxAlertAdminDialog.cs b/LibationWinForms/Dialogs/MessageBoxAlertAdminDialog.cs
index 14f2e76f..6ef2e320 100644
--- a/LibationWinForms/Dialogs/MessageBoxAlertAdminDialog.cs
+++ b/LibationWinForms/Dialogs/MessageBoxAlertAdminDialog.cs
@@ -1,7 +1,7 @@
-using System;
+using Dinah.Core;
+using System;
using System.Drawing;
using System.Windows.Forms;
-using Dinah.Core;
namespace LibationWinForms.Dialogs
{
diff --git a/LibationWinForms/Dialogs/RemoveBooksDialog.cs b/LibationWinForms/Dialogs/RemoveBooksDialog.cs
index a31ca838..40457837 100644
--- a/LibationWinForms/Dialogs/RemoveBooksDialog.cs
+++ b/LibationWinForms/Dialogs/RemoveBooksDialog.cs
@@ -3,27 +3,27 @@ using DataLayer;
using InternalUtilities;
using LibationWinForms.Login;
using System;
+using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Windows.Forms;
-using System.Collections;
namespace LibationWinForms.Dialogs
{
- public partial class RemoveBooksDialog : Form
+ public partial class RemoveBooksDialog : Form
{
public bool BooksRemoved { get; private set; }
private Account[] _accounts { get; }
- private List _libraryBooks;
- private SortableBindingList2 _removableGridEntries;
- private string _labelFormat;
+ private readonly List _libraryBooks;
+ private readonly SortableBindingList2 _removableGridEntries;
+ private readonly string _labelFormat;
private int SelectedCount => SelectedEntries?.Count() ?? 0;
private IEnumerable SelectedEntries => _removableGridEntries?.Where(b => b.Remove);
- public RemoveBooksDialog(params Account[] accounts)
+ public RemoveBooksDialog(params Account[] accounts)
{
_libraryBooks = DbContexts.GetContext().GetLibrary_Flat_NoTracking();
_accounts = accounts;
@@ -32,8 +32,8 @@ namespace LibationWinForms.Dialogs
_labelFormat = label1.Text;
_dataGridView.CellContentClick += (s, e) => _dataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
- _dataGridView.CellValueChanged += DataGridView1_CellValueChanged;
- _dataGridView.BindingContextChanged += (s, e) => UpdateSelection();
+ _dataGridView.CellValueChanged += DataGridView1_CellValueChanged;
+ _dataGridView.BindingContextChanged += (s, e) => UpdateSelection();
var orderedGridEntries = _libraryBooks
.Select(lb => new RemovableGridEntry(lb))
@@ -46,7 +46,7 @@ namespace LibationWinForms.Dialogs
_dataGridView.Enabled = false;
}
- private void DataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
+ private void DataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 0)
UpdateSelection();
@@ -78,9 +78,9 @@ namespace LibationWinForms.Dialogs
ex);
}
finally
- {
+ {
_dataGridView.Enabled = true;
- }
+ }
}
private void btnRemoveBooks_Click(object sender, EventArgs e)
@@ -122,15 +122,15 @@ namespace LibationWinForms.Dialogs
}
}
private void UpdateSelection()
- {
+ {
_dataGridView.Sort(_dataGridView.Columns[0], ListSortDirection.Descending);
var selectedCount = SelectedCount;
label1.Text = string.Format(_labelFormat, selectedCount, selectedCount != 1 ? "s" : string.Empty);
btnRemoveBooks.Enabled = selectedCount > 0;
- }
- }
+ }
+ }
- internal class RemovableGridEntry : GridEntry
+ internal class RemovableGridEntry : GridEntry
{
private static readonly IComparer BoolComparer = new ObjectComparer();
@@ -153,18 +153,18 @@ namespace LibationWinForms.Dialogs
}
}
- public override object GetMemberValue(string memberName)
- {
+ public override object GetMemberValue(string memberName)
+ {
if (memberName == nameof(Remove))
return Remove;
- return base.GetMemberValue(memberName);
- }
+ return base.GetMemberValue(memberName);
+ }
- public override IComparer GetMemberComparer(Type memberType)
+ public override IComparer GetMemberComparer(Type memberType)
{
if (memberType == typeof(bool))
return BoolComparer;
return base.GetMemberComparer(memberType);
- }
- }
+ }
+ }
}
diff --git a/LibationWinForms/Dialogs/RemoveBooksDialog.resx b/LibationWinForms/Dialogs/RemoveBooksDialog.resx
index a3058bc8..5ffc920f 100644
--- a/LibationWinForms/Dialogs/RemoveBooksDialog.resx
+++ b/LibationWinForms/Dialogs/RemoveBooksDialog.resx
@@ -1,4 +1,5 @@
-
+
+
@@ -57,7 +58,4 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- 17, 17
-
\ No newline at end of file
diff --git a/LibationWinForms/Dialogs/ScanAccountsDialog.cs b/LibationWinForms/Dialogs/ScanAccountsDialog.cs
index 6f31ef06..eebd720e 100644
--- a/LibationWinForms/Dialogs/ScanAccountsDialog.cs
+++ b/LibationWinForms/Dialogs/ScanAccountsDialog.cs
@@ -1,10 +1,7 @@
-using System;
+using InternalUtilities;
+using System;
using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Threading.Tasks;
using System.Windows.Forms;
-using InternalUtilities;
namespace LibationWinForms.Dialogs
{
@@ -12,7 +9,7 @@ namespace LibationWinForms.Dialogs
{
public List CheckedAccounts { get; } = new List();
- Form1 _parent { get; }
+ private Form1 _parent { get; }
public ScanAccountsDialog(Form1 parent)
{
@@ -21,7 +18,7 @@ namespace LibationWinForms.Dialogs
InitializeComponent();
}
- class listItem
+ private class listItem
{
public Account Account { get; set; }
public string Text { get; set; }
diff --git a/LibationWinForms/Dialogs/ScanAccountsDialog.resx b/LibationWinForms/Dialogs/ScanAccountsDialog.resx
index 1af7de15..e8ae276d 100644
--- a/LibationWinForms/Dialogs/ScanAccountsDialog.resx
+++ b/LibationWinForms/Dialogs/ScanAccountsDialog.resx
@@ -1,64 +1,5 @@
-
diff --git a/LibationWinForms/Dialogs/SearchSyntaxDialog.cs b/LibationWinForms/Dialogs/SearchSyntaxDialog.cs
index 1300a74a..bd8a2b87 100644
--- a/LibationWinForms/Dialogs/SearchSyntaxDialog.cs
+++ b/LibationWinForms/Dialogs/SearchSyntaxDialog.cs
@@ -3,18 +3,18 @@ using System.Windows.Forms;
namespace LibationWinForms.Dialogs
{
- public partial class SearchSyntaxDialog : Form
- {
- public SearchSyntaxDialog()
- {
- InitializeComponent();
+ public partial class SearchSyntaxDialog : Form
+ {
+ public SearchSyntaxDialog()
+ {
+ InitializeComponent();
- label2.Text += "\r\n\r\n" + string.Join("\r\n", LibationSearchEngine.SearchEngine.GetSearchStringFields());
- label3.Text += "\r\n\r\n" + string.Join("\r\n", LibationSearchEngine.SearchEngine.GetSearchNumberFields());
- label4.Text += "\r\n\r\n" + string.Join("\r\n", LibationSearchEngine.SearchEngine.GetSearchBoolFields());
- label5.Text += "\r\n\r\n" + string.Join("\r\n", LibationSearchEngine.SearchEngine.GetSearchIdFields());
- }
+ label2.Text += "\r\n\r\n" + string.Join("\r\n", LibationSearchEngine.SearchEngine.GetSearchStringFields());
+ label3.Text += "\r\n\r\n" + string.Join("\r\n", LibationSearchEngine.SearchEngine.GetSearchNumberFields());
+ label4.Text += "\r\n\r\n" + string.Join("\r\n", LibationSearchEngine.SearchEngine.GetSearchBoolFields());
+ label5.Text += "\r\n\r\n" + string.Join("\r\n", LibationSearchEngine.SearchEngine.GetSearchIdFields());
+ }
- private void CloseBtn_Click(object sender, EventArgs e) => this.Close();
- }
+ private void CloseBtn_Click(object sender, EventArgs e) => this.Close();
+ }
}
diff --git a/LibationWinForms/Dialogs/SearchSyntaxDialog.resx b/LibationWinForms/Dialogs/SearchSyntaxDialog.resx
index 1af7de15..e8ae276d 100644
--- a/LibationWinForms/Dialogs/SearchSyntaxDialog.resx
+++ b/LibationWinForms/Dialogs/SearchSyntaxDialog.resx
@@ -1,64 +1,5 @@
-
diff --git a/LibationWinForms/Dialogs/SettingsDialog.cs b/LibationWinForms/Dialogs/SettingsDialog.cs
index fee85e1d..81e9c0c8 100644
--- a/LibationWinForms/Dialogs/SettingsDialog.cs
+++ b/LibationWinForms/Dialogs/SettingsDialog.cs
@@ -1,15 +1,15 @@
-using System;
+using Dinah.Core;
+using FileManager;
+using System;
using System.IO;
using System.Windows.Forms;
-using Dinah.Core;
-using FileManager;
namespace LibationWinForms.Dialogs
{
public partial class SettingsDialog : Form
{
- Configuration config { get; } = Configuration.Instance;
- Func desc { get; } = Configuration.GetDescription;
+ private Configuration config { get; } = Configuration.Instance;
+ private Func desc { get; } = Configuration.GetDescription;
public SettingsDialog() => InitializeComponent();
@@ -57,13 +57,13 @@ namespace LibationWinForms.Dialogs
inProgressSelectControl.SelectDirectory(config.InProgress);
}
- private void allowLibationFixupCbox_CheckedChanged(object sender, EventArgs e)
- {
+ private void allowLibationFixupCbox_CheckedChanged(object sender, EventArgs e)
+ {
convertLosslessRb.Enabled = allowLibationFixupCbox.Checked;
convertLossyRb.Enabled = allowLibationFixupCbox.Checked;
if (!allowLibationFixupCbox.Checked)
- {
+ {
convertLosslessRb.Checked = true;
}
}
diff --git a/LibationWinForms/Dialogs/SettingsDialog.resx b/LibationWinForms/Dialogs/SettingsDialog.resx
index f298a7be..e8ae276d 100644
--- a/LibationWinForms/Dialogs/SettingsDialog.resx
+++ b/LibationWinForms/Dialogs/SettingsDialog.resx
@@ -1,4 +1,5 @@
-
+
+
diff --git a/LibationWinForms/Dialogs/SetupDialog.resx b/LibationWinForms/Dialogs/SetupDialog.resx
index 859984c0..9e9a5108 100644
--- a/LibationWinForms/Dialogs/SetupDialog.resx
+++ b/LibationWinForms/Dialogs/SetupDialog.resx
@@ -1,4 +1,5 @@
-
+
+
diff --git a/LibationWinForms/Form1.cs b/LibationWinForms/Form1.cs
index 30bba304..6f9223d3 100644
--- a/LibationWinForms/Form1.cs
+++ b/LibationWinForms/Form1.cs
@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-using ApplicationServices;
+using ApplicationServices;
using DataLayer;
using Dinah.Core;
using Dinah.Core.Drawing;
@@ -11,371 +6,376 @@ using Dinah.Core.Windows.Forms;
using FileManager;
using InternalUtilities;
using LibationWinForms.Dialogs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
namespace LibationWinForms
{
- public partial class Form1 : Form
- {
- private string backupsCountsLbl_Format { get; }
- private string pdfsCountsLbl_Format { get; }
+ public partial class Form1 : Form
+ {
+ private string backupsCountsLbl_Format { get; }
+ private string pdfsCountsLbl_Format { get; }
private string visibleCountLbl_Format { get; }
private string beginBookBackupsToolStripMenuItem_format { get; }
private string beginPdfBackupsToolStripMenuItem_format { get; }
public Form1()
- {
- InitializeComponent();
+ {
+ InitializeComponent();
- // back up string formats
- backupsCountsLbl_Format = backupsCountsLbl.Text;
- pdfsCountsLbl_Format = pdfsCountsLbl.Text;
- visibleCountLbl_Format = visibleCountLbl.Text;
+ // back up string formats
+ backupsCountsLbl_Format = backupsCountsLbl.Text;
+ pdfsCountsLbl_Format = pdfsCountsLbl.Text;
+ visibleCountLbl_Format = visibleCountLbl.Text;
beginBookBackupsToolStripMenuItem_format = beginBookBackupsToolStripMenuItem.Text;
- beginPdfBackupsToolStripMenuItem_format = beginPdfBackupsToolStripMenuItem.Text;
+ beginPdfBackupsToolStripMenuItem_format = beginPdfBackupsToolStripMenuItem.Text;
- // after backing up formats: can set default/temp visible text
- backupsCountsLbl.Text = "[Calculating backed up book quantities]";
- pdfsCountsLbl.Text = "[Calculating backed up PDFs]";
+ // after backing up formats: can set default/temp visible text
+ backupsCountsLbl.Text = "[Calculating backed up book quantities]";
+ pdfsCountsLbl.Text = "[Calculating backed up PDFs]";
setVisibleCount(null, 0);
- if (this.DesignMode)
- return;
+ if (this.DesignMode)
+ return;
- // independent UI updates
- this.Load += setBackupCountsAsync;
- this.Load += (_, __) => RestoreSizeAndLocation();
- this.Load += (_, __) => RefreshImportMenu();
+ // independent UI updates
+ this.Load += setBackupCountsAsync;
+ this.Load += (_, __) => RestoreSizeAndLocation();
+ this.Load += (_, __) => RefreshImportMenu();
- var format = System.Drawing.Imaging.ImageFormat.Jpeg;
- PictureStorage.SetDefaultImage(PictureSize._80x80, Properties.Resources.default_cover_80x80.ToBytes(format));
- PictureStorage.SetDefaultImage(PictureSize._300x300, Properties.Resources.default_cover_300x300.ToBytes(format));
- PictureStorage.SetDefaultImage(PictureSize._500x500, Properties.Resources.default_cover_500x500.ToBytes(format));
- }
+ var format = System.Drawing.Imaging.ImageFormat.Jpeg;
+ PictureStorage.SetDefaultImage(PictureSize._80x80, Properties.Resources.default_cover_80x80.ToBytes(format));
+ PictureStorage.SetDefaultImage(PictureSize._300x300, Properties.Resources.default_cover_300x300.ToBytes(format));
+ PictureStorage.SetDefaultImage(PictureSize._500x500, Properties.Resources.default_cover_500x500.ToBytes(format));
+ }
- private void Form1_Load(object sender, EventArgs e)
+ private void Form1_Load(object sender, EventArgs e)
{
- if (this.DesignMode)
- return;
+ if (this.DesignMode)
+ return;
- reloadGrid();
-
- // also applies filter. ONLY call AFTER loading grid
- loadInitialQuickFilterState();
- }
+ reloadGrid();
- private void Form1_FormClosing(object sender, FormClosingEventArgs e)
- {
- SaveSizeAndLocation();
- }
+ // also applies filter. ONLY call AFTER loading grid
+ loadInitialQuickFilterState();
+ }
+
+ private void Form1_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ SaveSizeAndLocation();
+ }
private void RestoreSizeAndLocation()
{
- var config = Configuration.Instance;
+ var config = Configuration.Instance;
- var width = config.MainFormWidth;
- var height = config.MainFormHeight;
+ var width = config.MainFormWidth;
+ var height = config.MainFormHeight;
- // too small -- something must have gone wrong. use defaults
- if (width < 25 || height < 25)
- {
- width = 1023;
- height = 578;
- }
+ // too small -- something must have gone wrong. use defaults
+ if (width < 25 || height < 25)
+ {
+ width = 1023;
+ height = 578;
+ }
- // Fit to the current screen size in case the screen resolution changed since the size was last persisted
- if (width > Screen.PrimaryScreen.WorkingArea.Width)
- width = Screen.PrimaryScreen.WorkingArea.Width;
- if (height > Screen.PrimaryScreen.WorkingArea.Height)
- height = Screen.PrimaryScreen.WorkingArea.Height;
+ // Fit to the current screen size in case the screen resolution changed since the size was last persisted
+ if (width > Screen.PrimaryScreen.WorkingArea.Width)
+ width = Screen.PrimaryScreen.WorkingArea.Width;
+ if (height > Screen.PrimaryScreen.WorkingArea.Height)
+ height = Screen.PrimaryScreen.WorkingArea.Height;
- var x = config.MainFormX;
- var y = config.MainFormY;
+ var x = config.MainFormX;
+ var y = config.MainFormY;
- var rect = new System.Drawing.Rectangle(x, y, width, height);
+ var rect = new System.Drawing.Rectangle(x, y, width, height);
- // is proposed rect on a screen?
- if (Screen.AllScreens.Any(screen => screen.WorkingArea.Contains(rect)))
- {
- this.StartPosition = FormStartPosition.Manual;
- this.DesktopBounds = rect;
- }
- else
- {
- this.StartPosition = FormStartPosition.WindowsDefaultLocation;
- this.Size = rect.Size;
- }
+ // is proposed rect on a screen?
+ if (Screen.AllScreens.Any(screen => screen.WorkingArea.Contains(rect)))
+ {
+ this.StartPosition = FormStartPosition.Manual;
+ this.DesktopBounds = rect;
+ }
+ else
+ {
+ this.StartPosition = FormStartPosition.WindowsDefaultLocation;
+ this.Size = rect.Size;
+ }
- // FINAL: for Maximized: start normal state, set size and location, THEN set max state
- this.WindowState = config.MainFormIsMaximized ? FormWindowState.Maximized : FormWindowState.Normal;
- }
+ // FINAL: for Maximized: start normal state, set size and location, THEN set max state
+ this.WindowState = config.MainFormIsMaximized ? FormWindowState.Maximized : FormWindowState.Normal;
+ }
- private void SaveSizeAndLocation()
- {
- System.Drawing.Point location;
- System.Drawing.Size size;
+ private void SaveSizeAndLocation()
+ {
+ System.Drawing.Point location;
+ System.Drawing.Size size;
- // save location and size if the state is normal
- if (this.WindowState == FormWindowState.Normal)
- {
- location = this.Location;
- size = this.Size;
- }
- else
- {
- // save the RestoreBounds if the form is minimized or maximized
- location = this.RestoreBounds.Location;
- size = this.RestoreBounds.Size;
- }
-
- var config = Configuration.Instance;
+ // save location and size if the state is normal
+ if (this.WindowState == FormWindowState.Normal)
+ {
+ location = this.Location;
+ size = this.Size;
+ }
+ else
+ {
+ // save the RestoreBounds if the form is minimized or maximized
+ location = this.RestoreBounds.Location;
+ size = this.RestoreBounds.Size;
+ }
- config.MainFormX = location.X;
- config.MainFormY = location.Y;
+ var config = Configuration.Instance;
- config.MainFormWidth = size.Width;
- config.MainFormHeight = size.Height;
+ config.MainFormX = location.X;
+ config.MainFormY = location.Y;
- config.MainFormIsMaximized = this.WindowState == FormWindowState.Maximized;
- }
+ config.MainFormWidth = size.Width;
+ config.MainFormHeight = size.Height;
- #region reload grid
- bool isProcessingGridSelect = false;
- private void reloadGrid()
- {
- // suppressed filter while init'ing UI
- var prev_isProcessingGridSelect = isProcessingGridSelect;
- isProcessingGridSelect = true;
- setGrid();
- isProcessingGridSelect = prev_isProcessingGridSelect;
+ config.MainFormIsMaximized = this.WindowState == FormWindowState.Maximized;
+ }
- // UI init complete. now we can apply filter
- doFilter(lastGoodFilter);
- }
+ #region reload grid
+ private bool isProcessingGridSelect = false;
+ private void reloadGrid()
+ {
+ // suppressed filter while init'ing UI
+ var prev_isProcessingGridSelect = isProcessingGridSelect;
+ isProcessingGridSelect = true;
+ setGrid();
+ isProcessingGridSelect = prev_isProcessingGridSelect;
- ProductsGrid currProductsGrid;
- private void setGrid()
- {
- SuspendLayout();
- {
- if (currProductsGrid != null)
- {
- gridPanel.Controls.Remove(currProductsGrid);
- currProductsGrid.VisibleCountChanged -= setVisibleCount;
- currProductsGrid.BackupCountsChanged -= setBackupCountsAsync;
- currProductsGrid.Dispose();
- }
+ // UI init complete. now we can apply filter
+ doFilter(lastGoodFilter);
+ }
- currProductsGrid = new ProductsGrid { Dock = DockStyle.Fill };
- currProductsGrid.VisibleCountChanged += setVisibleCount;
- currProductsGrid.BackupCountsChanged += setBackupCountsAsync;
- gridPanel.UIThread(() => gridPanel.Controls.Add(currProductsGrid));
- currProductsGrid.Display();
- }
- ResumeLayout();
- }
- #endregion
+ private ProductsGrid currProductsGrid;
+ private void setGrid()
+ {
+ SuspendLayout();
+ {
+ if (currProductsGrid != null)
+ {
+ gridPanel.Controls.Remove(currProductsGrid);
+ currProductsGrid.VisibleCountChanged -= setVisibleCount;
+ currProductsGrid.BackupCountsChanged -= setBackupCountsAsync;
+ currProductsGrid.Dispose();
+ }
- #region bottom: qty books visible
- private void setVisibleCount(object _, int qty) => visibleCountLbl.Text = string.Format(visibleCountLbl_Format, qty);
- #endregion
+ currProductsGrid = new ProductsGrid { Dock = DockStyle.Fill };
+ currProductsGrid.VisibleCountChanged += setVisibleCount;
+ currProductsGrid.BackupCountsChanged += setBackupCountsAsync;
+ gridPanel.UIThread(() => gridPanel.Controls.Add(currProductsGrid));
+ currProductsGrid.Display();
+ }
+ ResumeLayout();
+ }
+ #endregion
- #region bottom: backup counts
- private async void setBackupCountsAsync(object _, object __)
- {
- LibraryCommands.LibraryStats libraryStats = null;
- await Task.Run(() => libraryStats = LibraryCommands.GetCounts());
+ #region bottom: qty books visible
+ private void setVisibleCount(object _, int qty) => visibleCountLbl.Text = string.Format(visibleCountLbl_Format, qty);
+ #endregion
- setBookBackupCounts(libraryStats.booksFullyBackedUp, libraryStats.booksDownloadedOnly, libraryStats.booksNoProgress);
- setPdfBackupCounts(libraryStats.pdfsDownloaded, libraryStats.pdfsNotDownloaded);
- }
- private void setBookBackupCounts(int booksFullyBackedUp, int booksDownloadedOnly, int booksNoProgress)
- {
- // enable/disable export
- var hasResults = 0 < (booksFullyBackedUp + booksDownloadedOnly + booksNoProgress);
- exportLibraryToolStripMenuItem.Enabled = hasResults;
+ #region bottom: backup counts
+ private async void setBackupCountsAsync(object _, object __)
+ {
+ LibraryCommands.LibraryStats libraryStats = null;
+ await Task.Run(() => libraryStats = LibraryCommands.GetCounts());
- // update bottom numbers
- var pending = booksNoProgress + booksDownloadedOnly;
- var statusStripText
- = !hasResults ? "No books. Begin by importing your library"
- : pending > 0 ? string.Format(backupsCountsLbl_Format, booksNoProgress, booksDownloadedOnly, booksFullyBackedUp)
- : $"All {"book".PluralizeWithCount(booksFullyBackedUp)} backed up";
+ setBookBackupCounts(libraryStats.booksFullyBackedUp, libraryStats.booksDownloadedOnly, libraryStats.booksNoProgress);
+ setPdfBackupCounts(libraryStats.pdfsDownloaded, libraryStats.pdfsNotDownloaded);
+ }
+ private void setBookBackupCounts(int booksFullyBackedUp, int booksDownloadedOnly, int booksNoProgress)
+ {
+ // enable/disable export
+ var hasResults = 0 < (booksFullyBackedUp + booksDownloadedOnly + booksNoProgress);
+ exportLibraryToolStripMenuItem.Enabled = hasResults;
- // update menu item
- var menuItemText
- = pending > 0
- ? $"{pending} remaining"
- : "All books have been liberated";
+ // update bottom numbers
+ var pending = booksNoProgress + booksDownloadedOnly;
+ var statusStripText
+ = !hasResults ? "No books. Begin by importing your library"
+ : pending > 0 ? string.Format(backupsCountsLbl_Format, booksNoProgress, booksDownloadedOnly, booksFullyBackedUp)
+ : $"All {"book".PluralizeWithCount(booksFullyBackedUp)} backed up";
- // update UI
- statusStrip1.UIThread(() => backupsCountsLbl.Text = statusStripText);
- menuStrip1.UIThread(() => beginBookBackupsToolStripMenuItem.Enabled = pending > 0);
- menuStrip1.UIThread(() => beginBookBackupsToolStripMenuItem.Text = string.Format(beginBookBackupsToolStripMenuItem_format, menuItemText));
- }
- private void setPdfBackupCounts(int pdfsDownloaded, int pdfsNotDownloaded)
- {
- // update bottom numbers
- var hasResults = 0 < (pdfsNotDownloaded + pdfsDownloaded);
- var statusStripText
- = !hasResults ? ""
- : pdfsNotDownloaded > 0 ? string.Format(pdfsCountsLbl_Format, pdfsNotDownloaded, pdfsDownloaded)
- : $"| All {pdfsDownloaded} PDFs downloaded";
+ // update menu item
+ var menuItemText
+ = pending > 0
+ ? $"{pending} remaining"
+ : "All books have been liberated";
- // update menu item
- var menuItemText
- = pdfsNotDownloaded > 0
- ? $"{pdfsNotDownloaded} remaining"
- : "All PDFs have been downloaded";
+ // update UI
+ statusStrip1.UIThread(() => backupsCountsLbl.Text = statusStripText);
+ menuStrip1.UIThread(() => beginBookBackupsToolStripMenuItem.Enabled = pending > 0);
+ menuStrip1.UIThread(() => beginBookBackupsToolStripMenuItem.Text = string.Format(beginBookBackupsToolStripMenuItem_format, menuItemText));
+ }
+ private void setPdfBackupCounts(int pdfsDownloaded, int pdfsNotDownloaded)
+ {
+ // update bottom numbers
+ var hasResults = 0 < (pdfsNotDownloaded + pdfsDownloaded);
+ var statusStripText
+ = !hasResults ? ""
+ : pdfsNotDownloaded > 0 ? string.Format(pdfsCountsLbl_Format, pdfsNotDownloaded, pdfsDownloaded)
+ : $"| All {pdfsDownloaded} PDFs downloaded";
- // update UI
- statusStrip1.UIThread(() => pdfsCountsLbl.Text = statusStripText);
- menuStrip1.UIThread(() => beginPdfBackupsToolStripMenuItem.Enabled = pdfsNotDownloaded > 0);
- menuStrip1.UIThread(() => beginPdfBackupsToolStripMenuItem.Text = string.Format(beginPdfBackupsToolStripMenuItem_format, menuItemText));
- }
- #endregion
+ // update menu item
+ var menuItemText
+ = pdfsNotDownloaded > 0
+ ? $"{pdfsNotDownloaded} remaining"
+ : "All PDFs have been downloaded";
- #region filter
- private void filterHelpBtn_Click(object sender, EventArgs e) => new SearchSyntaxDialog().ShowDialog();
+ // update UI
+ statusStrip1.UIThread(() => pdfsCountsLbl.Text = statusStripText);
+ menuStrip1.UIThread(() => beginPdfBackupsToolStripMenuItem.Enabled = pdfsNotDownloaded > 0);
+ menuStrip1.UIThread(() => beginPdfBackupsToolStripMenuItem.Text = string.Format(beginPdfBackupsToolStripMenuItem_format, menuItemText));
+ }
+ #endregion
- private void AddFilterBtn_Click(object sender, EventArgs e)
- {
- QuickFilters.Add(this.filterSearchTb.Text);
- UpdateFilterDropDown();
- }
+ #region filter
+ private void filterHelpBtn_Click(object sender, EventArgs e) => new SearchSyntaxDialog().ShowDialog();
- private void filterSearchTb_KeyPress(object sender, KeyPressEventArgs e)
- {
- if (e.KeyChar == (char)Keys.Return)
- {
- doFilter();
+ private void AddFilterBtn_Click(object sender, EventArgs e)
+ {
+ QuickFilters.Add(this.filterSearchTb.Text);
+ UpdateFilterDropDown();
+ }
- // silence the 'ding'
- e.Handled = true;
- }
- }
- private void filterBtn_Click(object sender, EventArgs e) => doFilter();
+ private void filterSearchTb_KeyPress(object sender, KeyPressEventArgs e)
+ {
+ if (e.KeyChar == (char)Keys.Return)
+ {
+ doFilter();
- string lastGoodFilter = "";
- private void doFilter(string filterString)
- {
- this.filterSearchTb.Text = filterString;
- doFilter();
- }
- private void doFilter()
- {
- if (isProcessingGridSelect || currProductsGrid == null)
- return;
+ // silence the 'ding'
+ e.Handled = true;
+ }
+ }
+ private void filterBtn_Click(object sender, EventArgs e) => doFilter();
- try
- {
- currProductsGrid.Filter(filterSearchTb.Text);
- lastGoodFilter = filterSearchTb.Text;
- }
- catch (Exception ex)
- {
- MessageBox.Show($"Bad filter string:\r\n\r\n{ex.Message}", "Bad filter string", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ private string lastGoodFilter = "";
+ private void doFilter(string filterString)
+ {
+ this.filterSearchTb.Text = filterString;
+ doFilter();
+ }
+ private void doFilter()
+ {
+ if (isProcessingGridSelect || currProductsGrid == null)
+ return;
- // re-apply last good filter
- doFilter(lastGoodFilter);
- }
- }
- #endregion
+ try
+ {
+ currProductsGrid.Filter(filterSearchTb.Text);
+ lastGoodFilter = filterSearchTb.Text;
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Bad filter string:\r\n\r\n{ex.Message}", "Bad filter string", MessageBoxButtons.OK, MessageBoxIcon.Error);
- #region Import menu
- public void RefreshImportMenu()
- {
- using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
- var count = persister.AccountsSettings.Accounts.Count;
+ // re-apply last good filter
+ doFilter(lastGoodFilter);
+ }
+ }
+ #endregion
- noAccountsYetAddAccountToolStripMenuItem.Visible = count == 0;
- scanLibraryToolStripMenuItem.Visible = count == 1;
- scanLibraryOfAllAccountsToolStripMenuItem.Visible = count > 1;
- scanLibraryOfSomeAccountsToolStripMenuItem.Visible = count > 1;
+ #region Import menu
+ public void RefreshImportMenu()
+ {
+ using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
+ var count = persister.AccountsSettings.Accounts.Count;
- removeLibraryBooksToolStripMenuItem.Visible = count != 0;
+ noAccountsYetAddAccountToolStripMenuItem.Visible = count == 0;
+ scanLibraryToolStripMenuItem.Visible = count == 1;
+ scanLibraryOfAllAccountsToolStripMenuItem.Visible = count > 1;
+ scanLibraryOfSomeAccountsToolStripMenuItem.Visible = count > 1;
- if (count == 1)
- {
- removeLibraryBooksToolStripMenuItem.Click += removeThisAccountToolStripMenuItem_Click;
- }
+ removeLibraryBooksToolStripMenuItem.Visible = count != 0;
- removeSomeAccountsToolStripMenuItem.Visible = count > 1;
- removeAllAccountsToolStripMenuItem.Visible = count > 1;
- }
+ if (count == 1)
+ {
+ removeLibraryBooksToolStripMenuItem.Click += removeThisAccountToolStripMenuItem_Click;
+ }
- private void noAccountsYetAddAccountToolStripMenuItem_Click(object sender, EventArgs e)
- {
- MessageBox.Show("To load your Audible library, come back here to the Import menu after adding your account");
- new AccountsDialog(this).ShowDialog();
- }
+ removeSomeAccountsToolStripMenuItem.Visible = count > 1;
+ removeAllAccountsToolStripMenuItem.Visible = count > 1;
+ }
- private void scanLibraryToolStripMenuItem_Click(object sender, EventArgs e)
- {
- using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
- var firstAccount = persister.AccountsSettings.GetAll().FirstOrDefault();
- scanLibraries(firstAccount);
- }
+ private void noAccountsYetAddAccountToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ MessageBox.Show("To load your Audible library, come back here to the Import menu after adding your account");
+ new AccountsDialog(this).ShowDialog();
+ }
- private void scanLibraryOfAllAccountsToolStripMenuItem_Click(object sender, EventArgs e)
- {
- using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
- var allAccounts = persister.AccountsSettings.GetAll();
- scanLibraries(allAccounts);
- }
+ private void scanLibraryToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
+ var firstAccount = persister.AccountsSettings.GetAll().FirstOrDefault();
+ scanLibraries(firstAccount);
+ }
- private void scanLibraryOfSomeAccountsToolStripMenuItem_Click(object sender, EventArgs e)
- {
- using var scanAccountsDialog = new ScanAccountsDialog(this);
+ private void scanLibraryOfAllAccountsToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
+ var allAccounts = persister.AccountsSettings.GetAll();
+ scanLibraries(allAccounts);
+ }
- if (scanAccountsDialog.ShowDialog() != DialogResult.OK)
- return;
+ private void scanLibraryOfSomeAccountsToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ using var scanAccountsDialog = new ScanAccountsDialog(this);
- if (!scanAccountsDialog.CheckedAccounts.Any())
- return;
+ if (scanAccountsDialog.ShowDialog() != DialogResult.OK)
+ return;
- scanLibraries(scanAccountsDialog.CheckedAccounts);
- }
+ if (!scanAccountsDialog.CheckedAccounts.Any())
+ return;
- private void removeThisAccountToolStripMenuItem_Click(object sender, EventArgs e)
- {
- using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
- var firstAccount = persister.AccountsSettings.GetAll().FirstOrDefault();
- scanLibrariesRemovedBooks(firstAccount);
- }
+ scanLibraries(scanAccountsDialog.CheckedAccounts);
+ }
- private void removeAllAccountsToolStripMenuItem_Click(object sender, EventArgs e)
- {
- using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
- var allAccounts = persister.AccountsSettings.GetAll();
- scanLibrariesRemovedBooks(allAccounts.ToArray());
- }
+ private void removeThisAccountToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
+ var firstAccount = persister.AccountsSettings.GetAll().FirstOrDefault();
+ scanLibrariesRemovedBooks(firstAccount);
+ }
- private void removeSomeAccountsToolStripMenuItem_Click(object sender, EventArgs e)
- {
- using var scanAccountsDialog = new ScanAccountsDialog(this);
+ private void removeAllAccountsToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
+ var allAccounts = persister.AccountsSettings.GetAll();
+ scanLibrariesRemovedBooks(allAccounts.ToArray());
+ }
- if (scanAccountsDialog.ShowDialog() != DialogResult.OK)
- return;
+ private void removeSomeAccountsToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ using var scanAccountsDialog = new ScanAccountsDialog(this);
- if (!scanAccountsDialog.CheckedAccounts.Any())
- return;
+ if (scanAccountsDialog.ShowDialog() != DialogResult.OK)
+ return;
- scanLibrariesRemovedBooks(scanAccountsDialog.CheckedAccounts.ToArray());
- }
+ if (!scanAccountsDialog.CheckedAccounts.Any())
+ return;
- private void scanLibrariesRemovedBooks(params Account[] accounts)
- {
- using var dialog = new RemoveBooksDialog(accounts);
- dialog.ShowDialog();
+ scanLibrariesRemovedBooks(scanAccountsDialog.CheckedAccounts.ToArray());
+ }
- if (dialog.BooksRemoved)
- reloadGrid();
- }
+ private void scanLibrariesRemovedBooks(params Account[] accounts)
+ {
+ using var dialog = new RemoveBooksDialog(accounts);
+ dialog.ShowDialog();
- private void scanLibraries(IEnumerable accounts) => scanLibraries(accounts.ToArray());
- private void scanLibraries(params Account[] accounts)
+ if (dialog.BooksRemoved)
+ reloadGrid();
+ }
+
+ private void scanLibraries(IEnumerable accounts) => scanLibraries(accounts.ToArray());
+ private void scanLibraries(params Account[] accounts)
{
using var dialog = new IndexLibraryDialog(accounts);
dialog.ShowDialog();
@@ -387,112 +387,112 @@ namespace LibationWinForms
if (totalProcessed > 0)
reloadGrid();
- }
- #endregion
+ }
+ #endregion
- #region liberate menu
- private async void beginBookBackupsToolStripMenuItem_Click(object sender, EventArgs e)
- => await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync(updateGridRow);
+ #region liberate menu
+ private async void beginBookBackupsToolStripMenuItem_Click(object sender, EventArgs e)
+ => await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync(updateGridRow);
- private async void beginPdfBackupsToolStripMenuItem_Click(object sender, EventArgs e)
- => await BookLiberation.ProcessorAutomationController.BackupAllPdfsAsync(updateGridRow);
+ private async void beginPdfBackupsToolStripMenuItem_Click(object sender, EventArgs e)
+ => await BookLiberation.ProcessorAutomationController.BackupAllPdfsAsync(updateGridRow);
- private async void convertAllM4bToMp3ToolStripMenuItem_Click(object sender, EventArgs e)
- => await BookLiberation.ProcessorAutomationController.ConvertAllBooksAsync();
+ private async void convertAllM4bToMp3ToolStripMenuItem_Click(object sender, EventArgs e)
+ => await BookLiberation.ProcessorAutomationController.ConvertAllBooksAsync();
- private void updateGridRow(object _, LibraryBook libraryBook) => currProductsGrid.RefreshRow(libraryBook.Book.AudibleProductId);
- #endregion
+ private void updateGridRow(object _, LibraryBook libraryBook) => currProductsGrid.RefreshRow(libraryBook.Book.AudibleProductId);
+ #endregion
- #region Export menu
- private void exportLibraryToolStripMenuItem_Click(object sender, EventArgs e)
- {
- try
- {
- var saveFileDialog = new SaveFileDialog
- {
- Title = "Where to export Library",
- Filter = "Excel Workbook (*.xlsx)|*.xlsx|CSV files (*.csv)|*.csv|JSON files (*.json)|*.json" // + "|All files (*.*)|*.*"
- };
+ #region Export menu
+ private void exportLibraryToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ var saveFileDialog = new SaveFileDialog
+ {
+ Title = "Where to export Library",
+ Filter = "Excel Workbook (*.xlsx)|*.xlsx|CSV files (*.csv)|*.csv|JSON files (*.json)|*.json" // + "|All files (*.*)|*.*"
+ };
- if (saveFileDialog.ShowDialog() != DialogResult.OK)
- return;
+ if (saveFileDialog.ShowDialog() != DialogResult.OK)
+ return;
- // FilterIndex is 1-based, NOT 0-based
- switch (saveFileDialog.FilterIndex)
- {
- case 1: // xlsx
- default:
- LibraryExporter.ToXlsx(saveFileDialog.FileName);
- break;
- case 2: // csv
- LibraryExporter.ToCsv(saveFileDialog.FileName);
- break;
- case 3: // json
- LibraryExporter.ToJson(saveFileDialog.FileName);
- break;
- }
+ // FilterIndex is 1-based, NOT 0-based
+ switch (saveFileDialog.FilterIndex)
+ {
+ case 1: // xlsx
+ default:
+ LibraryExporter.ToXlsx(saveFileDialog.FileName);
+ break;
+ case 2: // csv
+ LibraryExporter.ToCsv(saveFileDialog.FileName);
+ break;
+ case 3: // json
+ LibraryExporter.ToJson(saveFileDialog.FileName);
+ break;
+ }
- MessageBox.Show("Library exported to:\r\n" + saveFileDialog.FileName);
- }
- catch (Exception ex)
- {
- MessageBoxAlertAdmin.Show("Error attempting to export your library.", "Error exporting", ex);
- }
- }
- #endregion
+ MessageBox.Show("Library exported to:\r\n" + saveFileDialog.FileName);
+ }
+ catch (Exception ex)
+ {
+ MessageBoxAlertAdmin.Show("Error attempting to export your library.", "Error exporting", ex);
+ }
+ }
+ #endregion
- #region quick filters menu
- private void loadInitialQuickFilterState()
- {
- // set inital state. do once only
- firstFilterIsDefaultToolStripMenuItem.Checked = QuickFilters.UseDefault;
+ #region quick filters menu
+ private void loadInitialQuickFilterState()
+ {
+ // set inital state. do once only
+ firstFilterIsDefaultToolStripMenuItem.Checked = QuickFilters.UseDefault;
- // load default filter. do once only
- if (QuickFilters.UseDefault)
- doFilter(QuickFilters.Filters.FirstOrDefault());
+ // load default filter. do once only
+ if (QuickFilters.UseDefault)
+ doFilter(QuickFilters.Filters.FirstOrDefault());
- // do after every save
- UpdateFilterDropDown();
- }
+ // do after every save
+ UpdateFilterDropDown();
+ }
- private void FirstFilterIsDefaultToolStripMenuItem_Click(object sender, EventArgs e)
- {
- firstFilterIsDefaultToolStripMenuItem.Checked = !firstFilterIsDefaultToolStripMenuItem.Checked;
- QuickFilters.UseDefault = firstFilterIsDefaultToolStripMenuItem.Checked;
- }
+ private void FirstFilterIsDefaultToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ firstFilterIsDefaultToolStripMenuItem.Checked = !firstFilterIsDefaultToolStripMenuItem.Checked;
+ QuickFilters.UseDefault = firstFilterIsDefaultToolStripMenuItem.Checked;
+ }
- object quickFilterTag { get; } = new object();
- public void UpdateFilterDropDown()
- {
- // remove old
- for (var i = quickFiltersToolStripMenuItem.DropDownItems.Count - 1; i >= 0; i--)
- {
- var menuItem = quickFiltersToolStripMenuItem.DropDownItems[i];
- if (menuItem.Tag == quickFilterTag)
- quickFiltersToolStripMenuItem.DropDownItems.Remove(menuItem);
- }
+ private object quickFilterTag { get; } = new object();
+ public void UpdateFilterDropDown()
+ {
+ // remove old
+ for (var i = quickFiltersToolStripMenuItem.DropDownItems.Count - 1; i >= 0; i--)
+ {
+ var menuItem = quickFiltersToolStripMenuItem.DropDownItems[i];
+ if (menuItem.Tag == quickFilterTag)
+ quickFiltersToolStripMenuItem.DropDownItems.Remove(menuItem);
+ }
- // re-populate
- var index = 0;
- foreach (var filter in QuickFilters.Filters)
- {
- var menuItem = new ToolStripMenuItem
- {
- Tag = quickFilterTag,
- Text = $"&{++index}: {filter}"
- };
- menuItem.Click += (_, __) => doFilter(filter);
- quickFiltersToolStripMenuItem.DropDownItems.Add(menuItem);
- }
- }
+ // re-populate
+ var index = 0;
+ foreach (var filter in QuickFilters.Filters)
+ {
+ var menuItem = new ToolStripMenuItem
+ {
+ Tag = quickFilterTag,
+ Text = $"&{++index}: {filter}"
+ };
+ menuItem.Click += (_, __) => doFilter(filter);
+ quickFiltersToolStripMenuItem.DropDownItems.Add(menuItem);
+ }
+ }
- private void EditQuickFiltersToolStripMenuItem_Click(object sender, EventArgs e) => new EditQuickFilters(this).ShowDialog();
- #endregion
+ private void EditQuickFiltersToolStripMenuItem_Click(object sender, EventArgs e) => new EditQuickFilters(this).ShowDialog();
+ #endregion
- #region settings menu
- private void accountsToolStripMenuItem_Click(object sender, EventArgs e) => new AccountsDialog(this).ShowDialog();
+ #region settings menu
+ private void accountsToolStripMenuItem_Click(object sender, EventArgs e) => new AccountsDialog(this).ShowDialog();
- private void basicSettingsToolStripMenuItem_Click(object sender, EventArgs e) => new SettingsDialog().ShowDialog();
- #endregion
- }
+ private void basicSettingsToolStripMenuItem_Click(object sender, EventArgs e) => new SettingsDialog().ShowDialog();
+ #endregion
+ }
}
diff --git a/LibationWinForms/GridEntry.cs b/LibationWinForms/GridEntry.cs
index 64a802f7..ae04e870 100644
--- a/LibationWinForms/GridEntry.cs
+++ b/LibationWinForms/GridEntry.cs
@@ -1,12 +1,12 @@
-using System;
+using ApplicationServices;
+using DataLayer;
+using Dinah.Core.Drawing;
+using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
-using ApplicationServices;
-using DataLayer;
-using Dinah.Core.Drawing;
namespace LibationWinForms
{
@@ -106,7 +106,7 @@ namespace LibationWinForms
///
/// Create getters for all member object values by name
///
- Dictionary> CreateMemberValueDictionary() => new()
+ private Dictionary> CreateMemberValueDictionary() => new()
{
{ nameof(Title), () => GetSortName(Book.Title) },
{ nameof(Series), () => GetSortName(Book.SeriesNames) },
diff --git a/LibationWinForms/IObjectMemberComparable.cs b/LibationWinForms/IObjectMemberComparable.cs
index ade585ab..bfacb0cd 100644
--- a/LibationWinForms/IObjectMemberComparable.cs
+++ b/LibationWinForms/IObjectMemberComparable.cs
@@ -3,7 +3,7 @@ using System.Collections;
namespace LibationWinForms
{
- interface IObjectMemberComparable
+ internal interface IObjectMemberComparable
{
IComparer GetMemberComparer(Type memberType);
object GetMemberValue(string memberName);
diff --git a/LibationWinForms/MessageBoxAlertAdmin.cs b/LibationWinForms/MessageBoxAlertAdmin.cs
index 8e97f09c..5ba8cbde 100644
--- a/LibationWinForms/MessageBoxAlertAdmin.cs
+++ b/LibationWinForms/MessageBoxAlertAdmin.cs
@@ -1,9 +1,5 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using LibationWinForms.Dialogs;
+using LibationWinForms.Dialogs;
+using System;
namespace LibationWinForms
{
diff --git a/LibationWinForms/MessageBoxWarnIfVerboseLogging.cs b/LibationWinForms/MessageBoxWarnIfVerboseLogging.cs
index 7b33a22c..c4f04ee6 100644
--- a/LibationWinForms/MessageBoxWarnIfVerboseLogging.cs
+++ b/LibationWinForms/MessageBoxWarnIfVerboseLogging.cs
@@ -1,7 +1,6 @@
-using System;
-using System.Windows.Forms;
-using Dinah.Core.Logging;
+using Dinah.Core.Logging;
using Serilog;
+using System.Windows.Forms;
namespace LibationWinForms
{
diff --git a/LibationWinForms/ObjectComparer[T].cs b/LibationWinForms/ObjectComparer[T].cs
index 2bc69d8d..2ca0ddb9 100644
--- a/LibationWinForms/ObjectComparer[T].cs
+++ b/LibationWinForms/ObjectComparer[T].cs
@@ -3,7 +3,7 @@ using System.Collections;
namespace LibationWinForms
{
- class ObjectComparer : IComparer where T : IComparable
+ internal class ObjectComparer : IComparer where T : IComparable
{
public int Compare(object x, object y) => ((T)x).CompareTo((T)y);
}
diff --git a/LibationWinForms/ObjectMemberComparer[T].cs b/LibationWinForms/ObjectMemberComparer[T].cs
index aea41e21..3024477e 100644
--- a/LibationWinForms/ObjectMemberComparer[T].cs
+++ b/LibationWinForms/ObjectMemberComparer[T].cs
@@ -3,19 +3,19 @@ using System.ComponentModel;
namespace LibationWinForms
{
- class ObjectMemberComparer : IComparer where T : IObjectMemberComparable
- {
- public ListSortDirection Direction { get; set; } = ListSortDirection.Ascending;
- public string PropertyName { get; set; }
+ internal class ObjectMemberComparer : IComparer where T : IObjectMemberComparable
+ {
+ public ListSortDirection Direction { get; set; } = ListSortDirection.Ascending;
+ public string PropertyName { get; set; }
- public int Compare(T x, T y)
- {
- var val1 = x.GetMemberValue(PropertyName);
- var val2 = y.GetMemberValue(PropertyName);
+ public int Compare(T x, T y)
+ {
+ var val1 = x.GetMemberValue(PropertyName);
+ var val2 = y.GetMemberValue(PropertyName);
- return DirMult * x.GetMemberComparer(val1.GetType()).Compare(val1, val2);
- }
+ return DirMult * x.GetMemberComparer(val1.GetType()).Compare(val1, val2);
+ }
- private int DirMult => Direction == ListSortDirection.Descending ? -1 : 1;
- }
+ private int DirMult => Direction == ListSortDirection.Descending ? -1 : 1;
+ }
}
diff --git a/LibationWinForms/ProductsGrid.cs b/LibationWinForms/ProductsGrid.cs
index 0f826435..4afe1f64 100644
--- a/LibationWinForms/ProductsGrid.cs
+++ b/LibationWinForms/ProductsGrid.cs
@@ -1,200 +1,200 @@
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-using ApplicationServices;
+using ApplicationServices;
using DataLayer;
using Dinah.Core;
using Dinah.Core.Windows.Forms;
using LibationWinForms.Dialogs;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
namespace LibationWinForms
{
- // INSTRUCTIONS TO UPDATE DATA_GRID_VIEW
- // - delete current DataGridView
- // - view > other windows > data sources
- // - refresh
- // OR
- // - Add New Data Source
- // Object. Next
- // LibationWinForms
- // AudibleDTO
- // GridEntry
- // - go to Design view
- // - click on Data Sources > ProductItem. drowdown: DataGridView
- // - drag/drop ProductItem on design surface
- // AS OF AUGUST 2021 THIS DOES NOT WORK IN VS2019 WITH .NET-5 PROJECTS
+ // INSTRUCTIONS TO UPDATE DATA_GRID_VIEW
+ // - delete current DataGridView
+ // - view > other windows > data sources
+ // - refresh
+ // OR
+ // - Add New Data Source
+ // Object. Next
+ // LibationWinForms
+ // AudibleDTO
+ // GridEntry
+ // - go to Design view
+ // - click on Data Sources > ProductItem. drowdown: DataGridView
+ // - drag/drop ProductItem on design surface
+ // AS OF AUGUST 2021 THIS DOES NOT WORK IN VS2019 WITH .NET-5 PROJECTS
- public partial class ProductsGrid : UserControl
- {
- public event EventHandler VisibleCountChanged;
- public event EventHandler BackupCountsChanged;
+ public partial class ProductsGrid : UserControl
+ {
+ public event EventHandler VisibleCountChanged;
+ public event EventHandler BackupCountsChanged;
- // alias
- private DataGridView _dataGridView => gridEntryDataGridView;
+ // alias
+ private DataGridView _dataGridView => gridEntryDataGridView;
public ProductsGrid()
{
InitializeComponent();
- // sorting breaks filters. must reapply filters after sorting
- _dataGridView.Sorted += (_, __) => Filter();
- _dataGridView.CellContentClick += DataGridView_CellContentClick;
+ // sorting breaks filters. must reapply filters after sorting
+ _dataGridView.Sorted += (_, __) => Filter();
+ _dataGridView.CellContentClick += DataGridView_CellContentClick;
- EnableDoubleBuffering();
- }
- private void EnableDoubleBuffering()
- {
- var propertyInfo = _dataGridView.GetType().GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
+ EnableDoubleBuffering();
+ }
+ private void EnableDoubleBuffering()
+ {
+ var propertyInfo = _dataGridView.GetType().GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
- propertyInfo.SetValue(_dataGridView, true, null);
- }
+ propertyInfo.SetValue(_dataGridView, true, null);
+ }
- #region Button controls
+ #region Button controls
- private async void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
- {
- // handle grid button click: https://stackoverflow.com/a/13687844
- if (e.RowIndex < 0 || _dataGridView.Columns[e.ColumnIndex] is not DataGridViewButtonColumn)
- return;
+ private async void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
+ {
+ // handle grid button click: https://stackoverflow.com/a/13687844
+ if (e.RowIndex < 0 || _dataGridView.Columns[e.ColumnIndex] is not DataGridViewButtonColumn)
+ return;
- var liveGridEntry = getGridEntry(e.RowIndex);
+ var liveGridEntry = getGridEntry(e.RowIndex);
- switch (_dataGridView.Columns[e.ColumnIndex].DataPropertyName)
- {
- case nameof(liveGridEntry.Liberate):
- await Liberate_Click(liveGridEntry);
- break;
- case nameof(liveGridEntry.DisplayTags):
- EditTags_Click(liveGridEntry);
- break;
- }
- }
+ switch (_dataGridView.Columns[e.ColumnIndex].DataPropertyName)
+ {
+ case nameof(liveGridEntry.Liberate):
+ await Liberate_Click(liveGridEntry);
+ break;
+ case nameof(liveGridEntry.DisplayTags):
+ EditTags_Click(liveGridEntry);
+ break;
+ }
+ }
- private async Task Liberate_Click(GridEntry liveGridEntry)
- {
- var libraryBook = liveGridEntry.LibraryBook;
+ private async Task Liberate_Click(GridEntry liveGridEntry)
+ {
+ var libraryBook = liveGridEntry.LibraryBook;
- // liberated: open explorer to file
- if (TransitionalFileLocator.Audio_Exists(libraryBook.Book))
- {
- var filePath = TransitionalFileLocator.Audio_GetPath(libraryBook.Book);
- if (!Go.To.File(filePath))
- MessageBox.Show($"File not found:\r\n{filePath}");
- return;
- }
+ // liberated: open explorer to file
+ if (TransitionalFileLocator.Audio_Exists(libraryBook.Book))
+ {
+ var filePath = TransitionalFileLocator.Audio_GetPath(libraryBook.Book);
+ if (!Go.To.File(filePath))
+ MessageBox.Show($"File not found:\r\n{filePath}");
+ return;
+ }
- // else: liberate
- await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook, (_, __) => RefreshRow(libraryBook.Book.AudibleProductId));
- }
+ // else: liberate
+ await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook, (_, __) => RefreshRow(libraryBook.Book.AudibleProductId));
+ }
- private void EditTags_Click(GridEntry liveGridEntry)
- {
- var bookDetailsForm = new BookDetailsDialog(liveGridEntry.Title, liveGridEntry.LibraryBook.Book.UserDefinedItem.Tags);
- if (bookDetailsForm.ShowDialog() != DialogResult.OK)
- return;
+ private void EditTags_Click(GridEntry liveGridEntry)
+ {
+ var bookDetailsForm = new BookDetailsDialog(liveGridEntry.Title, liveGridEntry.LibraryBook.Book.UserDefinedItem.Tags);
+ if (bookDetailsForm.ShowDialog() != DialogResult.OK)
+ return;
- var qtyChanges = LibraryCommands.UpdateTags(liveGridEntry.LibraryBook.Book, bookDetailsForm.NewTags);
- if (qtyChanges == 0)
- return;
+ var qtyChanges = LibraryCommands.UpdateTags(liveGridEntry.LibraryBook.Book, bookDetailsForm.NewTags);
+ if (qtyChanges == 0)
+ return;
- //Re-apply filters
- Filter();
- }
+ //Re-apply filters
+ Filter();
+ }
- #endregion
+ #endregion
- #region UI display functions
+ #region UI display functions
private bool hasBeenDisplayed = false;
- public void Display()
- {
- if (hasBeenDisplayed)
- return;
- hasBeenDisplayed = true;
+ public void Display()
+ {
+ if (hasBeenDisplayed)
+ return;
+ hasBeenDisplayed = true;
- //
- // transform into sorted GridEntry.s BEFORE binding
- //
- using var context = DbContexts.GetContext();
- var lib = context.GetLibrary_Flat_NoTracking();
+ //
+ // transform into sorted GridEntry.s BEFORE binding
+ //
+ using var context = DbContexts.GetContext();
+ var lib = context.GetLibrary_Flat_NoTracking();
- // if no data. hide all columns. return
- if (!lib.Any())
- {
- for (var i = _dataGridView.ColumnCount - 1; i >= 0; i--)
- _dataGridView.Columns.RemoveAt(i);
- return;
- }
+ // if no data. hide all columns. return
+ if (!lib.Any())
+ {
+ for (var i = _dataGridView.ColumnCount - 1; i >= 0; i--)
+ _dataGridView.Columns.RemoveAt(i);
+ return;
+ }
- var orderedGridEntries = lib
- .Select(lb => new GridEntry(lb)).ToList()
- // default load order
- .OrderByDescending(ge => (DateTime)ge.GetMemberValue(nameof(ge.PurchaseDate)))
- //// more advanced example: sort by author, then series, then title
- //.OrderBy(ge => ge.Authors)
- // .ThenBy(ge => ge.Series)
- // .ThenBy(ge => ge.Title)
- .ToList();
+ var orderedGridEntries = lib
+ .Select(lb => new GridEntry(lb)).ToList()
+ // default load order
+ .OrderByDescending(ge => (DateTime)ge.GetMemberValue(nameof(ge.PurchaseDate)))
+ //// more advanced example: sort by author, then series, then title
+ //.OrderBy(ge => ge.Authors)
+ // .ThenBy(ge => ge.Series)
+ // .ThenBy(ge => ge.Title)
+ .ToList();
- // BIND
- gridEntryBindingSource.DataSource = new SortableBindingList2(orderedGridEntries);
+ // BIND
+ gridEntryBindingSource.DataSource = new SortableBindingList2(orderedGridEntries);
- // FILTER
- Filter();
+ // FILTER
+ Filter();
- BackupCountsChanged?.Invoke(this, EventArgs.Empty);
- }
+ BackupCountsChanged?.Invoke(this, EventArgs.Empty);
+ }
- public void RefreshRow(string productId)
- {
- var rowIndex = getRowIndex((ge) => ge.AudibleProductId == productId);
+ public void RefreshRow(string productId)
+ {
+ var rowIndex = getRowIndex((ge) => ge.AudibleProductId == productId);
- // update cells incl Liberate button text
- _dataGridView.InvalidateRow(rowIndex);
+ // update cells incl Liberate button text
+ _dataGridView.InvalidateRow(rowIndex);
- // needed in case filtering by -IsLiberated and it gets changed to Liberated. want to immediately show the change
- Filter();
+ // needed in case filtering by -IsLiberated and it gets changed to Liberated. want to immediately show the change
+ Filter();
- BackupCountsChanged?.Invoke(this, EventArgs.Empty);
- }
+ BackupCountsChanged?.Invoke(this, EventArgs.Empty);
+ }
- #endregion
+ #endregion
- #region Filter
+ #region Filter
- string _filterSearchString;
- private void Filter() => Filter(_filterSearchString);
- public void Filter(string searchString)
- {
- _filterSearchString = searchString;
+ private string _filterSearchString;
+ private void Filter() => Filter(_filterSearchString);
+ public void Filter(string searchString)
+ {
+ _filterSearchString = searchString;
if (_dataGridView.Rows.Count == 0)
return;
- var searchResults = SearchEngineCommands.Search(searchString);
- var productIds = searchResults.Docs.Select(d => d.ProductId).ToList();
+ var searchResults = SearchEngineCommands.Search(searchString);
+ var productIds = searchResults.Docs.Select(d => d.ProductId).ToList();
- // https://stackoverflow.com/a/18942430
- var bindingContext = BindingContext[_dataGridView.DataSource];
- bindingContext.SuspendBinding();
- {
- for (var r = _dataGridView.RowCount - 1; r >= 0; r--)
- _dataGridView.Rows[r].Visible = productIds.Contains(getGridEntry(r).AudibleProductId);
- }
+ // https://stackoverflow.com/a/18942430
+ var bindingContext = BindingContext[_dataGridView.DataSource];
+ bindingContext.SuspendBinding();
+ {
+ for (var r = _dataGridView.RowCount - 1; r >= 0; r--)
+ _dataGridView.Rows[r].Visible = productIds.Contains(getGridEntry(r).AudibleProductId);
+ }
- //Causes repainting of the DataGridView
- bindingContext.ResumeBinding();
+ //Causes repainting of the DataGridView
+ bindingContext.ResumeBinding();
VisibleCountChanged?.Invoke(this, _dataGridView.AsEnumerable().Count(r => r.Visible));
- }
+ }
- #endregion
+ #endregion
- #region DataGridView Macro
+ #region DataGridView Macro
- private int getRowIndex(Func func) => _dataGridView.GetRowIdOfBoundItem(func);
- private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem(rowIndex);
+ private int getRowIndex(Func func) => _dataGridView.GetRowIdOfBoundItem(func);
+ private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem(rowIndex);
- #endregion
- }
+ #endregion
+ }
}
diff --git a/LibationWinForms/SortableBindingList2[T].cs b/LibationWinForms/SortableBindingList2[T].cs
index 838dda29..377cdf86 100644
--- a/LibationWinForms/SortableBindingList2[T].cs
+++ b/LibationWinForms/SortableBindingList2[T].cs
@@ -1,75 +1,74 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace LibationWinForms
{
- class SortableBindingList2 : BindingList where T : IObjectMemberComparable
- {
- private bool isSorted;
- private ListSortDirection listSortDirection;
- private PropertyDescriptor propertyDescriptor;
+ internal class SortableBindingList2 : BindingList where T : IObjectMemberComparable
+ {
+ private bool isSorted;
+ private ListSortDirection listSortDirection;
+ private PropertyDescriptor propertyDescriptor;
- public SortableBindingList2() : base(new List()) { }
- public SortableBindingList2(IEnumerable enumeration) : base(new List(enumeration)) { }
+ public SortableBindingList2() : base(new List()) { }
+ public SortableBindingList2(IEnumerable enumeration) : base(new List(enumeration)) { }
- private ObjectMemberComparer Comparer { get; } = new();
- protected override bool SupportsSortingCore => true;
- protected override bool SupportsSearchingCore => true;
- protected override bool IsSortedCore => isSorted;
- protected override PropertyDescriptor SortPropertyCore => propertyDescriptor;
- protected override ListSortDirection SortDirectionCore => listSortDirection;
+ private ObjectMemberComparer Comparer { get; } = new();
+ protected override bool SupportsSortingCore => true;
+ protected override bool SupportsSearchingCore => true;
+ protected override bool IsSortedCore => isSorted;
+ protected override PropertyDescriptor SortPropertyCore => propertyDescriptor;
+ protected override ListSortDirection SortDirectionCore => listSortDirection;
- protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
- {
- List itemsList = (List)Items;
+ protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
+ {
+ List itemsList = (List)Items;
- Comparer.PropertyName = property.Name;
- Comparer.Direction = direction;
+ Comparer.PropertyName = property.Name;
+ Comparer.Direction = direction;
- //Array.Sort() and List.Sort() are unstable sorts. OrderBy is stable.
- var sortedItems = itemsList.OrderBy((ge) => ge, Comparer).ToList();
+ //Array.Sort() and List.Sort() are unstable sorts. OrderBy is stable.
+ var sortedItems = itemsList.OrderBy((ge) => ge, Comparer).ToList();
- itemsList.Clear();
- itemsList.AddRange(sortedItems);
+ itemsList.Clear();
+ itemsList.AddRange(sortedItems);
- propertyDescriptor = property;
- listSortDirection = direction;
- isSorted = true;
+ propertyDescriptor = property;
+ listSortDirection = direction;
+ isSorted = true;
- OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
- }
+ OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
+ }
- protected override void RemoveSortCore()
- {
- isSorted = false;
- propertyDescriptor = base.SortPropertyCore;
- listSortDirection = base.SortDirectionCore;
+ protected override void RemoveSortCore()
+ {
+ isSorted = false;
+ propertyDescriptor = base.SortPropertyCore;
+ listSortDirection = base.SortDirectionCore;
- OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
- }
- //NOTE: Libation does not currently use BindingSource.Find anywhere,
- //so this override may be removed (along with SupportsSearchingCore)
- protected override int FindCore(PropertyDescriptor property, object key)
- {
- int count = Count;
+ OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
+ }
+ //NOTE: Libation does not currently use BindingSource.Find anywhere,
+ //so this override may be removed (along with SupportsSearchingCore)
+ protected override int FindCore(PropertyDescriptor property, object key)
+ {
+ int count = Count;
- System.Collections.IComparer valueComparer = null;
+ System.Collections.IComparer valueComparer = null;
- for (int i = 0; i < count; ++i)
- {
- T element = this[i];
- var elemValue = element.GetMemberValue(property.Name);
- valueComparer ??= element.GetMemberComparer(elemValue.GetType());
+ for (int i = 0; i < count; ++i)
+ {
+ T element = this[i];
+ var elemValue = element.GetMemberValue(property.Name);
+ valueComparer ??= element.GetMemberComparer(elemValue.GetType());
- if (valueComparer.Compare(elemValue, key) == 0)
- {
- return i;
- }
- }
+ if (valueComparer.Compare(elemValue, key) == 0)
+ {
+ return i;
+ }
+ }
- return -1;
- }
- }
+ return -1;
+ }
+ }
}