From c95ba0764bd748b90a21954528c92aa965a71f32 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Thu, 26 May 2022 16:11:52 -0600 Subject: [PATCH 1/7] Fix bug and add groundwork for future feature --- Source/AudibleUtilities/ApiExtended.cs | 7 ++++++- Source/LibationWinForms/GridView/GridEntry.cs | 1 + Source/LibationWinForms/GridView/ProductsGrid.cs | 6 +++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index 70d338b6..ca09a64b 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -133,6 +133,11 @@ namespace AudibleUtilities { //Get child episodes asynchronously and await all at the end getChildEpisodesTasks.Add(getChildEpisodesAsync(concurrencySemaphore, item)); + + //Add the parent to the library because it contains the series + //description, series rating, and series cover art which differ + //from the individual episodes' values. + items.Add(item); } else if (!item.IsEpisodes) items.Add(item); @@ -178,7 +183,7 @@ namespace AudibleUtilities // actual individual episode, not the parent of a series. // for now I'm keeping it inside this method since it fits the work flow, incl. importEpisodes logic if (!children.Any()) - return new List() { parent }; + return new(); foreach (var child in children) { diff --git a/Source/LibationWinForms/GridView/GridEntry.cs b/Source/LibationWinForms/GridView/GridEntry.cs index b20c1287..e6d479f2 100644 --- a/Source/LibationWinForms/GridView/GridEntry.cs +++ b/Source/LibationWinForms/GridView/GridEntry.cs @@ -109,5 +109,6 @@ namespace LibationWinForms.GridView => gridEntries.Series().FirstOrDefault(i => matchSeries.Any(s => s.Series.Name == i.Series)); public static IEnumerable EmptySeries(this IEnumerable gridEntries) => gridEntries.Series().Where(i => i.Children.Count == 0); + public static bool IsEpisodeWithSeries(this LibraryBook lb) => lb.Book.ContentType == ContentType.Episode && lb.Book.SeriesLink is not null && lb.Book.SeriesLink.Any(); } } diff --git a/Source/LibationWinForms/GridView/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs index 99c52788..f655bf74 100644 --- a/Source/LibationWinForms/GridView/ProductsGrid.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.cs @@ -84,7 +84,7 @@ namespace LibationWinForms.GridView { var geList = dbBooks.Where(b => b.Book.ContentType is not ContentType.Episode).Select(b => new LibraryBookEntry(b)).Cast().ToList(); - var episodes = dbBooks.Where(b => b.Book.ContentType is ContentType.Episode).ToList(); + var episodes = dbBooks.Where(b => b.IsEpisodeWithSeries()).ToList(); foreach (var series in episodes.Select(lb => lb.Book.SeriesLink.First()).DistinctBy(s => s.Series)) { @@ -117,7 +117,7 @@ namespace LibationWinForms.GridView // add new to top if (existingItem is null) { - if (libraryBook.Book.ContentType is ContentType.Episode) + if (libraryBook.IsEpisodeWithSeries()) { LibraryBookEntry lbe; //Find the series that libraryBook belongs to, if it exists @@ -148,7 +148,7 @@ namespace LibationWinForms.GridView series.NotifyPropertyChanged(); } - else + else if (libraryBook.Book.ContentType is not ContentType.Episode) //Add the new product bindingList.Insert(0, new LibraryBookEntry(libraryBook)); } From 845af854bde9d52548063075c52c2bddee75f6aa Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Thu, 26 May 2022 16:29:40 -0600 Subject: [PATCH 2/7] Add exception handling to products display --- .../GridView/ProductsDisplay.cs | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/Source/LibationWinForms/GridView/ProductsDisplay.cs b/Source/LibationWinForms/GridView/ProductsDisplay.cs index 7e1b2d8a..7c411f3f 100644 --- a/Source/LibationWinForms/GridView/ProductsDisplay.cs +++ b/Source/LibationWinForms/GridView/ProductsDisplay.cs @@ -84,18 +84,25 @@ namespace LibationWinForms.GridView public void Display() { - // don't return early if lib size == 0. this will not update correctly if all books are removed - var lib = DbContexts.GetLibrary_Flat_NoTracking(); - - if (!hasBeenDisplayed) + try { - // bind - productsGrid.BindToGrid(lib); - hasBeenDisplayed = true; - InitialLoaded?.Invoke(this, new()); + // don't return early if lib size == 0. this will not update correctly if all books are removed + var lib = DbContexts.GetLibrary_Flat_NoTracking(); + + if (!hasBeenDisplayed) + { + // bind + productsGrid.BindToGrid(lib); + hasBeenDisplayed = true; + InitialLoaded?.Invoke(this, new()); + } + else + productsGrid.UpdateGrid(lib); + } + catch (Exception ex) + { + Serilog.Log.Error(ex, "Error displaying library in {0}", nameof(ProductsDisplay)); } - else - productsGrid.UpdateGrid(lib); } From 1b2ec6772603f1279a983171adc848d50039585b Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Thu, 26 May 2022 16:43:56 -0600 Subject: [PATCH 3/7] Add series info for parent will null order. --- Source/AudibleUtilities/ApiExtended.cs | 1 + Source/LibationWinForms/GridView/GridEntry.cs | 3 ++- Source/LibationWinForms/GridView/ProductsGrid.cs | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index ca09a64b..9a8bf20c 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -137,6 +137,7 @@ namespace AudibleUtilities //Add the parent to the library because it contains the series //description, series rating, and series cover art which differ //from the individual episodes' values. + item.Series = new Series[]{ new Series { Asin = item.Asin, Title = item.TitleWithSubtitle } }; items.Add(item); } else if (!item.IsEpisodes) diff --git a/Source/LibationWinForms/GridView/GridEntry.cs b/Source/LibationWinForms/GridView/GridEntry.cs index e6d479f2..5a330897 100644 --- a/Source/LibationWinForms/GridView/GridEntry.cs +++ b/Source/LibationWinForms/GridView/GridEntry.cs @@ -109,6 +109,7 @@ namespace LibationWinForms.GridView => gridEntries.Series().FirstOrDefault(i => matchSeries.Any(s => s.Series.Name == i.Series)); public static IEnumerable EmptySeries(this IEnumerable gridEntries) => gridEntries.Series().Where(i => i.Children.Count == 0); - public static bool IsEpisodeWithSeries(this LibraryBook lb) => lb.Book.ContentType == ContentType.Episode && lb.Book.SeriesLink is not null && lb.Book.SeriesLink.Any(); + public static bool IsEpisodeChild(this LibraryBook lb) => lb.Book.ContentType == ContentType.Episode && lb.Book.SeriesLink is not null && lb.Book.SeriesLink.Any() && lb.Book.SeriesLink.First().Order is not null; + public static bool IsEpisodeParent(this LibraryBook lb) => lb.Book.ContentType == ContentType.Episode && lb.Book.SeriesLink is not null && lb.Book.SeriesLink.Any() && lb.Book.SeriesLink.First().Order is null; } } diff --git a/Source/LibationWinForms/GridView/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs index f655bf74..3fa7d0d8 100644 --- a/Source/LibationWinForms/GridView/ProductsGrid.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.cs @@ -84,7 +84,7 @@ namespace LibationWinForms.GridView { var geList = dbBooks.Where(b => b.Book.ContentType is not ContentType.Episode).Select(b => new LibraryBookEntry(b)).Cast().ToList(); - var episodes = dbBooks.Where(b => b.IsEpisodeWithSeries()).ToList(); + var episodes = dbBooks.Where(b => b.IsEpisodeChild()).ToList(); foreach (var series in episodes.Select(lb => lb.Book.SeriesLink.First()).DistinctBy(s => s.Series)) { @@ -117,7 +117,7 @@ namespace LibationWinForms.GridView // add new to top if (existingItem is null) { - if (libraryBook.IsEpisodeWithSeries()) + if (libraryBook.IsEpisodeChild()) { LibraryBookEntry lbe; //Find the series that libraryBook belongs to, if it exists From 2c8657181889ca5069abd8d6de15c79db828f186 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Thu, 26 May 2022 16:49:03 -0600 Subject: [PATCH 4/7] Better identification of Chilv vs Parent from SeriesBook.Order --- Source/AudibleUtilities/ApiExtended.cs | 2 +- Source/LibationWinForms/GridView/GridEntry.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index 9a8bf20c..9f339dfd 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -137,7 +137,7 @@ namespace AudibleUtilities //Add the parent to the library because it contains the series //description, series rating, and series cover art which differ //from the individual episodes' values. - item.Series = new Series[]{ new Series { Asin = item.Asin, Title = item.TitleWithSubtitle } }; + item.Series = new Series[]{ new Series { Asin = item.Asin, Sequence = RelationshipToProduct.Parent, Title = item.TitleWithSubtitle } }; items.Add(item); } else if (!item.IsEpisodes) diff --git a/Source/LibationWinForms/GridView/GridEntry.cs b/Source/LibationWinForms/GridView/GridEntry.cs index 5a330897..63c1a7c1 100644 --- a/Source/LibationWinForms/GridView/GridEntry.cs +++ b/Source/LibationWinForms/GridView/GridEntry.cs @@ -109,7 +109,7 @@ namespace LibationWinForms.GridView => gridEntries.Series().FirstOrDefault(i => matchSeries.Any(s => s.Series.Name == i.Series)); public static IEnumerable EmptySeries(this IEnumerable gridEntries) => gridEntries.Series().Where(i => i.Children.Count == 0); - public static bool IsEpisodeChild(this LibraryBook lb) => lb.Book.ContentType == ContentType.Episode && lb.Book.SeriesLink is not null && lb.Book.SeriesLink.Any() && lb.Book.SeriesLink.First().Order is not null; - public static bool IsEpisodeParent(this LibraryBook lb) => lb.Book.ContentType == ContentType.Episode && lb.Book.SeriesLink is not null && lb.Book.SeriesLink.Any() && lb.Book.SeriesLink.First().Order is null; + public static bool IsEpisodeChild(this LibraryBook lb) => lb.Book.ContentType == ContentType.Episode && lb.Book.SeriesLink is not null && lb.Book.SeriesLink.Any() && lb.Book.SeriesLink.First().Order != AudibleApi.Common.RelationshipToProduct.Parent; + public static bool IsEpisodeParent(this LibraryBook lb) => lb.Book.ContentType == ContentType.Episode && lb.Book.SeriesLink is not null && lb.Book.SeriesLink.Any() && lb.Book.SeriesLink.First().Order == AudibleApi.Common.RelationshipToProduct.Parent; } } From 61f4dbd89627bcd899bf7dcc8f0d916700b700cf Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Thu, 26 May 2022 16:50:43 -0600 Subject: [PATCH 5/7] No need to make a new list. --- Source/AudibleUtilities/ApiExtended.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index 9f339dfd..f732ff78 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -181,10 +181,8 @@ namespace AudibleUtilities var children = await getEpisodeChildrenAsync(parent); - // actual individual episode, not the parent of a series. - // for now I'm keeping it inside this method since it fits the work flow, incl. importEpisodes logic if (!children.Any()) - return new(); + return children; foreach (var child in children) { From fb247fb33fb3457ce644c2e552cde65e7cddb89f Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Thu, 26 May 2022 17:29:55 -0600 Subject: [PATCH 6/7] Add better handling for parents and series with no children. --- Source/AudibleUtilities/ApiExtended.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index f732ff78..dd5e602e 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -138,6 +138,9 @@ namespace AudibleUtilities //description, series rating, and series cover art which differ //from the individual episodes' values. item.Series = new Series[]{ new Series { Asin = item.Asin, Sequence = RelationshipToProduct.Parent, Title = item.TitleWithSubtitle } }; + //Helps to distinguish product parrents which have no content + //from children which do have content. + item.Asin = $"PARENT_{item.Asin}"; items.Add(item); } else if (!item.IsEpisodes) @@ -155,7 +158,7 @@ namespace AudibleUtilities Serilog.Log.Logger.Debug("Completed library scan."); #if DEBUG -//System.IO.File.WriteAllText(library_json, AudibleApi.Common.Converter.ToJson(items)); + //System.IO.File.WriteAllText(library_json, AudibleApi.Common.Converter.ToJson(items)); #endif var validators = new List(); validators.AddRange(getValidators()); @@ -182,7 +185,13 @@ namespace AudibleUtilities var children = await getEpisodeChildrenAsync(parent); if (!children.Any()) - return children; + { + //The parent is the only episode in the podcase series, + //so the parent is its own child. + var parentJson = parent.ToJson(parent).ToString(); + var child = Item.FromJson(parentJson); + children.Add(child); + } foreach (var child in children) { @@ -195,7 +204,7 @@ namespace AudibleUtilities { Asin = parent.Asin, // This should properly be Single() not FirstOrDefault(), but FirstOrDefault is defensive for malformed data from audible - Sequence = parent.Relationships.FirstOrDefault(r => r.Asin == child.Asin).Sort.ToString(), + Sequence = parent.Relationships.FirstOrDefault(r => r.Asin == child.Asin)?.Sort?.ToString() ?? "0", Title = parent.TitleWithSubtitle } }; From d9fbcc615a68c620adadbb975e4c3be32c5ccfc9 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Thu, 26 May 2022 18:06:44 -0600 Subject: [PATCH 7/7] Change flow --- Source/AudibleUtilities/ApiExtended.cs | 16 +++++++++------- Source/LibationWinForms/GridView/ProductsGrid.cs | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index dd5e602e..1cb31a28 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -131,16 +131,17 @@ namespace AudibleUtilities { if (item.IsEpisodes && importEpisodes) { - //Get child episodes asynchronously and await all at the end - getChildEpisodesTasks.Add(getChildEpisodesAsync(concurrencySemaphore, item)); - + //Helps to distinguish product parrents which have no content + //from children which do have content. + item.Asin = $"SERIES_{item.Asin}"; //Add the parent to the library because it contains the series //description, series rating, and series cover art which differ //from the individual episodes' values. - item.Series = new Series[]{ new Series { Asin = item.Asin, Sequence = RelationshipToProduct.Parent, Title = item.TitleWithSubtitle } }; - //Helps to distinguish product parrents which have no content - //from children which do have content. - item.Asin = $"PARENT_{item.Asin}"; + item.Series = new Series[] { new Series { Asin = item.Asin, Sequence = RelationshipToProduct.Parent, Title = item.TitleWithSubtitle } }; + + //Get child episodes asynchronously and await all at the end + getChildEpisodesTasks.Add(getChildEpisodesAsync(concurrencySemaphore, item)); + items.Add(item); } else if (!item.IsEpisodes) @@ -190,6 +191,7 @@ namespace AudibleUtilities //so the parent is its own child. var parentJson = parent.ToJson(parent).ToString(); var child = Item.FromJson(parentJson); + child.Asin = child.Asin.Replace("SERIES_", ""); children.Add(child); } diff --git a/Source/LibationWinForms/GridView/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs index 3fa7d0d8..f158c8e9 100644 --- a/Source/LibationWinForms/GridView/ProductsGrid.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.cs @@ -86,9 +86,9 @@ namespace LibationWinForms.GridView var episodes = dbBooks.Where(b => b.IsEpisodeChild()).ToList(); - foreach (var series in episodes.Select(lb => lb.Book.SeriesLink.First()).DistinctBy(s => s.Series)) + foreach (var series in episodes.SelectMany(lb => lb.Book.SeriesLink).DistinctBy(s => s.Series)) { - var seriesEntry = new SeriesEntry(series, episodes.Where(lb => lb.Book.SeriesLink.First().Series == series.Book.SeriesLink.First().Series)); + var seriesEntry = new SeriesEntry(series, episodes.Where(lb => lb.Book.SeriesLink.Any(s => s.Series == series.Series))); geList.Add(seriesEntry); geList.AddRange(seriesEntry.Children);