From 1369ee575a657f50689d7fae89c1d2b5c3bbf846 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sat, 21 Aug 2021 16:27:33 -0600 Subject: [PATCH 01/13] Replaced LiberatedState with LiberatedStatus and PdfState with LiberatedStatus? --- ApplicationServices/LibraryCommands.cs | 33 ++++++++----------- DataLayer/EfClasses/UserDefinedItem.cs | 6 +++- LibationWinForms/GridEntry.cs | 7 ++-- .../LiberateDataGridViewImageButtonColumn.cs | 27 ++++++++------- 4 files changed, 36 insertions(+), 37 deletions(-) diff --git a/ApplicationServices/LibraryCommands.cs b/ApplicationServices/LibraryCommands.cs index 96012f68..c9999b2d 100644 --- a/ApplicationServices/LibraryCommands.cs +++ b/ApplicationServices/LibraryCommands.cs @@ -11,13 +11,6 @@ using Serilog; namespace ApplicationServices { - // subtly different from DataLayer.LiberatedStatus - // - DataLayer.LiberatedStatus: has no concept of partially downloaded - // - ApplicationServices.LiberatedState: has no concept of Error/skipped - public enum LiberatedState { NotDownloaded, PartialDownload, Liberated } - - public enum PdfState { NoPdf, Downloaded, NotDownloaded } - public static class LibraryCommands { private static LibraryOptions.ResponseGroupOptions LibraryResponseGroups = LibraryOptions.ResponseGroupOptions.ALL_OPTIONS; @@ -257,15 +250,15 @@ namespace ApplicationServices // below are queries, not commands. maybe I should make a LibraryQueries. except there's already one of those... - public static LiberatedState Liberated_Status(Book book) - => book.Audio_Exists ? LiberatedState.Liberated - : FileManager.AudibleFileStorage.AaxcExists(book.AudibleProductId) ? LiberatedState.PartialDownload - : LiberatedState.NotDownloaded; + public static LiberatedStatus Liberated_Status(Book book) + => book.Audio_Exists ? LiberatedStatus.Liberated + : FileManager.AudibleFileStorage.AaxcExists(book.AudibleProductId) ? LiberatedStatus.PartialDownload + : LiberatedStatus.NotLiberated; - public static PdfState Pdf_Status(Book book) - => !book.Supplements.Any() ? PdfState.NoPdf - : book.PDF_Exists ? PdfState.Downloaded - : PdfState.NotDownloaded; + public static LiberatedStatus? Pdf_Status(Book book) + => !book.Supplements.Any() ? null + : book.PDF_Exists ? LiberatedStatus.Liberated + : LiberatedStatus.NotLiberated; public record LibraryStats(int booksFullyBackedUp, int booksDownloadedOnly, int booksNoProgress, int pdfsDownloaded, int pdfsNotDownloaded) { } public static LibraryStats GetCounts() @@ -276,9 +269,9 @@ namespace ApplicationServices .AsParallel() .Select(lb => Liberated_Status(lb.Book)) .ToList(); - var booksFullyBackedUp = results.Count(r => r == LiberatedState.Liberated); - var booksDownloadedOnly = results.Count(r => r == LiberatedState.PartialDownload); - var booksNoProgress = results.Count(r => r == LiberatedState.NotDownloaded); + var booksFullyBackedUp = results.Count(r => r == LiberatedStatus.Liberated); + var booksDownloadedOnly = results.Count(r => r == LiberatedStatus.PartialDownload); + var booksNoProgress = results.Count(r => r == LiberatedStatus.NotLiberated); Log.Logger.Information("Book counts. {@DebugInfo}", new { total = results.Count, booksFullyBackedUp, booksDownloadedOnly, booksNoProgress }); @@ -287,8 +280,8 @@ namespace ApplicationServices .Where(lb => lb.Book.Supplements.Any()) .Select(lb => Pdf_Status(lb.Book)) .ToList(); - var pdfsDownloaded = boolResults.Count(r => r == PdfState.Downloaded); - var pdfsNotDownloaded = boolResults.Count(r => r == PdfState.NotDownloaded); + var pdfsDownloaded = boolResults.Count(r => r == LiberatedStatus.Liberated); + var pdfsNotDownloaded = boolResults.Count(r => r == LiberatedStatus.NotLiberated); Log.Logger.Information("PDF counts. {@DebugInfo}", new { total = boolResults.Count, pdfsDownloaded, pdfsNotDownloaded }); diff --git a/DataLayer/EfClasses/UserDefinedItem.cs b/DataLayer/EfClasses/UserDefinedItem.cs index 4178a20e..4643d02e 100644 --- a/DataLayer/EfClasses/UserDefinedItem.cs +++ b/DataLayer/EfClasses/UserDefinedItem.cs @@ -14,7 +14,11 @@ namespace DataLayer NotLiberated = 0, Liberated = 1, /// Error occurred during liberation. Don't retry - Error = 2 + Error = 2, + + /// Application-state only. Not a valid persistence state. + PartialDownload = 0x1000 + } public class UserDefinedItem diff --git a/LibationWinForms/GridEntry.cs b/LibationWinForms/GridEntry.cs index 7a90c437..d6f414bf 100644 --- a/LibationWinForms/GridEntry.cs +++ b/LibationWinForms/GridEntry.cs @@ -71,7 +71,6 @@ namespace LibationWinForms } #region Data Source properties - public Image Cover { get @@ -97,7 +96,7 @@ namespace LibationWinForms public string Misc { get; } public string Description { get; } public string DisplayTags => string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated); - public (LiberatedState, PdfState) Liberate => (LibraryCommands.Liberated_Status(Book), LibraryCommands.Pdf_Status(Book)); + public (LiberatedStatus BookStatus, LiberatedStatus? PdfStatus) Liberate => (LibraryCommands.Liberated_Status(Book), LibraryCommands.Pdf_Status(Book)); #endregion #region Data Sorting @@ -121,7 +120,7 @@ namespace LibationWinForms { nameof(Category), () => Category }, { nameof(Misc), () => Misc }, { nameof(DisplayTags), () => DisplayTags }, - { nameof(Liberate), () => Liberate.Item1 } + { nameof(Liberate), () => Liberate.BookStatus } }; // Instantiate comparers for every exposed member object type. @@ -131,7 +130,7 @@ namespace LibationWinForms { typeof(int), new ObjectComparer() }, { typeof(float), new ObjectComparer() }, { typeof(DateTime), new ObjectComparer() }, - { typeof(LiberatedState), new ObjectComparer() }, + { typeof(LiberatedStatus), new ObjectComparer() }, }; public virtual object GetMemberValue(string memberName) => _memberValues[memberName](); diff --git a/LibationWinForms/LiberateDataGridViewImageButtonColumn.cs b/LibationWinForms/LiberateDataGridViewImageButtonColumn.cs index b8509e5d..6b85a4df 100644 --- a/LibationWinForms/LiberateDataGridViewImageButtonColumn.cs +++ b/LibationWinForms/LiberateDataGridViewImageButtonColumn.cs @@ -3,6 +3,7 @@ using System; using System.Drawing; using System.Windows.Forms; using System.Linq; +using DataLayer; namespace LibationWinForms { @@ -20,9 +21,11 @@ namespace LibationWinForms { base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts); - if (value is (LiberatedState liberatedState, PdfState pdfState)) + if (value is (LiberatedStatus, LiberatedStatus) or (LiberatedStatus, null)) { - (string mouseoverText, Bitmap buttonImage) = GetLiberateDisplay(liberatedState, pdfState); + var (bookState, pdfState) = ((LiberatedStatus bookState, LiberatedStatus? pdfState))value; + + (string mouseoverText, Bitmap buttonImage) = GetLiberateDisplay(bookState, pdfState); DrawButtonImage(graphics, buttonImage, cellBounds); @@ -30,29 +33,29 @@ namespace LibationWinForms } } - private static (string mouseoverText, Bitmap buttonImage) GetLiberateDisplay(LiberatedState liberatedStatus, PdfState pdfStatus) + private static (string mouseoverText, Bitmap buttonImage) GetLiberateDisplay(LiberatedStatus liberatedStatus, LiberatedStatus? pdfStatus) { (string libState, string image_lib) = liberatedStatus switch { - LiberatedState.Liberated => ("Liberated", "green"), - LiberatedState.PartialDownload => ("File has been at least\r\npartially downloaded", "yellow"), - LiberatedState.NotDownloaded => ("Book NOT downloaded", "red"), + LiberatedStatus.Liberated => ("Liberated", "green"), + LiberatedStatus.PartialDownload => ("File has been at least\r\npartially downloaded", "yellow"), + LiberatedStatus.NotLiberated => ("Book NOT downloaded", "red"), _ => throw new Exception("Unexpected liberation state") }; (string pdfState, string image_pdf) = pdfStatus switch { - PdfState.Downloaded => ("\r\nPDF downloaded", "_pdf_yes"), - PdfState.NotDownloaded => ("\r\nPDF NOT downloaded", "_pdf_no"), - PdfState.NoPdf => ("", ""), + LiberatedStatus.Liberated => ("\r\nPDF downloaded", "_pdf_yes"), + LiberatedStatus.NotLiberated => ("\r\nPDF NOT downloaded", "_pdf_no"), + null => ("", ""), _ => throw new Exception("Unexpected PDF state") }; var mouseoverText = libState + pdfState; - if (liberatedStatus == LiberatedState.NotDownloaded || - liberatedStatus == LiberatedState.PartialDownload || - pdfStatus == PdfState.NotDownloaded) + if (liberatedStatus == LiberatedStatus.NotLiberated || + liberatedStatus == LiberatedStatus.PartialDownload || + pdfStatus == LiberatedStatus.NotLiberated) mouseoverText += "\r\nClick to complete"; var buttonImage = (Bitmap)Properties.Resources.ResourceManager.GetObject($"liberate_{image_lib}{image_pdf}"); From ede8397f13164eb3d21c388247b08821eded064b Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sat, 21 Aug 2021 18:15:39 -0600 Subject: [PATCH 02/13] Needed to add check for actual file since Audio_Exists is now an application state. --- FileLiberator/DownloadDecryptBook.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/FileLiberator/DownloadDecryptBook.cs b/FileLiberator/DownloadDecryptBook.cs index e0d960ef..4acf9681 100644 --- a/FileLiberator/DownloadDecryptBook.cs +++ b/FileLiberator/DownloadDecryptBook.cs @@ -47,13 +47,15 @@ namespace FileLiberator return new StatusHandler { "Decrypt failed" }; // moves files and returns dest dir - _ = moveFilesToBooksDir(libraryBook.Book, outputAudioFilename); + var moveResults = MoveFilesToBooksDir(libraryBook.Book, outputAudioFilename); - if (!libraryBook.Book.Audio_Exists) + if (!moveResults.movedAudioFile) return new StatusHandler { "Cannot find final audio file after decryption" }; - // only need to update if success. if failure, it will remain at 0 == NotLiberated - ApplicationServices.LibraryCommands.UpdateBook(libraryBook, LiberatedStatus.Liberated); + // need to update before Audio_Exists will return true + ApplicationServices.LibraryCommands.UpdateBook(libraryBook.Book, LiberatedStatus.Liberated); + + return new StatusHandler(); } @@ -146,7 +148,7 @@ namespace FileLiberator NarratorsDiscovered?.Invoke(this, e.Narrator ?? "[unknown]"); } - private static string moveFilesToBooksDir(Book product, string outputAudioFilename) + private static (string destinationDir, bool movedAudioFile) MoveFilesToBooksDir(Book product, string outputAudioFilename) { // create final directory. move each file into it. MOVE AUDIO FILE LAST // new dir: safetitle_limit50char + " [" + productId + "]" @@ -161,6 +163,7 @@ namespace FileLiberator // audio filename: safetitle_limit50char + " [" + productId + "]." + audio_ext var audioFileName = FileUtility.GetValidFilename(destinationDir, product.Title, musicFileExt, product.AudibleProductId); + bool movedAudioFile = false; foreach (var f in sortedFiles) { var dest @@ -173,11 +176,14 @@ namespace FileLiberator Cue.UpdateFileName(f, audioFileName); File.Move(f.FullName, dest); + + movedAudioFile |= AudibleFileStorage.Audio.IsFileTypeMatch(f); + } AudibleFileStorage.Audio.Refresh(); - return destinationDir; + return (destinationDir, movedAudioFile); } private static List getProductFilesSorted(Book product, string outputAudioFilename) From f25734334defe6cfa4896e59e9965a577cec05ab Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sat, 21 Aug 2021 18:16:24 -0600 Subject: [PATCH 03/13] Add separate command for updating tags --- ApplicationServices/LibraryCommands.cs | 42 +++++++++----------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/ApplicationServices/LibraryCommands.cs b/ApplicationServices/LibraryCommands.cs index c9999b2d..6643a50a 100644 --- a/ApplicationServices/LibraryCommands.cs +++ b/ApplicationServices/LibraryCommands.cs @@ -154,37 +154,25 @@ namespace ApplicationServices } #endregion - #region Update book details - public static int UpdateUserDefinedItem(Book book, string newTags, LiberatedStatus bookStatus, LiberatedStatus? pdfStatus) + #region Update book details + + public static int UpdateTags(Book libraryBook, string newTags) { try { using var context = DbContexts.GetContext(); - var udi = book.UserDefinedItem; + var udi = libraryBook.UserDefinedItem; - var tagsChanged = udi.Tags != newTags; - var bookStatusChanged = udi.BookStatus != bookStatus; - var pdfStatusChanged = udi.PdfStatus != pdfStatus; - - if (!tagsChanged && !bookStatusChanged && !pdfStatusChanged) + if (udi.Tags == newTags) return 0; - udi.Tags = newTags; - udi.BookStatus = bookStatus; - udi.PdfStatus = pdfStatus; - // Attach() NoTracking entities before SaveChanges() + udi.Tags = newTags; context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; var qtyChanges = context.SaveChanges(); - - if (qtyChanges == 0) - return 0; - - if (tagsChanged) - SearchEngineCommands.UpdateBookTags(book); - if (bookStatusChanged || pdfStatusChanged) - SearchEngineCommands.UpdateLiberatedStatus(book); + if (qtyChanges > 0) + SearchEngineCommands.UpdateBookTags(libraryBook); return qtyChanges; } @@ -195,13 +183,13 @@ namespace ApplicationServices } } - public static int UpdateBook(LibraryBook libraryBook, LiberatedStatus liberatedStatus) + public static int UpdateBook(Book libraryBook, LiberatedStatus liberatedStatus) { try { using var context = DbContexts.GetContext(); - var udi = libraryBook.Book.UserDefinedItem; + var udi = libraryBook.UserDefinedItem; if (udi.BookStatus == liberatedStatus) return 0; @@ -211,24 +199,24 @@ namespace ApplicationServices context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; var qtyChanges = context.SaveChanges(); if (qtyChanges > 0) - SearchEngineCommands.UpdateLiberatedStatus(libraryBook.Book); + SearchEngineCommands.UpdateLiberatedStatus(libraryBook); return qtyChanges; } catch (Exception ex) { - Log.Logger.Error(ex, "Error updating tags"); + Log.Logger.Error(ex, "Error updating audiobook status"); throw; } } - public static int UpdatePdf(LibraryBook libraryBook, LiberatedStatus liberatedStatus) + public static int UpdatePdf(Book libraryBook, LiberatedStatus? liberatedStatus) { try { using var context = DbContexts.GetContext(); - var udi = libraryBook.Book.UserDefinedItem; + var udi = libraryBook.UserDefinedItem; if (udi.PdfStatus == liberatedStatus) return 0; @@ -242,7 +230,7 @@ namespace ApplicationServices } catch (Exception ex) { - Log.Logger.Error(ex, "Error updating tags"); + Log.Logger.Error(ex, "Error updating pdf status"); throw; } } From 263222d8ccc4abd0059e3aa0dd1c66175e12ee33 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sat, 21 Aug 2021 18:17:07 -0600 Subject: [PATCH 04/13] Changed method signature. --- FileLiberator/DownloadPdf.cs | 2 +- .../BookLiberation/ProcessorAutomationController.cs | 2 +- LibationWinForms/Form1.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/FileLiberator/DownloadPdf.cs b/FileLiberator/DownloadPdf.cs index 356cb13a..0919c841 100644 --- a/FileLiberator/DownloadPdf.cs +++ b/FileLiberator/DownloadPdf.cs @@ -24,7 +24,7 @@ namespace FileLiberator var result = verifyDownload(libraryBook); var liberatedStatus = result.IsSuccess ? LiberatedStatus.Liberated : LiberatedStatus.NotLiberated; - ApplicationServices.LibraryCommands.UpdatePdf(libraryBook, liberatedStatus); + ApplicationServices.LibraryCommands.UpdatePdf(libraryBook.Book, liberatedStatus); return result; } diff --git a/LibationWinForms/BookLiberation/ProcessorAutomationController.cs b/LibationWinForms/BookLiberation/ProcessorAutomationController.cs index 034be1af..820fbc31 100644 --- a/LibationWinForms/BookLiberation/ProcessorAutomationController.cs +++ b/LibationWinForms/BookLiberation/ProcessorAutomationController.cs @@ -246,7 +246,7 @@ $@" Title: {libraryBook.Book.Title} if (dialogResult == SkipResult) { - ApplicationServices.LibraryCommands.UpdateBook(libraryBook, LiberatedStatus.Error); + ApplicationServices.LibraryCommands.UpdateBook(libraryBook.Book, LiberatedStatus.Error); LogMe.Info($"Error. Skip: [{libraryBook.Book.AudibleProductId}] {libraryBook.Book.Title}"); } diff --git a/LibationWinForms/Form1.cs b/LibationWinForms/Form1.cs index a114b094..8a1a1fb5 100644 --- a/LibationWinForms/Form1.cs +++ b/LibationWinForms/Form1.cs @@ -392,7 +392,7 @@ namespace LibationWinForms private async void convertAllM4bToMp3ToolStripMenuItem_Click(object sender, EventArgs e) => await BookLiberation.ProcessorAutomationController.ConvertAllBooksAsync(); - private void updateGridRow(object _, LibraryBook libraryBook) => currProductsGrid.RefreshRow(libraryBook.Book.AudibleProductId); + private void updateGridRow(object _, LibraryBook libraryBook) => currProductsGrid.RefreshLiberatedStatus(libraryBook); #endregion #region Export menu From d8268857280bcdb7f56c524631fa610a52e65311 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sat, 21 Aug 2021 18:37:07 -0600 Subject: [PATCH 05/13] Fix display for new LiberatedStatus values. --- LibationWinForms/LiberateDataGridViewImageButtonColumn.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/LibationWinForms/LiberateDataGridViewImageButtonColumn.cs b/LibationWinForms/LiberateDataGridViewImageButtonColumn.cs index 6b85a4df..f1769f36 100644 --- a/LibationWinForms/LiberateDataGridViewImageButtonColumn.cs +++ b/LibationWinForms/LiberateDataGridViewImageButtonColumn.cs @@ -40,6 +40,7 @@ namespace LibationWinForms LiberatedStatus.Liberated => ("Liberated", "green"), LiberatedStatus.PartialDownload => ("File has been at least\r\npartially downloaded", "yellow"), LiberatedStatus.NotLiberated => ("Book NOT downloaded", "red"), + LiberatedStatus.Error => ("Book downloaded ERROR", "red"), _ => throw new Exception("Unexpected liberation state") }; @@ -47,6 +48,7 @@ namespace LibationWinForms { LiberatedStatus.Liberated => ("\r\nPDF downloaded", "_pdf_yes"), LiberatedStatus.NotLiberated => ("\r\nPDF NOT downloaded", "_pdf_no"), + LiberatedStatus.Error => ("\r\nPDF downloaded ERROR", "_pdf_no"), null => ("", ""), _ => throw new Exception("Unexpected PDF state") }; From 40b3a9990dcd58e9773c85f8fe9792f121a475f6 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sat, 21 Aug 2021 20:49:36 -0600 Subject: [PATCH 06/13] FileLiberator is now DB ignorant. IProcessables update UserDaefinedData which notifies the view model. --- DataLayer/EfClasses/UserDefinedItem.cs | 40 +++++++++++++++++-- FileLiberator/DownloadDecryptBook.cs | 5 +-- FileLiberator/DownloadPdf.cs | 3 +- FileLiberator/FileLiberator.csproj | 1 - FileLiberator/IProcessableExt.cs | 7 +--- Libation.sln | 4 +- .../ProcessorAutomationController.cs | 19 +++++---- LibationWinForms/LibationWinForms.csproj | 1 + 8 files changed, 52 insertions(+), 28 deletions(-) diff --git a/DataLayer/EfClasses/UserDefinedItem.cs b/DataLayer/EfClasses/UserDefinedItem.cs index 4643d02e..18bb4fd5 100644 --- a/DataLayer/EfClasses/UserDefinedItem.cs +++ b/DataLayer/EfClasses/UserDefinedItem.cs @@ -42,7 +42,11 @@ namespace DataLayer public string Tags { get => _tags; - set => _tags = sanitize(value); + set + { + _tags = sanitize(value); + ItemChanged?.Invoke(this, nameof(Tags)); + } } public IEnumerable TagsEnumerated => Tags == "" ? new string[0] : Tags.Split(null as char[], StringSplitOptions.RemoveEmptyEntries); @@ -99,10 +103,38 @@ namespace DataLayer #endregion #region LiberatedStatuses - public LiberatedStatus BookStatus { get; set; } - public LiberatedStatus? PdfStatus { get; set; } - #endregion + private LiberatedStatus _bookStatus; + private LiberatedStatus? _pdfStatus; + public LiberatedStatus BookStatus + { + get => _bookStatus; + set + { + if (_bookStatus != value) + { + _bookStatus = value; + ItemChanged?.Invoke(this, nameof(BookStatus)); + } + } + } + public LiberatedStatus? PdfStatus + { + get => _pdfStatus; + set + { + if (_pdfStatus != value) + { + _pdfStatus = value; + ItemChanged?.Invoke(this, nameof(PdfStatus)); + } + } + } + #endregion + /// + /// Occurs when , , or values change. + /// + public static event EventHandler ItemChanged; public override string ToString() => $"{Book} {Rating} {Tags}"; } } diff --git a/FileLiberator/DownloadDecryptBook.cs b/FileLiberator/DownloadDecryptBook.cs index 4acf9681..8dc6eea8 100644 --- a/FileLiberator/DownloadDecryptBook.cs +++ b/FileLiberator/DownloadDecryptBook.cs @@ -52,10 +52,7 @@ namespace FileLiberator if (!moveResults.movedAudioFile) return new StatusHandler { "Cannot find final audio file after decryption" }; - // need to update before Audio_Exists will return true - ApplicationServices.LibraryCommands.UpdateBook(libraryBook.Book, LiberatedStatus.Liberated); - - + libraryBook.Book.UserDefinedItem.BookStatus = LiberatedStatus.Liberated; return new StatusHandler(); } diff --git a/FileLiberator/DownloadPdf.cs b/FileLiberator/DownloadPdf.cs index 0919c841..a9b8815c 100644 --- a/FileLiberator/DownloadPdf.cs +++ b/FileLiberator/DownloadPdf.cs @@ -23,8 +23,7 @@ namespace FileLiberator await downloadPdfAsync(libraryBook, proposedDownloadFilePath); var result = verifyDownload(libraryBook); - var liberatedStatus = result.IsSuccess ? LiberatedStatus.Liberated : LiberatedStatus.NotLiberated; - ApplicationServices.LibraryCommands.UpdatePdf(libraryBook.Book, liberatedStatus); + libraryBook.Book.UserDefinedItem.PdfStatus = result.IsSuccess ? LiberatedStatus.Liberated : LiberatedStatus.NotLiberated; return result; } diff --git a/FileLiberator/FileLiberator.csproj b/FileLiberator/FileLiberator.csproj index 08a7d736..776a70fa 100644 --- a/FileLiberator/FileLiberator.csproj +++ b/FileLiberator/FileLiberator.csproj @@ -7,7 +7,6 @@ - diff --git a/FileLiberator/IProcessableExt.cs b/FileLiberator/IProcessableExt.cs index 3a4339e3..0ba57965 100644 --- a/FileLiberator/IProcessableExt.cs +++ b/FileLiberator/IProcessableExt.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using ApplicationServices; using DataLayer; using Dinah.Core; using Dinah.Core.ErrorHandling; @@ -18,10 +17,8 @@ namespace FileLiberator // when used in foreach: stateful. deferred execution - public static IEnumerable GetValidLibraryBooks(this IProcessable processable) - => DbContexts.GetContext() - .GetLibrary_Flat_NoTracking() - .Where(libraryBook => processable.Validate(libraryBook)); + public static IEnumerable GetValidLibraryBooks(this IProcessable processable, IEnumerable library) + => library.Where(libraryBook => processable.Validate(libraryBook)); public static async Task ProcessSingleAsync(this IProcessable processable, LibraryBook libraryBook, bool validate) { diff --git a/Libation.sln b/Libation.sln index 8d11ecc4..1dcd66ee 100644 --- a/Libation.sln +++ b/Libation.sln @@ -86,7 +86,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dinah.EntityFrameworkCore.T EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AAXClean", "..\AAXClean\AAXClean.csproj", "{94BEB7CC-511D-45AB-9F09-09BE858EE486}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hoopla", "Hoopla\Hoopla.csproj", "{D8F56E5A-3E65-41A6-B7E7-C4515A264B1F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hoopla", "Hoopla\Hoopla.csproj", "{D8F56E5A-3E65-41A6-B7E7-C4515A264B1F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -222,7 +222,7 @@ Global {8BD8E012-F44F-4EE2-A234-D66C14D5FE4B} = {7FBBB086-0807-4998-85BF-6D1A49C8AD05} {1AE65B61-9C05-4C80-ABFF-48F16E22FDF1} = {7FBBB086-0807-4998-85BF-6D1A49C8AD05} {59A10DF3-63EC-43F1-A3BF-4000CFA118D2} = {751093DD-5DBA-463E-ADBE-E05FAFB6983E} - {393B5B27-D15C-4F77-9457-FA14BA8F3C73} = {41CDCC73-9B81-49DD-9570-C54406E852AF} + {393B5B27-D15C-4F77-9457-FA14BA8F3C73} = {7FBBB086-0807-4998-85BF-6D1A49C8AD05} {06882742-27A6-4347-97D9-56162CEC9C11} = {F0CBB7A7-D3FB-41FF-8F47-CF3F6A592249} {2E1F5DB4-40CC-4804-A893-5DCE0193E598} = {41CDCC73-9B81-49DD-9570-C54406E852AF} {9F1AA3DE-962F-469B-82B2-46F93491389B} = {F61184E7-2426-4A13-ACEF-5689928E2CE2} diff --git a/LibationWinForms/BookLiberation/ProcessorAutomationController.cs b/LibationWinForms/BookLiberation/ProcessorAutomationController.cs index 820fbc31..6d604184 100644 --- a/LibationWinForms/BookLiberation/ProcessorAutomationController.cs +++ b/LibationWinForms/BookLiberation/ProcessorAutomationController.cs @@ -50,24 +50,24 @@ namespace LibationWinForms.BookLiberation public static class ProcessorAutomationController { - public static async Task BackupSingleBookAsync(LibraryBook libraryBook, EventHandler completedAction = null) + public static async Task BackupSingleBookAsync(LibraryBook libraryBook) { Serilog.Log.Logger.Information($"Begin {nameof(BackupSingleBookAsync)} {{@DebugInfo}}", new { libraryBook?.Book?.AudibleProductId }); var logMe = LogMe.RegisterForm(); - var backupBook = CreateBackupBook(completedAction, logMe); + var backupBook = CreateBackupBook(logMe); // continue even if libraryBook is null. we'll display even that in the processing box await new BackupSingle(logMe, backupBook, libraryBook).RunBackupAsync(); } - public static async Task BackupAllBooksAsync(EventHandler completedAction = null) + public static async Task BackupAllBooksAsync() { Serilog.Log.Logger.Information("Begin " + nameof(BackupAllBooksAsync)); var automatedBackupsForm = new AutomatedBackupsForm(); var logMe = LogMe.RegisterForm(automatedBackupsForm); - var backupBook = CreateBackupBook(completedAction, logMe); + var backupBook = CreateBackupBook(logMe); await new BackupLoop(logMe, backupBook, automatedBackupsForm).RunBackupAsync(); } @@ -84,19 +84,19 @@ namespace LibationWinForms.BookLiberation await new BackupLoop(logMe, convertBook, automatedBackupsForm).RunBackupAsync(); } - public static async Task BackupAllPdfsAsync(EventHandler completedAction = null) + public static async Task BackupAllPdfsAsync() { Serilog.Log.Logger.Information("Begin " + nameof(BackupAllPdfsAsync)); var automatedBackupsForm = new AutomatedBackupsForm(); var logMe = LogMe.RegisterForm(automatedBackupsForm); - var downloadPdf = CreateProcessable(logMe, completedAction); + var downloadPdf = CreateProcessable(logMe); await new BackupLoop(logMe, downloadPdf, automatedBackupsForm).RunBackupAsync(); } - private static IProcessable CreateBackupBook(EventHandler completedAction, LogMe logMe) + private static IProcessable CreateBackupBook(LogMe logMe) { var downloadPdf = CreateProcessable(logMe); @@ -104,7 +104,6 @@ namespace LibationWinForms.BookLiberation async void onDownloadDecryptBookCompleted(object sender, LibraryBook e) { await downloadPdf.TryProcessAsync(e); - completedAction(sender, e); } var downloadDecryptBook = CreateProcessable(logMe, onDownloadDecryptBookCompleted); @@ -246,7 +245,7 @@ $@" Title: {libraryBook.Book.Title} if (dialogResult == SkipResult) { - ApplicationServices.LibraryCommands.UpdateBook(libraryBook.Book, LiberatedStatus.Error); + libraryBook.Book.UserDefinedItem.BookStatus = LiberatedStatus.Error; LogMe.Info($"Error. Skip: [{libraryBook.Book.AudibleProductId}] {libraryBook.Book.Title}"); } @@ -305,7 +304,7 @@ An error occurred while trying to process this book. 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()) + foreach (var libraryBook in Processable.GetValidLibraryBooks(ApplicationServices.DbContexts.GetContext().GetLibrary_Flat_NoTracking())) { var keepGoing = await ProcessOneAsync(libraryBook, validate: false); if (!keepGoing) diff --git a/LibationWinForms/LibationWinForms.csproj b/LibationWinForms/LibationWinForms.csproj index 343425c6..7eee10cc 100644 --- a/LibationWinForms/LibationWinForms.csproj +++ b/LibationWinForms/LibationWinForms.csproj @@ -14,6 +14,7 @@ + From 9e06c343c1fa8641e764469cf6421a18929abc44 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sat, 21 Aug 2021 21:15:25 -0600 Subject: [PATCH 07/13] Don't check if values have changed when updating the database. --- ApplicationServices/LibraryCommands.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/ApplicationServices/LibraryCommands.cs b/ApplicationServices/LibraryCommands.cs index 6643a50a..d544a77f 100644 --- a/ApplicationServices/LibraryCommands.cs +++ b/ApplicationServices/LibraryCommands.cs @@ -164,11 +164,7 @@ namespace ApplicationServices var udi = libraryBook.UserDefinedItem; - if (udi.Tags == newTags) - return 0; - // Attach() NoTracking entities before SaveChanges() - udi.Tags = newTags; context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; var qtyChanges = context.SaveChanges(); if (qtyChanges > 0) @@ -191,11 +187,7 @@ namespace ApplicationServices var udi = libraryBook.UserDefinedItem; - if (udi.BookStatus == liberatedStatus) - return 0; - // Attach() NoTracking entities before SaveChanges() - udi.BookStatus = liberatedStatus; context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; var qtyChanges = context.SaveChanges(); if (qtyChanges > 0) @@ -218,11 +210,7 @@ namespace ApplicationServices var udi = libraryBook.UserDefinedItem; - if (udi.PdfStatus == liberatedStatus) - return 0; - // Attach() NoTracking entities before SaveChanges() - udi.PdfStatus = liberatedStatus; context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; var qtyChanges = context.SaveChanges(); From 2a7e185dc30902f311b8ea24d81ca97681127571 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sat, 21 Aug 2021 21:17:22 -0600 Subject: [PATCH 08/13] Finish MVVM conversion --- ApplicationServices/LibraryCommands.cs | 3 + DataLayer/EfClasses/UserDefinedItem.cs | 8 ++- LibationWinForms/Form1.cs | 5 +- LibationWinForms/GridEntry.cs | 78 ++++++++++++++++++++++++-- LibationWinForms/ProductsGrid.cs | 33 ++--------- 5 files changed, 89 insertions(+), 38 deletions(-) diff --git a/ApplicationServices/LibraryCommands.cs b/ApplicationServices/LibraryCommands.cs index d544a77f..1d428237 100644 --- a/ApplicationServices/LibraryCommands.cs +++ b/ApplicationServices/LibraryCommands.cs @@ -163,6 +163,7 @@ namespace ApplicationServices using var context = DbContexts.GetContext(); var udi = libraryBook.UserDefinedItem; + udi.Tags = newTags; // Attach() NoTracking entities before SaveChanges() context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; @@ -186,6 +187,7 @@ namespace ApplicationServices using var context = DbContexts.GetContext(); var udi = libraryBook.UserDefinedItem; + udi.BookStatus = liberatedStatus; // Attach() NoTracking entities before SaveChanges() context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; @@ -209,6 +211,7 @@ namespace ApplicationServices using var context = DbContexts.GetContext(); var udi = libraryBook.UserDefinedItem; + udi.PdfStatus = liberatedStatus; // Attach() NoTracking entities before SaveChanges() context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; diff --git a/DataLayer/EfClasses/UserDefinedItem.cs b/DataLayer/EfClasses/UserDefinedItem.cs index 18bb4fd5..5bd52beb 100644 --- a/DataLayer/EfClasses/UserDefinedItem.cs +++ b/DataLayer/EfClasses/UserDefinedItem.cs @@ -44,8 +44,12 @@ namespace DataLayer get => _tags; set { - _tags = sanitize(value); - ItemChanged?.Invoke(this, nameof(Tags)); + var newTags = sanitize(value); + if (_tags != newTags) + { + _tags = newTags; + ItemChanged?.Invoke(this, nameof(Tags)); + } } } diff --git a/LibationWinForms/Form1.cs b/LibationWinForms/Form1.cs index 8a1a1fb5..134a2123 100644 --- a/LibationWinForms/Form1.cs +++ b/LibationWinForms/Form1.cs @@ -384,15 +384,14 @@ namespace LibationWinForms #region liberate menu private async void beginBookBackupsToolStripMenuItem_Click(object sender, EventArgs e) - => await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync(updateGridRow); + => await BookLiberation.ProcessorAutomationController.BackupAllBooksAsync(); private async void beginPdfBackupsToolStripMenuItem_Click(object sender, EventArgs e) - => await BookLiberation.ProcessorAutomationController.BackupAllPdfsAsync(updateGridRow); + => await BookLiberation.ProcessorAutomationController.BackupAllPdfsAsync(); private async void convertAllM4bToMp3ToolStripMenuItem_Click(object sender, EventArgs e) => await BookLiberation.ProcessorAutomationController.ConvertAllBooksAsync(); - private void updateGridRow(object _, LibraryBook libraryBook) => currProductsGrid.RefreshLiberatedStatus(libraryBook); #endregion #region Export menu diff --git a/LibationWinForms/GridEntry.cs b/LibationWinForms/GridEntry.cs index d6f414bf..b2184d6e 100644 --- a/LibationWinForms/GridEntry.cs +++ b/LibationWinForms/GridEntry.cs @@ -12,6 +12,9 @@ using Dinah.Core.Drawing; namespace LibationWinForms { + /// + /// The View Model for a LibraryBook + /// internal class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable { #region implementation properties @@ -26,12 +29,15 @@ namespace LibationWinForms private Book Book => LibraryBook.Book; private Image _cover; + private Action _refilter; - public GridEntry(LibraryBook libraryBook) + public GridEntry(LibraryBook libraryBook, Action refilterOnChanged = null) { LibraryBook = libraryBook; + _refilter = refilterOnChanged; _memberValues = CreateMemberValueDictionary(); + //Get cover art. If it's default, subscribe to PictureCached { (bool isDefault, byte[] picture) = FileManager.PictureStorage.GetPicture(new FileManager.PictureDefinition(Book.PictureId, FileManager.PictureSize._80x80)); @@ -58,7 +64,7 @@ namespace LibationWinForms Description = GetDescriptionDisplay(Book); } - //DisplayTags and Liberate properties are live. + UserDefinedItem.ItemChanged += UserDefinedItem_ItemChanged; } private void PictureStorage_PictureCached(object sender, FileManager.PictureCachedEventArgs e) @@ -70,7 +76,48 @@ namespace LibationWinForms } } - #region Data Source properties + #region detect changes to the model and update the view + + /// + /// This event handler receives notifications from the model that it has changed. + /// Save to the database and notify the view that it's changed. + /// + private void UserDefinedItem_ItemChanged(object sender, string itemName) + { + var udi = sender as UserDefinedItem; + + if (udi.Book.AudibleProductId != LibraryBook.Book.AudibleProductId) + return; + + switch (itemName) + { + case nameof(udi.Tags): + { + LibraryCommands.UpdateTags(LibraryBook.Book, udi.Tags); + NotifyPropertyChanged(nameof(DisplayTags)); + } + break; + case nameof(udi.BookStatus): + { + var status = udi.BookStatus == LiberatedStatus.PartialDownload ? LiberatedStatus.NotLiberated : udi.BookStatus; + LibraryCommands.UpdateBook(LibraryBook.Book, status); + NotifyPropertyChanged(nameof(Liberate)); + } + break; + case nameof(udi.PdfStatus): + { + LibraryCommands.UpdatePdf(LibraryBook.Book, udi.PdfStatus); + NotifyPropertyChanged(nameof(Liberate)); + } + break; + } + + _refilter?.Invoke(); + } + + #endregion + + #region Model properties exposed to the view public Image Cover { get @@ -95,8 +142,22 @@ namespace LibationWinForms public string Category { get; } public string Misc { get; } public string Description { get; } - public string DisplayTags => string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated); - public (LiberatedStatus BookStatus, LiberatedStatus? PdfStatus) Liberate => (LibraryCommands.Liberated_Status(Book), LibraryCommands.Pdf_Status(Book)); + public string DisplayTags + { + get=> Book.UserDefinedItem.Tags; + set => Book.UserDefinedItem.Tags = value; + } + public (LiberatedStatus BookStatus, LiberatedStatus? PdfStatus) Liberate + { + get => (LibraryCommands.Liberated_Status(LibraryBook.Book), LibraryCommands.Pdf_Status(LibraryBook.Book)); + + set + { + LibraryBook.Book.UserDefinedItem.BookStatus = value.BookStatus; + LibraryBook.Book.UserDefinedItem.PdfStatus = value.PdfStatus; + } + } + #endregion #region Data Sorting @@ -199,5 +260,12 @@ namespace LibationWinForms } #endregion + + ~GridEntry() + { + UserDefinedItem.ItemChanged -= UserDefinedItem_ItemChanged; + FileManager.PictureStorage.PictureCached -= PictureStorage_PictureCached; + } } + } diff --git a/LibationWinForms/ProductsGrid.cs b/LibationWinForms/ProductsGrid.cs index 46b721a4..043c0a1e 100644 --- a/LibationWinForms/ProductsGrid.cs +++ b/LibationWinForms/ProductsGrid.cs @@ -86,7 +86,7 @@ namespace LibationWinForms } // else: liberate - await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook, (_, __) => RefreshRow(libraryBook.Book.AudibleProductId)); + await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook); } private void Details_Click(GridEntry liveGridEntry) @@ -95,15 +95,10 @@ namespace LibationWinForms if (bookDetailsForm.ShowDialog() != DialogResult.OK) return; - var qtyChanges = LibraryCommands.UpdateUserDefinedItem(liveGridEntry.LibraryBook.Book, bookDetailsForm.NewTags, bookDetailsForm.BookLiberatedStatus, bookDetailsForm.PdfLiberatedStatus); - if (qtyChanges == 0) - return; + liveGridEntry.DisplayTags = bookDetailsForm.NewTags; + liveGridEntry.Liberate = (bookDetailsForm.BookLiberatedStatus, bookDetailsForm.PdfLiberatedStatus); - //Re-apply filters - Filter(); - - //Update whole GridEntry row - liveGridEntry.NotifyPropertyChanged(); + BackupCountsChanged?.Invoke(this, EventArgs.Empty); } #endregion @@ -132,7 +127,7 @@ namespace LibationWinForms } var orderedGridEntries = lib - .Select(lb => new GridEntry(lb)).ToList() + .Select(lb => new GridEntry(lb, Filter)).ToList() // default load order .OrderByDescending(ge => (DateTime)ge.GetMemberValue(nameof(ge.PurchaseDate))) //// more advanced example: sort by author, then series, then title @@ -150,19 +145,6 @@ namespace LibationWinForms BackupCountsChanged?.Invoke(this, EventArgs.Empty); } - public void RefreshRow(string productId) - { - var liveGridEntry = getGridEntry((ge) => ge.AudibleProductId == productId); - - // update GridEntry Liberate cell - liveGridEntry?.NotifyPropertyChanged(nameof(liveGridEntry.Liberate)); - - // needed in case filtering by -IsLiberated and it gets changed to Liberated. want to immediately show the change - Filter(); - - BackupCountsChanged?.Invoke(this, EventArgs.Empty); - } - #endregion #region Filter @@ -195,12 +177,7 @@ namespace LibationWinForms #endregion #region DataGridView Macro - - private GridEntry getGridEntry(Func predicate) - => ((SortableBindingList)gridEntryBindingSource.DataSource).FirstOrDefault(predicate); - private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem(rowIndex); - #endregion } } From e4257afc14b8b7ada86d060a77da26393a8dd5c0 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sat, 21 Aug 2021 22:06:54 -0600 Subject: [PATCH 09/13] Version Num --- LibationLauncher/LibationLauncher.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LibationLauncher/LibationLauncher.csproj b/LibationLauncher/LibationLauncher.csproj index c6dc6bb2..c437de6d 100644 --- a/LibationLauncher/LibationLauncher.csproj +++ b/LibationLauncher/LibationLauncher.csproj @@ -13,7 +13,7 @@ win-x64 - 5.5.2.1 + 5.5.3.1 From 673451dc11106cb21a203a64b09e8f98f72eacd9 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sat, 21 Aug 2021 22:08:35 -0600 Subject: [PATCH 10/13] Git resolve --- FileLiberator/DownloadDecryptBook.cs | 84 +++++++++++++--------------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/FileLiberator/DownloadDecryptBook.cs b/FileLiberator/DownloadDecryptBook.cs index 8dc6eea8..450cdb6a 100644 --- a/FileLiberator/DownloadDecryptBook.cs +++ b/FileLiberator/DownloadDecryptBook.cs @@ -16,22 +16,22 @@ namespace FileLiberator public class DownloadDecryptBook : IAudioDecodable { - private AaxcDownloadConverter aaxcDownloader; + private AaxcDownloadConverter aaxcDownloader; - public event EventHandler StreamingTimeRemaining; - public event EventHandler> RequestCoverArt; - public event EventHandler TitleDiscovered; - public event EventHandler AuthorsDiscovered; - public event EventHandler NarratorsDiscovered; - public event EventHandler CoverImageDiscovered; - public event EventHandler StreamingBegin; - public event EventHandler StreamingProgressChanged; - public event EventHandler StreamingCompleted; - public event EventHandler Begin; - public event EventHandler StatusUpdate; - public event EventHandler Completed; + public event EventHandler StreamingTimeRemaining; + public event EventHandler> RequestCoverArt; + public event EventHandler TitleDiscovered; + public event EventHandler AuthorsDiscovered; + public event EventHandler NarratorsDiscovered; + public event EventHandler CoverImageDiscovered; + public event EventHandler StreamingBegin; + public event EventHandler StreamingProgressChanged; + public event EventHandler StreamingCompleted; + public event EventHandler Begin; + public event EventHandler StatusUpdate; + public event EventHandler Completed; - public async Task ProcessAsync(LibraryBook libraryBook) + public async Task ProcessAsync(LibraryBook libraryBook) { Begin?.Invoke(this, libraryBook); @@ -47,12 +47,10 @@ namespace FileLiberator return new StatusHandler { "Decrypt failed" }; // moves files and returns dest dir - var moveResults = MoveFilesToBooksDir(libraryBook.Book, outputAudioFilename); + _ = moveFilesToBooksDir(libraryBook.Book, outputAudioFilename); - if (!moveResults.movedAudioFile) - return new StatusHandler { "Cannot find final audio file after decryption" }; - - libraryBook.Book.UserDefinedItem.BookStatus = LiberatedStatus.Liberated; + // only need to update if success. if failure, it will remain at 0 == NotLiberated + ApplicationServices.LibraryCommands.UpdateBook(libraryBook, LiberatedStatus.Liberated); return new StatusHandler(); } @@ -125,7 +123,7 @@ namespace FileLiberator } - private void AaxcDownloader_RetrievedCoverArt(object sender, byte[] e) + private void AaxcDownloader_RetrievedCoverArt(object sender, byte[] e) { if (e is null && Configuration.Instance.AllowLibationFixup) { @@ -142,10 +140,10 @@ namespace FileLiberator { TitleDiscovered?.Invoke(this, e.TitleSansUnabridged); AuthorsDiscovered?.Invoke(this, e.FirstAuthor ?? "[unknown]"); - NarratorsDiscovered?.Invoke(this, e.Narrator ?? "[unknown]"); + NarratorsDiscovered?.Invoke(this, e.Narrator ?? "[unknown]"); } - private static (string destinationDir, bool movedAudioFile) MoveFilesToBooksDir(Book product, string outputAudioFilename) + private static string moveFilesToBooksDir(Book product, string outputAudioFilename) { // create final directory. move each file into it. MOVE AUDIO FILE LAST // new dir: safetitle_limit50char + " [" + productId + "]" @@ -160,7 +158,6 @@ namespace FileLiberator // audio filename: safetitle_limit50char + " [" + productId + "]." + audio_ext var audioFileName = FileUtility.GetValidFilename(destinationDir, product.Title, musicFileExt, product.AudibleProductId); - bool movedAudioFile = false; foreach (var f in sortedFiles) { var dest @@ -173,14 +170,11 @@ namespace FileLiberator Cue.UpdateFileName(f, audioFileName); File.Move(f.FullName, dest); - - movedAudioFile |= AudibleFileStorage.Audio.IsFileTypeMatch(f); - } AudibleFileStorage.Audio.Refresh(); - return (destinationDir, movedAudioFile); + return destinationDir; } private static List getProductFilesSorted(Book product, string outputAudioFilename) @@ -203,26 +197,26 @@ namespace FileLiberator } private static void validate(LibraryBook libraryBook) - { - string errorString(string field) - => $"{errorTitle()}\r\nCannot download book. {field} is not known. Try re-importing the account which owns this book."; + { + string errorString(string field) + => $"{errorTitle()}\r\nCannot download book. {field} is not known. Try re-importing the account which owns this book."; - string errorTitle() - { - var title - = (libraryBook.Book.Title.Length > 53) - ? $"{libraryBook.Book.Title.Truncate(50)}..." - : libraryBook.Book.Title; - var errorBookTitle = $"{title} [{libraryBook.Book.AudibleProductId}]"; - return errorBookTitle; - }; + string errorTitle() + { + var title + = (libraryBook.Book.Title.Length > 53) + ? $"{libraryBook.Book.Title.Truncate(50)}..." + : libraryBook.Book.Title; + var errorBookTitle = $"{title} [{libraryBook.Book.AudibleProductId}]"; + return errorBookTitle; + }; - if (string.IsNullOrWhiteSpace(libraryBook.Account)) - throw new Exception(errorString("Account")); + if (string.IsNullOrWhiteSpace(libraryBook.Account)) + throw new Exception(errorString("Account")); - if (string.IsNullOrWhiteSpace(libraryBook.Book.Locale)) - throw new Exception(errorString("Locale")); - } + if (string.IsNullOrWhiteSpace(libraryBook.Book.Locale)) + throw new Exception(errorString("Locale")); + } public bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists; @@ -231,4 +225,4 @@ namespace FileLiberator aaxcDownloader?.Cancel(); } } -} +} \ No newline at end of file From 4ceb4f9c03eba99bebf40b4131695129ef29ba69 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sat, 21 Aug 2021 22:09:13 -0600 Subject: [PATCH 11/13] Change back. --- FileLiberator/DownloadDecryptBook.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/FileLiberator/DownloadDecryptBook.cs b/FileLiberator/DownloadDecryptBook.cs index 450cdb6a..db7fc144 100644 --- a/FileLiberator/DownloadDecryptBook.cs +++ b/FileLiberator/DownloadDecryptBook.cs @@ -47,10 +47,12 @@ namespace FileLiberator return new StatusHandler { "Decrypt failed" }; // moves files and returns dest dir - _ = moveFilesToBooksDir(libraryBook.Book, outputAudioFilename); + var moveResults = MoveFilesToBooksDir(libraryBook.Book, outputAudioFilename); - // only need to update if success. if failure, it will remain at 0 == NotLiberated - ApplicationServices.LibraryCommands.UpdateBook(libraryBook, LiberatedStatus.Liberated); + if (!moveResults.movedAudioFile) + return new StatusHandler { "Cannot find final audio file after decryption" }; + + libraryBook.Book.UserDefinedItem.BookStatus = LiberatedStatus.Liberated; return new StatusHandler(); } @@ -143,7 +145,7 @@ namespace FileLiberator NarratorsDiscovered?.Invoke(this, e.Narrator ?? "[unknown]"); } - private static string moveFilesToBooksDir(Book product, string outputAudioFilename) + private static (string destinationDir, bool movedAudioFile) MoveFilesToBooksDir(Book product, string outputAudioFilename) { // create final directory. move each file into it. MOVE AUDIO FILE LAST // new dir: safetitle_limit50char + " [" + productId + "]" @@ -158,6 +160,7 @@ namespace FileLiberator // audio filename: safetitle_limit50char + " [" + productId + "]." + audio_ext var audioFileName = FileUtility.GetValidFilename(destinationDir, product.Title, musicFileExt, product.AudibleProductId); + bool movedAudioFile = false; foreach (var f in sortedFiles) { var dest @@ -170,11 +173,14 @@ namespace FileLiberator Cue.UpdateFileName(f, audioFileName); File.Move(f.FullName, dest); + + movedAudioFile |= AudibleFileStorage.Audio.IsFileTypeMatch(f); + } AudibleFileStorage.Audio.Refresh(); - return destinationDir; + return (destinationDir, movedAudioFile); } private static List getProductFilesSorted(Book product, string outputAudioFilename) @@ -225,4 +231,4 @@ namespace FileLiberator aaxcDownloader?.Cancel(); } } -} \ No newline at end of file +} From 6beb5cc74a67afaec297ac626e248dc40a1416de Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sun, 22 Aug 2021 13:27:39 -0600 Subject: [PATCH 12/13] Made changes discussed. --- ApplicationServices/LibraryCommands.cs | 61 +++----------------------- LibationWinForms/GridEntry.cs | 50 ++++++++++++++++----- LibationWinForms/ProductsGrid.cs | 4 ++ 3 files changed, 49 insertions(+), 66 deletions(-) diff --git a/ApplicationServices/LibraryCommands.cs b/ApplicationServices/LibraryCommands.cs index 1d428237..ed36595e 100644 --- a/ApplicationServices/LibraryCommands.cs +++ b/ApplicationServices/LibraryCommands.cs @@ -154,74 +154,25 @@ namespace ApplicationServices } #endregion - #region Update book details - - public static int UpdateTags(Book libraryBook, string newTags) + #region Update book details + + public static int UpdateUserDefinedItem(Book book) { try { using var context = DbContexts.GetContext(); - var udi = libraryBook.UserDefinedItem; - udi.Tags = newTags; - // Attach() NoTracking entities before SaveChanges() - context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; + context.Attach(book.UserDefinedItem).State = Microsoft.EntityFrameworkCore.EntityState.Modified; var qtyChanges = context.SaveChanges(); if (qtyChanges > 0) - SearchEngineCommands.UpdateBookTags(libraryBook); + SearchEngineCommands.UpdateLiberatedStatus(book); return qtyChanges; } catch (Exception ex) { - Log.Logger.Error(ex, "Error updating tags"); - throw; - } - } - - public static int UpdateBook(Book libraryBook, LiberatedStatus liberatedStatus) - { - try - { - using var context = DbContexts.GetContext(); - - var udi = libraryBook.UserDefinedItem; - udi.BookStatus = liberatedStatus; - - // Attach() NoTracking entities before SaveChanges() - context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; - var qtyChanges = context.SaveChanges(); - if (qtyChanges > 0) - SearchEngineCommands.UpdateLiberatedStatus(libraryBook); - - return qtyChanges; - } - catch (Exception ex) - { - Log.Logger.Error(ex, "Error updating audiobook status"); - throw; - } - } - - public static int UpdatePdf(Book libraryBook, LiberatedStatus? liberatedStatus) - { - try - { - using var context = DbContexts.GetContext(); - - var udi = libraryBook.UserDefinedItem; - udi.PdfStatus = liberatedStatus; - - // Attach() NoTracking entities before SaveChanges() - context.Attach(udi).State = Microsoft.EntityFrameworkCore.EntityState.Modified; - var qtyChanges = context.SaveChanges(); - - return qtyChanges; - } - catch (Exception ex) - { - Log.Logger.Error(ex, "Error updating pdf status"); + Log.Logger.Error(ex, $"Error updating {nameof(book.UserDefinedItem)}"); throw; } } diff --git a/LibationWinForms/GridEntry.cs b/LibationWinForms/GridEntry.cs index b2184d6e..3cc8922c 100644 --- a/LibationWinForms/GridEntry.cs +++ b/LibationWinForms/GridEntry.cs @@ -29,12 +29,12 @@ namespace LibationWinForms private Book Book => LibraryBook.Book; private Image _cover; - private Action _refilter; + private Action Refilter { get; } public GridEntry(LibraryBook libraryBook, Action refilterOnChanged = null) { LibraryBook = libraryBook; - _refilter = refilterOnChanged; + Refilter = refilterOnChanged; _memberValues = CreateMemberValueDictionary(); @@ -76,7 +76,7 @@ namespace LibationWinForms } } - #region detect changes to the model and update the view + #region detect changes to the model, update the view, and save to database. /// /// This event handler receives notifications from the model that it has changed. @@ -86,33 +86,62 @@ namespace LibationWinForms { var udi = sender as UserDefinedItem; - if (udi.Book.AudibleProductId != LibraryBook.Book.AudibleProductId) + if (udi.Book.AudibleProductId != Book.AudibleProductId) return; switch (itemName) { case nameof(udi.Tags): { - LibraryCommands.UpdateTags(LibraryBook.Book, udi.Tags); + Book.UserDefinedItem.Tags = udi.Tags; NotifyPropertyChanged(nameof(DisplayTags)); } break; case nameof(udi.BookStatus): { - var status = udi.BookStatus == LiberatedStatus.PartialDownload ? LiberatedStatus.NotLiberated : udi.BookStatus; - LibraryCommands.UpdateBook(LibraryBook.Book, status); + Book.UserDefinedItem.BookStatus = udi.BookStatus; NotifyPropertyChanged(nameof(Liberate)); } break; case nameof(udi.PdfStatus): { - LibraryCommands.UpdatePdf(LibraryBook.Book, udi.PdfStatus); + Book.UserDefinedItem.PdfStatus = udi.PdfStatus; NotifyPropertyChanged(nameof(Liberate)); } break; } - _refilter?.Invoke(); + if (!suspendCommit) + Commit(); + + Refilter?.Invoke(); + } + private bool suspendCommit = false; + + /// + /// Begin editing the model, suspending commits until is called. + /// + public void BeginEdit() => suspendCommit = true; + + /// + /// Save all edits to the database. + /// + public void EndEdit() + { + Commit(); + suspendCommit = false; + } + + private void Commit() + { + //We don't want LiberatedStatus.PartialDownload to be a persistent status. + var bookStatus = Book.UserDefinedItem.BookStatus; + var saveStatus = bookStatus == LiberatedStatus.PartialDownload ? LiberatedStatus.NotLiberated : bookStatus; + Book.UserDefinedItem.BookStatus = saveStatus; + + LibraryCommands.UpdateUserDefinedItem(Book); + + Book.UserDefinedItem.BookStatus = bookStatus; } #endregion @@ -144,7 +173,7 @@ namespace LibationWinForms public string Description { get; } public string DisplayTags { - get=> Book.UserDefinedItem.Tags; + get => string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated); set => Book.UserDefinedItem.Tags = value; } public (LiberatedStatus BookStatus, LiberatedStatus? PdfStatus) Liberate @@ -267,5 +296,4 @@ namespace LibationWinForms FileManager.PictureStorage.PictureCached -= PictureStorage_PictureCached; } } - } diff --git a/LibationWinForms/ProductsGrid.cs b/LibationWinForms/ProductsGrid.cs index 043c0a1e..b6c6b5b0 100644 --- a/LibationWinForms/ProductsGrid.cs +++ b/LibationWinForms/ProductsGrid.cs @@ -95,9 +95,13 @@ namespace LibationWinForms if (bookDetailsForm.ShowDialog() != DialogResult.OK) return; + liveGridEntry.BeginEdit(); + liveGridEntry.DisplayTags = bookDetailsForm.NewTags; liveGridEntry.Liberate = (bookDetailsForm.BookLiberatedStatus, bookDetailsForm.PdfLiberatedStatus); + liveGridEntry.EndEdit(); + BackupCountsChanged?.Invoke(this, EventArgs.Empty); } From b1de10a71a15e70c597feac8d2940fe58964f416 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sun, 22 Aug 2021 13:29:01 -0600 Subject: [PATCH 13/13] Fix filtering. --- LibationWinForms/GridEntry.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LibationWinForms/GridEntry.cs b/LibationWinForms/GridEntry.cs index 3cc8922c..fb716e31 100644 --- a/LibationWinForms/GridEntry.cs +++ b/LibationWinForms/GridEntry.cs @@ -113,8 +113,6 @@ namespace LibationWinForms if (!suspendCommit) Commit(); - - Refilter?.Invoke(); } private bool suspendCommit = false; @@ -142,6 +140,8 @@ namespace LibationWinForms LibraryCommands.UpdateUserDefinedItem(Book); Book.UserDefinedItem.BookStatus = bookStatus; + + Refilter?.Invoke(); } #endregion