diff --git a/Source/AppScaffolding/AppScaffolding.csproj b/Source/AppScaffolding/AppScaffolding.csproj index c4ee9183..ad40a990 100644 --- a/Source/AppScaffolding/AppScaffolding.csproj +++ b/Source/AppScaffolding/AppScaffolding.csproj @@ -3,7 +3,7 @@ net6.0-windows - 7.4.0.1 + 7.5.0.1 diff --git a/Source/ApplicationServices/LibraryCommands.cs b/Source/ApplicationServices/LibraryCommands.cs index 8c181354..773a3567 100644 --- a/Source/ApplicationServices/LibraryCommands.cs +++ b/Source/ApplicationServices/LibraryCommands.cs @@ -313,7 +313,7 @@ namespace ApplicationServices // must be here instead of in db layer due to AaxcExists public static LiberatedStatus Liberated_Status(Book book) - => book.Audio_Exists ? book.UserDefinedItem.BookStatus + => book.Audio_Exists() ? book.UserDefinedItem.BookStatus : AudibleFileStorage.AaxcExists(book.AudibleProductId) ? LiberatedStatus.PartialDownload : LiberatedStatus.NotLiberated; @@ -340,7 +340,7 @@ namespace ApplicationServices var boolResults = libraryBooks .AsParallel() - .Where(lb => lb.Book.HasPdf) + .Where(lb => lb.Book.HasPdf()) .Select(lb => Pdf_Status(lb.Book)) .ToList(); var pdfsDownloaded = boolResults.Count(r => r == LiberatedStatus.Liberated); diff --git a/Source/ApplicationServices/LibraryExporter.cs b/Source/ApplicationServices/LibraryExporter.cs index 4508d7ed..3f948e66 100644 --- a/Source/ApplicationServices/LibraryExporter.cs +++ b/Source/ApplicationServices/LibraryExporter.cs @@ -111,13 +111,13 @@ namespace ApplicationServices AudibleProductId = a.Book.AudibleProductId, Locale = a.Book.Locale, Title = a.Book.Title, - AuthorNames = a.Book.AuthorNames, - NarratorNames = a.Book.NarratorNames, + AuthorNames = a.Book.AuthorNames(), + NarratorNames = a.Book.NarratorNames(), LengthInMinutes = a.Book.LengthInMinutes, Description = a.Book.Description, Publisher = a.Book.Publisher, - HasPdf = a.Book.HasPdf, - SeriesNames = a.Book.SeriesNames, + HasPdf = a.Book.HasPdf(), + SeriesNames = a.Book.SeriesNames(), SeriesOrder = a.Book.SeriesLink.Any() ? a.Book.SeriesLink?.Select(sl => $"{sl.Order} : {sl.Series.Name}").Aggregate((a, b) => $"{a}, {b}") : "", CommunityRatingOverall = a.Book.Rating?.OverallRating, CommunityRatingPerformance = a.Book.Rating?.PerformanceRating, @@ -125,7 +125,7 @@ namespace ApplicationServices PictureId = a.Book.PictureId, IsAbridged = a.Book.IsAbridged, DatePublished = a.Book.DatePublished, - CategoriesNames = a.Book.CategoriesNames.Any() ? a.Book.CategoriesNames.Aggregate((a, b) => $"{a}, {b}") : "", + CategoriesNames = a.Book.CategoriesNames().Any() ? a.Book.CategoriesNames().Aggregate((a, b) => $"{a}, {b}") : "", MyRatingOverall = a.Book.UserDefinedItem.Rating.OverallRating, MyRatingPerformance = a.Book.UserDefinedItem.Rating.PerformanceRating, MyRatingStory = a.Book.UserDefinedItem.Rating.StoryRating, diff --git a/Source/DataLayer/EfClasses/Book.cs b/Source/DataLayer/EfClasses/Book.cs index 63d3e7c3..804df40a 100644 --- a/Source/DataLayer/EfClasses/Book.cs +++ b/Source/DataLayer/EfClasses/Book.cs @@ -43,27 +43,10 @@ namespace DataLayer // non-null. use "empty pattern" internal int CategoryId { get; private set; } public Category Category { get; private set; } - public string[] CategoriesNames - => Category is null ? new string[0] - : Category.ParentCategory is null ? new[] { Category.Name } - : new[] { Category.ParentCategory.Name, Category.Name }; - public string[] CategoriesIds - => Category is null ? null - : Category.ParentCategory is null ? new[] { Category.AudibleCategoryId } - : new[] { Category.ParentCategory.AudibleCategoryId, Category.AudibleCategoryId }; - - public string TitleSortable => Formatters.GetSortName(Title); - public string SeriesSortable => Formatters.GetSortName(SeriesNames); // is owned, not optional 1:1 public UserDefinedItem UserDefinedItem { get; private set; } - // UserDefinedItem convenience properties - /// True if IsLiberated or Error. False if NotLiberated - public bool Audio_Exists => UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated; - /// True if exists and IsLiberated. Else false - public bool PDF_Exists => UserDefinedItem.PdfStatus == LiberatedStatus.Liberated; - // is owned, not optional 1:1 /// The product's aggregate community rating public Rating Rating { get; private set; } = new Rating(0, 0, 0); @@ -125,11 +108,7 @@ namespace DataLayer .ToList(); public IEnumerable Authors => getContributions(Role.Author).Select(bc => bc.Contributor).ToList(); - public string AuthorNames => string.Join(", ", Authors.Select(a => a.Name)); - public IEnumerable Narrators => getContributions(Role.Narrator).Select(bc => bc.Contributor).ToList(); - public string NarratorNames => string.Join(", ", Narrators.Select(n => n.Name)); - public string Publisher => getContributions(Role.Publisher).SingleOrDefault()?.Contributor.Name; public void ReplaceAuthors(IEnumerable authors, DbContext context = null) @@ -185,30 +164,6 @@ namespace DataLayer #region series private HashSet _seriesLink; public IEnumerable SeriesLink => _seriesLink?.ToList(); - public string SeriesNames - { - get - { - if (_seriesLink is null) - return ""; - - // first: alphabetical by name - var withNames = _seriesLink - .Where(s => !string.IsNullOrWhiteSpace(s.Series.Name)) - .Select(s => s.Series.Name) - .OrderBy(a => a) - .ToList(); - // then un-named are alpha by series id - var nullNames = _seriesLink - .Where(s => string.IsNullOrWhiteSpace(s.Series.Name)) - .Select(s => s.Series.AudibleSeriesId) - .OrderBy(a => a) - .ToList(); - - var all = withNames.Union(nullNames).ToList(); - return string.Join(", ", all); - } - } public void UpsertSeries(Series series, string order, DbContext context = null) { @@ -230,7 +185,6 @@ namespace DataLayer #region supplements private HashSet _supplements; public IEnumerable Supplements => _supplements?.ToList(); - public bool HasPdf => Supplements.Any(); public void AddSupplementDownloadUrl(string url) { diff --git a/Source/DataLayer/EfClasses/Rating.cs b/Source/DataLayer/EfClasses/Rating.cs index 25ee3e28..761633be 100644 --- a/Source/DataLayer/EfClasses/Rating.cs +++ b/Source/DataLayer/EfClasses/Rating.cs @@ -38,41 +38,6 @@ namespace DataLayer yield return StoryRating; } - public float FirstScore - => OverallRating > 0 ? OverallRating - : PerformanceRating > 0 ? PerformanceRating - : StoryRating; - - /// character: ★ - const char STAR = '\u2605'; - /// character: ½ - const char HALF = '\u00BD'; - string getStars(float score) - { - var fullStars = (int)Math.Floor(score); - - var starString = "".PadLeft(fullStars, STAR); - - if (score - fullStars == 0.5f) - starString += HALF; - - return starString; - } - - public string ToStarString() - { - var items = new List(); - - if (OverallRating > 0) - items.Add($"Overall: {getStars(OverallRating)}"); - if (PerformanceRating > 0) - items.Add($"Perform: {getStars(PerformanceRating)}"); - if (StoryRating > 0) - items.Add($"Story: {getStars(StoryRating)}"); - - return string.Join("\r\n", items); - } - public override string ToString() => $"Overall={OverallRating} Perf={PerformanceRating} Story={StoryRating}"; } } diff --git a/Source/DataLayer/EntityExtensions.cs b/Source/DataLayer/EntityExtensions.cs new file mode 100644 index 00000000..ef18a232 --- /dev/null +++ b/Source/DataLayer/EntityExtensions.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DataLayer +{ + public static class EntityExtensions + { + public static string TitleSortable(this Book book) => Formatters.GetSortName(book.Title); + + public static string AuthorNames(this Book book) => string.Join(", ", book.Authors.Select(a => a.Name)); + public static string NarratorNames(this Book book) => string.Join(", ", book.Narrators.Select(n => n.Name)); + + /// True if IsLiberated or Error. False if NotLiberated + public static bool Audio_Exists(this Book book) => book.UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated; + /// True if exists and IsLiberated. Else false + public static bool PDF_Exists(this Book book) => book.UserDefinedItem.PdfStatus == LiberatedStatus.Liberated; + + public static string SeriesSortable(this Book book) => Formatters.GetSortName(book.SeriesNames()); + public static bool HasPdf(this Book book) => book.Supplements.Any(); + public static string SeriesNames(this Book book) + { + if (book.SeriesLink is null) + return ""; + + // first: alphabetical by name + var withNames = book.SeriesLink + .Where(s => !string.IsNullOrWhiteSpace(s.Series.Name)) + .Select(s => s.Series.Name) + .OrderBy(a => a) + .ToList(); + // then un-named are alpha by series id + var nullNames = book.SeriesLink + .Where(s => string.IsNullOrWhiteSpace(s.Series.Name)) + .Select(s => s.Series.AudibleSeriesId) + .OrderBy(a => a) + .ToList(); + + var all = withNames.Union(nullNames).ToList(); + return string.Join(", ", all); + } + public static string[] CategoriesNames(this Book book) + => book.Category is null ? new string[0] + : book.Category.ParentCategory is null ? new[] { book.Category.Name } + : new[] { book.Category.ParentCategory.Name, book.Category.Name }; + public static string[] CategoriesIds(this Book book) + => book.Category is null ? null + : book.Category.ParentCategory is null ? new[] { book.Category.AudibleCategoryId } + : new[] { book.Category.ParentCategory.AudibleCategoryId, book.Category.AudibleCategoryId }; + + public static string AggregateTitles(this IEnumerable libraryBooks, int max = 5) + { + if (libraryBooks is null || !libraryBooks.Any()) + return ""; + + max = Math.Max(max, 1); + + var titles = libraryBooks.Select(lb => "- " + lb.Book.Title).ToList(); + var titlesAgg = titles.Take(max).Aggregate((a, b) => $"{a}\r\n{b}"); + if (titles.Count == max + 1) + titlesAgg += $"\r\n\r\nand 1 other"; + else if (titles.Count > max + 1) + titlesAgg += $"\r\n\r\nand {titles.Count - max } others"; + return titlesAgg; + } + + public static float FirstScore(this Rating rating) + => rating.OverallRating > 0 ? rating.OverallRating + : rating.PerformanceRating > 0 ? rating.PerformanceRating + : rating.StoryRating; + public static string ToStarString(this Rating rating) + { + var items = new List(); + + if (rating.OverallRating > 0) + items.Add($"Overall: {getStars(rating.OverallRating)}"); + if (rating.PerformanceRating > 0) + items.Add($"Perform: {getStars(rating.PerformanceRating)}"); + if (rating.StoryRating > 0) + items.Add($"Story: {getStars(rating.StoryRating)}"); + + return string.Join("\r\n", items); + } + /// character: ★ + const char STAR = '\u2605'; + /// character: ½ + const char HALF = '\u00BD'; + private static string getStars(float score) + { + var fullStars = (int)Math.Floor(score); + + var starString = new string(STAR, fullStars); + + if (score - fullStars >= 0.25f) + starString += HALF; + + return starString; + } + } +} diff --git a/Source/FileLiberator/DownloadDecryptBook.cs b/Source/FileLiberator/DownloadDecryptBook.cs index 7a9136eb..ef54d81d 100644 --- a/Source/FileLiberator/DownloadDecryptBook.cs +++ b/Source/FileLiberator/DownloadDecryptBook.cs @@ -18,7 +18,7 @@ namespace FileLiberator public override string Name => "Download & Decrypt"; private AudiobookDownloadBase abDownloader; - public override bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists; + public override bool Validate(LibraryBook libraryBook) => !libraryBook.Book.Audio_Exists(); public override void Cancel() => abDownloader?.Cancel(); @@ -43,7 +43,7 @@ namespace FileLiberator try { - if (libraryBook.Book.Audio_Exists) + if (libraryBook.Book.Audio_Exists()) return new StatusHandler { "Cannot find decrypt. Final audio file already exists" }; bool success = false; @@ -120,7 +120,7 @@ namespace FileLiberator : new AaxcDownloadSingleConverter(outFileName, cacheDir, audiobookDlLic); if (config.AllowLibationFixup) - converter.RetrievedMetadata += (_, tags) => tags.Generes = string.Join(", ", libraryBook.Book.CategoriesNames); + converter.RetrievedMetadata += (_, tags) => tags.Generes = string.Join(", ", libraryBook.Book.CategoriesNames()); abDownloader = converter; } diff --git a/Source/FileLiberator/DownloadPdf.cs b/Source/FileLiberator/DownloadPdf.cs index 2ebd155a..5bc61f7c 100644 --- a/Source/FileLiberator/DownloadPdf.cs +++ b/Source/FileLiberator/DownloadPdf.cs @@ -17,7 +17,7 @@ namespace FileLiberator public override string Name => "Download Pdf"; public override bool Validate(LibraryBook libraryBook) => !string.IsNullOrWhiteSpace(getdownloadUrl(libraryBook)) - && !libraryBook.Book.PDF_Exists; + && !libraryBook.Book.PDF_Exists(); public override async Task ProcessAsync(LibraryBook libraryBook) { diff --git a/Source/LibationSearchEngine/SearchEngine.cs b/Source/LibationSearchEngine/SearchEngine.cs index ac365a35..f10ea71c 100644 --- a/Source/LibationSearchEngine/SearchEngine.cs +++ b/Source/LibationSearchEngine/SearchEngine.cs @@ -30,8 +30,14 @@ namespace LibationSearchEngine // the workaround which allows displaying all books when query is empty public const string ALL_QUERY = "*:*"; - #region index rules - private static ReadOnlyDictionary> idIndexRules { get; } + #region index rules + // common fields used in the "all" default search field + public const string ALL_AUDIBLE_PRODUCT_ID = nameof(Book.AudibleProductId); + public const string ALL_TITLE = nameof(Book.Title); + public const string ALL_AUTHOR_NAMES = "AuthorNames"; + public const string ALL_NARRATOR_NAMES = "NarratorNames"; + + private static ReadOnlyDictionary> idIndexRules { get; } = new ReadOnlyDictionary>( new Dictionary> { @@ -50,15 +56,15 @@ namespace LibationSearchEngine [nameof(Book.DatePublished)] = lb => lb.Book.DatePublished?.ToLuceneString(), [nameof(Book.Title)] = lb => lb.Book.Title, - [nameof(Book.AuthorNames)] = lb => lb.Book.AuthorNames, - ["Author"] = lb => lb.Book.AuthorNames, - ["Authors"] = lb => lb.Book.AuthorNames, - [nameof(Book.NarratorNames)] = lb => lb.Book.NarratorNames, - ["Narrator"] = lb => lb.Book.NarratorNames, - ["Narrators"] = lb => lb.Book.NarratorNames, + [ALL_AUTHOR_NAMES] = lb => lb.Book.AuthorNames(), + ["Author"] = lb => lb.Book.AuthorNames(), + ["Authors"] = lb => lb.Book.AuthorNames(), + [ALL_NARRATOR_NAMES] = lb => lb.Book.NarratorNames(), + ["Narrator"] = lb => lb.Book.NarratorNames(), + ["Narrators"] = lb => lb.Book.NarratorNames(), [nameof(Book.Publisher)] = lb => lb.Book.Publisher, - [nameof(Book.SeriesNames)] = lb => string.Join( + ["SeriesNames"] = lb => string.Join( ", ", lb.Book.SeriesLink .Where(s => !string.IsNullOrWhiteSpace(s.Series.Name)) @@ -70,11 +76,11 @@ namespace LibationSearchEngine .Select(s => s.Series.AudibleSeriesId)), ["SeriesId"] = lb => string.Join(", ", lb.Book.SeriesLink.Select(s => s.Series.AudibleSeriesId)), - [nameof(Book.CategoriesNames)] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds), - [nameof(Book.Category)] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds), - ["Categories"] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds), - ["CategoriesId"] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds), - ["CategoryId"] = lb => lb.Book.CategoriesIds is null ? null : string.Join(", ", lb.Book.CategoriesIds), + ["CategoriesNames"] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()), + [nameof(Book.Category)] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()), + ["Categories"] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()), + ["CategoriesId"] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()), + ["CategoryId"] = lb => lb.Book.CategoriesIds() is null ? null : string.Join(", ", lb.Book.CategoriesIds()), [TAGS.FirstCharToUpper()] = lb => lb.Book.UserDefinedItem.Tags, @@ -107,14 +113,14 @@ namespace LibationSearchEngine = new ReadOnlyDictionary>( new Dictionary> { - ["HasDownloads"] = lb => lb.Book.HasPdf, - ["HasDownload"] = lb => lb.Book.HasPdf, - ["Downloads"] = lb => lb.Book.HasPdf, - ["Download"] = lb => lb.Book.HasPdf, - ["HasPDFs"] = lb => lb.Book.HasPdf, - ["HasPDF"] = lb => lb.Book.HasPdf, - ["PDFs"] = lb => lb.Book.HasPdf, - ["PDF"] = lb => lb.Book.HasPdf, + ["HasDownloads"] = lb => lb.Book.HasPdf(), + ["HasDownload"] = lb => lb.Book.HasPdf(), + ["Downloads"] = lb => lb.Book.HasPdf(), + ["Download"] = lb => lb.Book.HasPdf(), + ["HasPDFs"] = lb => lb.Book.HasPdf(), + ["HasPDF"] = lb => lb.Book.HasPdf(), + ["PDFs"] = lb => lb.Book.HasPdf(), + ["PDF"] = lb => lb.Book.HasPdf(), ["IsRated"] = lb => lb.Book.UserDefinedItem.Rating.OverallRating > 0f, ["Rated"] = lb => lb.Book.UserDefinedItem.Rating.OverallRating > 0f, @@ -151,10 +157,10 @@ namespace LibationSearchEngine private static IEnumerable> allFieldIndexRules { get; } = new List> { - idIndexRules[nameof(Book.AudibleProductId)], - stringIndexRules[nameof(Book.Title)], - stringIndexRules[nameof(Book.AuthorNames)], - stringIndexRules[nameof(Book.NarratorNames)] + idIndexRules[ALL_AUDIBLE_PRODUCT_ID], + stringIndexRules[ALL_TITLE], + stringIndexRules[ALL_AUTHOR_NAMES], + stringIndexRules[ALL_NARRATOR_NAMES] }; #endregion diff --git a/Source/LibationWinForms/BookLiberation/AudioDecodeForm.cs b/Source/LibationWinForms/BookLiberation/AudioDecodeForm.cs index 56775e8e..73d43562 100644 --- a/Source/LibationWinForms/BookLiberation/AudioDecodeForm.cs +++ b/Source/LibationWinForms/BookLiberation/AudioDecodeForm.cs @@ -26,8 +26,8 @@ namespace LibationWinForms.BookLiberation //Set default values from library AudioDecodable_TitleDiscovered(sender, libraryBook.Book.Title); - AudioDecodable_AuthorsDiscovered(sender, libraryBook.Book.AuthorNames); - AudioDecodable_NarratorsDiscovered(sender, libraryBook.Book.NarratorNames); + AudioDecodable_AuthorsDiscovered(sender, libraryBook.Book.AuthorNames()); + AudioDecodable_NarratorsDiscovered(sender, libraryBook.Book.NarratorNames()); AudioDecodable_CoverImageDiscovered(sender, PictureStorage.GetPicture( new PictureDefinition( diff --git a/Source/LibationWinForms/BookLiberation/ProcessorAutomationController.cs b/Source/LibationWinForms/BookLiberation/ProcessorAutomationController.cs index 78c7d4c4..38a185c3 100644 --- a/Source/LibationWinForms/BookLiberation/ProcessorAutomationController.cs +++ b/Source/LibationWinForms/BookLiberation/ProcessorAutomationController.cs @@ -241,8 +241,8 @@ namespace LibationWinForms.BookLiberation details = $@" Title: {libraryBook.Book.Title} ID: {libraryBook.Book.AudibleProductId} - Author: {trunc(libraryBook.Book.AuthorNames)} - Narr: {trunc(libraryBook.Book.NarratorNames)}"; + Author: {trunc(libraryBook.Book.AuthorNames())} + Narr: {trunc(libraryBook.Book.NarratorNames())}"; } catch { diff --git a/Source/LibationWinForms/Dialogs/AccountsDialog.cs b/Source/LibationWinForms/Dialogs/AccountsDialog.cs index afcbbf79..7670c0dd 100644 --- a/Source/LibationWinForms/Dialogs/AccountsDialog.cs +++ b/Source/LibationWinForms/Dialogs/AccountsDialog.cs @@ -128,7 +128,7 @@ namespace LibationWinForms.Dialogs } catch (Exception ex) { - MessageBoxAlertAdmin.Show("Error attempting to save accounts", "Error saving accounts", ex); + MessageBoxLib.ShowAdminAlert("Error attempting to save accounts", "Error saving accounts", ex); } } diff --git a/Source/LibationWinForms/Dialogs/BookDetailsDialog.cs b/Source/LibationWinForms/Dialogs/BookDetailsDialog.cs index 89184b00..fdbc7ac7 100644 --- a/Source/LibationWinForms/Dialogs/BookDetailsDialog.cs +++ b/Source/LibationWinForms/Dialogs/BookDetailsDialog.cs @@ -46,15 +46,16 @@ namespace LibationWinForms.Dialogs var t = @$" Title: {Book.Title} -Author(s): {Book.AuthorNames} -Narrator(s): {Book.NarratorNames} +Author(s): {Book.AuthorNames()} +Narrator(s): {Book.NarratorNames()} Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")} -Category: {string.Join(" > ", Book.CategoriesNames)} +Category: {string.Join(" > ", Book.CategoriesNames())} Purchase Date: {_libraryBook.DateAdded.ToString("d")} ".Trim(); - if (!string.IsNullOrWhiteSpace(Book.SeriesNames)) - t += $"\r\nSeries: {Book.SeriesNames}"; + var seriesNames = Book.SeriesNames(); + if (!string.IsNullOrWhiteSpace(seriesNames)) + t += $"\r\nSeries: {seriesNames}"; var bookRating = Book.Rating?.ToStarString(); if (!string.IsNullOrWhiteSpace(bookRating)) diff --git a/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs b/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs index 734a49d4..693072f7 100644 --- a/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs +++ b/Source/LibationWinForms/Dialogs/EditTemplateDialog.cs @@ -46,7 +46,7 @@ namespace LibationWinForms.Dialogs if (template is null) { - MessageBoxAlertAdmin.Show($"Programming error. {nameof(EditTemplateDialog)} was not created correctly", "Edit template error", new NullReferenceException($"{nameof(template)} is null")); + MessageBoxLib.ShowAdminAlert($"Programming error. {nameof(EditTemplateDialog)} was not created correctly", "Edit template error", new NullReferenceException($"{nameof(template)} is null")); return; } diff --git a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs index 1f7e18d9..2dac8bd9 100644 --- a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs +++ b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs @@ -77,7 +77,7 @@ namespace LibationWinForms.Dialogs } catch (Exception ex) { - MessageBoxAlertAdmin.Show( + MessageBoxLib.ShowAdminAlert( "Error scanning library. You may still manually select books to remove from Libation's library.", "Error scanning library", ex); @@ -95,34 +95,22 @@ namespace LibationWinForms.Dialogs if (selectedBooks.Count == 0) return; - var titles = selectedBooks.Select(rge => "- " + rge.Title).ToList(); - var titlesAgg = titles.Take(5).Aggregate((a, b) => $"{a}\r\n{b}"); - if (titles.Count == 6) - titlesAgg += $"\r\n\r\nand 1 other"; - else if (titles.Count > 6) - titlesAgg += $"\r\n\r\nand {titles.Count - 5} others"; + var libraryBooks = selectedBooks.Select(rge => rge.LibraryBook).ToList(); + var result = MessageBoxLib.ShowConfirmationDialog( + libraryBooks, + $"Are you sure you want to remove {0} from Libation's library?", + "Remove books from Libation?"); - string thisThese = selectedBooks.Count > 1 ? "these" : "this"; - string bookBooks = selectedBooks.Count > 1 ? "books" : "book"; + if (result != DialogResult.Yes) + return; - var result = MessageBox.Show( - this, - $"Are you sure you want to remove {thisThese} {selectedBooks.Count} {bookBooks} from Libation's library?\r\n\r\n{titlesAgg}", - "Remove books from Libation?", - MessageBoxButtons.YesNo, - MessageBoxIcon.Question, - MessageBoxDefaultButton.Button1); + var idsToRemove = libraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); + var removeLibraryBooks = await LibraryCommands.RemoveBooksAsync(idsToRemove); - if (result == DialogResult.Yes) - { - var idsToRemove = selectedBooks.Select(rge => rge.AudibleProductId).ToList(); - var removeLibraryBooks = await LibraryCommands.RemoveBooksAsync(idsToRemove); + foreach (var rEntry in selectedBooks) + _removableGridEntries.Remove(rEntry); - foreach (var rEntry in selectedBooks) - _removableGridEntries.Remove(rEntry); - - UpdateSelection(); - } + UpdateSelection(); } private void UpdateSelection() diff --git a/Source/LibationWinForms/Dialogs/SettingsDialog.cs b/Source/LibationWinForms/Dialogs/SettingsDialog.cs index 0741a0f3..cc84ecc2 100644 --- a/Source/LibationWinForms/Dialogs/SettingsDialog.cs +++ b/Source/LibationWinForms/Dialogs/SettingsDialog.cs @@ -181,7 +181,7 @@ namespace LibationWinForms.Dialogs // only warn if changed during this time. don't want to warn every time user happens to change settings while level is verbose if (logLevelOld != logLevelNew) - MessageBoxVerboseLoggingWarning.ShowIfTrue(); + MessageBoxLib.VerboseLoggingWarning_ShowIfTrue(); } config.AllowLibationFixup = allowLibationFixupCbox.Checked; diff --git a/Source/LibationWinForms/MessageBoxAlertAdmin.cs b/Source/LibationWinForms/MessageBoxAlertAdmin.cs deleted file mode 100644 index 3d68836f..00000000 --- a/Source/LibationWinForms/MessageBoxAlertAdmin.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using LibationWinForms.Dialogs; - -namespace LibationWinForms -{ - public static class MessageBoxAlertAdmin - { - /// - /// Logs error. Displays a message box dialog with specified text and caption. - /// - /// The text to display in the message box. - /// The text to display in the title bar of the message box. - /// Exception to log - /// One of the System.Windows.Forms.DialogResult values. - public static System.Windows.Forms.DialogResult Show(string text, string caption, Exception exception) - { - try - { - Serilog.Log.Logger.Error(exception, "Alert admin error: {@DebugText}", new { text, caption }); - } - catch { } - - using var form = new MessageBoxAlertAdminDialog(text, caption, exception); - return form.ShowDialog(); - } - } -} diff --git a/Source/LibationWinForms/MessageBoxWarnIfVerboseLogging.cs b/Source/LibationWinForms/MessageBoxWarnIfVerboseLogging.cs deleted file mode 100644 index 0f47ef3c..00000000 --- a/Source/LibationWinForms/MessageBoxWarnIfVerboseLogging.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Linq; -using Dinah.Core.Logging; -using Serilog; -using System.Windows.Forms; - -namespace LibationWinForms -{ - public static class MessageBoxVerboseLoggingWarning - { - public static void ShowIfTrue() - { - // when turning on debug (and especially Verbose) to share logs, some privacy settings may not be obscured - if (Log.Logger.IsVerboseEnabled()) - MessageBox.Show(@" -Warning: verbose logging is enabled. - -This should be used for debugging only. It creates many -more logs and debug files, neither of which are as -strictly anonymous. - -When you are finished debugging, it's highly recommended -to set your debug MinimumLevel to Information and restart -Libation. -".Trim(), "Verbose logging enabled", MessageBoxButtons.OK, MessageBoxIcon.Warning); - } - } -} diff --git a/Source/LibationWinForms/Program.cs b/Source/LibationWinForms/Program.cs index 30343ac0..f02df0ba 100644 --- a/Source/LibationWinForms/Program.cs +++ b/Source/LibationWinForms/Program.cs @@ -49,7 +49,7 @@ namespace LibationWinForms // migrations which require Forms or are long-running RunWindowsOnlyMigrations(config); - MessageBoxVerboseLoggingWarning.ShowIfTrue(); + MessageBoxLib.VerboseLoggingWarning_ShowIfTrue(); #if !DEBUG checkForUpdate(); @@ -63,7 +63,7 @@ namespace LibationWinForms var body = "An unrecoverable error occurred. Since this error happened before logging could be initialized, this error can not be written to the log file."; try { - MessageBoxAlertAdmin.Show(body, title, ex); + MessageBoxLib.ShowAdminAlert(body, title, ex); } catch { @@ -178,7 +178,7 @@ namespace LibationWinForms } catch (Exception ex) { - MessageBoxAlertAdmin.Show("Error checking for update", "Error checking for update", ex); + MessageBoxLib.ShowAdminAlert("Error checking for update", "Error checking for update", ex); return; } @@ -203,7 +203,7 @@ namespace LibationWinForms } catch (Exception ex) { - MessageBoxAlertAdmin.Show("Error downloading update", "Error downloading update", ex); + MessageBoxLib.ShowAdminAlert("Error downloading update", "Error downloading update", ex); } } } diff --git a/Source/LibationWinForms/grid/GridEntry.cs b/Source/LibationWinForms/grid/GridEntry.cs index aee51d4c..0d20ef1f 100644 --- a/Source/LibationWinForms/grid/GridEntry.cs +++ b/Source/LibationWinForms/grid/GridEntry.cs @@ -128,14 +128,14 @@ namespace LibationWinForms // Immutable properties { Title = Book.Title; - Series = Book.SeriesNames; + Series = Book.SeriesNames(); Length = Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min"; MyRating = Book.UserDefinedItem.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace(""); PurchaseDate = libraryBook.DateAdded.ToString("d"); ProductRating = Book.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace(""); - Authors = Book.AuthorNames; - Narrators = Book.NarratorNames; - Category = string.Join(" > ", Book.CategoriesNames); + Authors = Book.AuthorNames(); + Narrators = Book.NarratorNames(); + Category = string.Join(" > ", Book.CategoriesNames()); Misc = GetMiscDisplay(libraryBook); LongDescription = GetDescriptionDisplay(Book); Description = TrimTextToWord(LongDescription, 62); @@ -232,12 +232,12 @@ namespace LibationWinForms /// private Dictionary> CreateMemberValueDictionary() => new() { - { nameof(Title), () => Book.TitleSortable }, - { nameof(Series), () => Book.SeriesSortable }, + { nameof(Title), () => Book.TitleSortable() }, + { nameof(Series), () => Book.SeriesSortable() }, { nameof(Length), () => Book.LengthInMinutes }, - { nameof(MyRating), () => Book.UserDefinedItem.Rating.FirstScore }, + { nameof(MyRating), () => Book.UserDefinedItem.Rating.FirstScore() }, { nameof(PurchaseDate), () => LibraryBook.DateAdded }, - { nameof(ProductRating), () => Book.Rating.FirstScore }, + { nameof(ProductRating), () => Book.Rating.FirstScore() }, { nameof(Authors), () => Authors }, { nameof(Narrators), () => Narrators }, { nameof(Description), () => Description }, @@ -292,7 +292,7 @@ namespace LibationWinForms details.Add($"Account: {locale} - {acct}"); - if (libraryBook.Book.HasPdf) + if (libraryBook.Book.HasPdf()) details.Add("Has PDF"); if (libraryBook.Book.IsAbridged) details.Add("Abridged"); diff --git a/Source/LibationWinForms/grid/ProductsGrid.cs b/Source/LibationWinForms/grid/ProductsGrid.cs index 2ab0f271..ba429dfc 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.cs +++ b/Source/LibationWinForms/grid/ProductsGrid.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using ApplicationServices; +using DataLayer; using Dinah.Core; using Dinah.Core.DataBinding; using Dinah.Core.Threading; @@ -130,7 +131,7 @@ namespace LibationWinForms var libraryBook = liveGridEntry.LibraryBook; // liberated: open explorer to file - if (libraryBook.Book.Audio_Exists) + if (libraryBook.Book.Audio_Exists()) { var filePath = AudibleFileStorage.Audio.GetPath(libraryBook.Book.AudibleProductId); if (!Go.To.File(filePath))