diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index 70d338b6..1cb31a28 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -131,8 +131,18 @@ namespace AudibleUtilities { if (item.IsEpisodes && importEpisodes) { + //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 } }; + //Get child episodes asynchronously and await all at the end getChildEpisodesTasks.Add(getChildEpisodesAsync(concurrencySemaphore, item)); + + items.Add(item); } else if (!item.IsEpisodes) items.Add(item); @@ -149,7 +159,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()); @@ -175,10 +185,15 @@ 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 List() { parent }; + { + //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); + child.Asin = child.Asin.Replace("SERIES_", ""); + children.Add(child); + } foreach (var child in children) { @@ -191,7 +206,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 } }; diff --git a/Source/LibationWinForms/GridView/GridEntry.cs b/Source/LibationWinForms/GridView/GridEntry.cs index b20c1287..63c1a7c1 100644 --- a/Source/LibationWinForms/GridView/GridEntry.cs +++ b/Source/LibationWinForms/GridView/GridEntry.cs @@ -109,5 +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 != 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; } } 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); } diff --git a/Source/LibationWinForms/GridView/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs index 99c52788..f158c8e9 100644 --- a/Source/LibationWinForms/GridView/ProductsGrid.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.cs @@ -84,11 +84,11 @@ 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.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); @@ -117,7 +117,7 @@ namespace LibationWinForms.GridView // add new to top if (existingItem is null) { - if (libraryBook.Book.ContentType is ContentType.Episode) + if (libraryBook.IsEpisodeChild()) { 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)); }