diff --git a/Source/AppScaffolding/AppScaffolding.csproj b/Source/AppScaffolding/AppScaffolding.csproj index 2c1c3052..9e6412c7 100644 --- a/Source/AppScaffolding/AppScaffolding.csproj +++ b/Source/AppScaffolding/AppScaffolding.csproj @@ -2,7 +2,7 @@ net7.0 - 8.5.1.1 + 8.6.0.1 diff --git a/Source/ApplicationServices/BulkSetDownloadStatus.cs b/Source/ApplicationServices/BulkSetDownloadStatus.cs new file mode 100644 index 00000000..f0f835d3 --- /dev/null +++ b/Source/ApplicationServices/BulkSetDownloadStatus.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using DataLayer; +using Dinah.Core; +using LibationFileManager; + +namespace ApplicationServices +{ + public class BulkSetDownloadStatus + { + private List<(string message, LiberatedStatus newStatus, IEnumerable Books)> actionSets { get; } = new(); + + public int Count => actionSets.Count; + + public IEnumerable Messages => actionSets.Select(a => a.message); + public string AggregateMessage => $"Are you sure you want to set {Messages.Aggregate((a, b) => $"{a} and {b}")}?"; + + private List _libraryBooks; + private bool _setDownloaded; + private bool _setNotDownloaded; + + public BulkSetDownloadStatus(List libraryBooks, bool setDownloaded, bool setNotDownloaded) + { + _libraryBooks = libraryBooks; + _setDownloaded = setDownloaded; + _setNotDownloaded = setNotDownloaded; + } + + public int Discover() + { + var bookExistsList = _libraryBooks + .Select(libraryBook => new + { + libraryBook.Book, + FileExists = AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId) is not null + }) + .ToList(); + + if (_setDownloaded) + { + var books2change = bookExistsList + .Where(a => a.FileExists && a.Book.UserDefinedItem.BookStatus != LiberatedStatus.Liberated) + .Select(a => a.Book) + .ToList(); + + if (books2change.Any()) + actionSets.Add(( + $"{"book".PluralizeWithCount(books2change.Count)} to 'Downloaded'", + LiberatedStatus.Liberated, + books2change)); + } + + if (_setNotDownloaded) + { + var books2change = bookExistsList + .Where(a => !a.FileExists && a.Book.UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated) + .Select(a => a.Book) + .ToList(); + + if (books2change.Any()) + actionSets.Add(( + $"{"book".PluralizeWithCount(books2change.Count)} to 'Not Downloaded'", + LiberatedStatus.NotLiberated, + books2change)); + } + + return Count; + } + + public void Execute() + { + foreach (var a in actionSets) + a.Books.UpdateBookStatus(a.newStatus); + } + } +} diff --git a/Source/ApplicationServices/LibraryCommands.cs b/Source/ApplicationServices/LibraryCommands.cs index 56c64e2d..cd72b61c 100644 --- a/Source/ApplicationServices/LibraryCommands.cs +++ b/Source/ApplicationServices/LibraryCommands.cs @@ -9,6 +9,7 @@ using Dinah.Core; using DtoImporterService; using LibationFileManager; using Serilog; +using static System.Reflection.Metadata.BlobBuilder; using static DtoImporterService.PerfLogger; namespace ApplicationServices @@ -366,74 +367,101 @@ namespace ApplicationServices public static event EventHandler> BookUserDefinedItemCommitted; #region Update book details - public static int UpdateBookStatus(this Book book, LiberatedStatus bookStatus) - { - book.UserDefinedItem.BookStatus = bookStatus; - return UpdateUserDefinedItem(book); - } - public static int UpdatePdfStatus(this Book book, LiberatedStatus pdfStatus) - { - book.UserDefinedItem.PdfStatus = pdfStatus; - return UpdateUserDefinedItem(book); - } - public static int UpdateBook( + public static int UpdateUserDefinedItem( this Book book, string tags = null, LiberatedStatus? bookStatus = null, LiberatedStatus? pdfStatus = null) - => UpdateBooks(tags, bookStatus, pdfStatus, book); - public static int UpdateBooks( + => new[] { book }.UpdateUserDefinedItem(tags, bookStatus, pdfStatus); + + public static int UpdateUserDefinedItem( + this IEnumerable books, string tags = null, LiberatedStatus? bookStatus = null, - LiberatedStatus? pdfStatus = null, - params Book[] books) + LiberatedStatus? pdfStatus = null) + => updateUserDefinedItem( + books, + udi => { + // blank tags are expected. null tags are not + if (tags is not null && udi.Tags != tags) + udi.Tags = tags; + + if (bookStatus is not null && udi.BookStatus != bookStatus.Value) + udi.BookStatus = bookStatus.Value; + + // even though PdfStatus is nullable, there's no case where we'd actually overwrite with null + if (pdfStatus is not null && udi.PdfStatus != pdfStatus.Value) + udi.PdfStatus = pdfStatus.Value; + }); + + public static int UpdateBookStatus(this Book book, LiberatedStatus bookStatus) + => book.UpdateUserDefinedItem(udi => udi.BookStatus = bookStatus); + public static int UpdateBookStatus(this IEnumerable books, LiberatedStatus bookStatus) + => books.UpdateUserDefinedItem(udi => udi.BookStatus = bookStatus); + public static int UpdateBookStatus(this LibraryBook libraryBook, LiberatedStatus bookStatus) + => libraryBook.UpdateUserDefinedItem(udi => udi.BookStatus = bookStatus); + public static int UpdateBookStatus(this IEnumerable libraryBooks, LiberatedStatus bookStatus) + => libraryBooks.UpdateUserDefinedItem(udi => udi.BookStatus = bookStatus); + + public static int UpdatePdfStatus(this Book book, LiberatedStatus pdfStatus) + => book.UpdateUserDefinedItem(udi => udi.PdfStatus = pdfStatus); + public static int UpdatePdfStatus(this IEnumerable books, LiberatedStatus pdfStatus) + => books.UpdateUserDefinedItem(udi => udi.PdfStatus = pdfStatus); + public static int UpdatePdfStatus(this LibraryBook libraryBook, LiberatedStatus pdfStatus) + => libraryBook.UpdateUserDefinedItem(udi => udi.PdfStatus = pdfStatus); + public static int UpdatePdfStatus(this IEnumerable libraryBooks, LiberatedStatus pdfStatus) + => libraryBooks.UpdateUserDefinedItem(udi => udi.PdfStatus = pdfStatus); + + public static int UpdateTags(this Book book, string tags) + => book.UpdateUserDefinedItem(udi => udi.Tags = tags); + public static int UpdateTags(this IEnumerable books, string tags) + => books.UpdateUserDefinedItem(udi => udi.Tags = tags); + public static int UpdateTags(this LibraryBook libraryBook, string tags) + => libraryBook.UpdateUserDefinedItem(udi => udi.Tags = tags); + public static int UpdateTags(this IEnumerable libraryBooks, string tags) + => libraryBooks.UpdateUserDefinedItem(udi => udi.Tags = tags); + + public static int UpdateUserDefinedItem(this LibraryBook libraryBook, Action action) + => libraryBook.Book.updateUserDefinedItem(action); + public static int UpdateUserDefinedItem(this IEnumerable libraryBooks, Action action) + => libraryBooks.Select(lb => lb.Book).updateUserDefinedItem(action); + + public static int UpdateUserDefinedItem(this Book book, Action action) => book.updateUserDefinedItem(action); + public static int UpdateUserDefinedItem(this IEnumerable books, Action action) => books.updateUserDefinedItem(action); + + private static int updateUserDefinedItem(this Book book, Action action) => new[] { book }.updateUserDefinedItem(action); + private static int updateUserDefinedItem(this IEnumerable books, Action action) { - foreach (var book in books) - { - // blank tags are expected. null tags are not - if (tags is not null && book.UserDefinedItem.Tags != tags) - book.UserDefinedItem.Tags = tags; + try + { + if (books is null || !books.Any()) + return 0; - if (bookStatus is not null && book.UserDefinedItem.BookStatus != bookStatus.Value) - book.UserDefinedItem.BookStatus = bookStatus.Value; - - // even though PdfStatus is nullable, there's no case where we'd actually overwrite with null - if (pdfStatus is not null && book.UserDefinedItem.PdfStatus != pdfStatus.Value) - book.UserDefinedItem.PdfStatus = pdfStatus.Value; - } - - return UpdateUserDefinedItem(books); - } - public static int UpdateUserDefinedItem(params Book[] books) => UpdateUserDefinedItem(books.ToList()); - public static int UpdateUserDefinedItem(IEnumerable books) - { - try - { - if (books is null || !books.Any()) - return 0; - - using var context = DbContexts.GetContext(); - - // Attach() NoTracking entities before SaveChanges() foreach (var book in books) - context.Attach(book.UserDefinedItem).State = Microsoft.EntityFrameworkCore.EntityState.Modified; + action?.Invoke(book.UserDefinedItem); - var qtyChanges = context.SaveChanges(); - if (qtyChanges > 0) - BookUserDefinedItemCommitted?.Invoke(null, books); + using var context = DbContexts.GetContext(); - return qtyChanges; - } - catch (Exception ex) - { - Log.Logger.Error(ex, $"Error updating {nameof(Book.UserDefinedItem)}"); - throw; - } - } - #endregion + // Attach() NoTracking entities before SaveChanges() + foreach (var book in books) + context.Attach(book.UserDefinedItem).State = Microsoft.EntityFrameworkCore.EntityState.Modified; - // must be here instead of in db layer due to AaxcExists - public static LiberatedStatus Liberated_Status(Book book) + var qtyChanges = context.SaveChanges(); + if (qtyChanges > 0) + BookUserDefinedItemCommitted?.Invoke(null, books); + + return qtyChanges; + } + catch (Exception ex) + { + Log.Logger.Error(ex, $"Error updating {nameof(Book.UserDefinedItem)}"); + throw; + } + } + #endregion + + // must be here instead of in db layer due to AaxcExists + public static LiberatedStatus Liberated_Status(Book book) => book.Audio_Exists() ? book.UserDefinedItem.BookStatus : AudibleFileStorage.AaxcExists(book.AudibleProductId) ? LiberatedStatus.PartialDownload : LiberatedStatus.NotLiberated; diff --git a/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs index 1c10c204..180c736a 100644 --- a/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs @@ -50,7 +50,7 @@ namespace LibationAvalonia.Dialogs protected override void SaveAndClose() { - LibraryBook.Book.UpdateBook(NewTags, bookStatus: BookLiberatedStatus, pdfStatus: PdfLiberatedStatus); + LibraryBook.Book.UpdateUserDefinedItem(NewTags, bookStatus: BookLiberatedStatus, pdfStatus: PdfLiberatedStatus); base.SaveAndClose(); } diff --git a/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchAutoDialog.axaml b/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchAutoDialog.axaml new file mode 100644 index 00000000..8430a7c5 --- /dev/null +++ b/Source/LibationAvalonia/Dialogs/LiberatedStatusBatchAutoDialog.axaml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + +