diff --git a/Source/LibationUiBase/GridView/GridEntry[TStatus].cs b/Source/LibationUiBase/GridView/GridEntry[TStatus].cs
index a57b52db..e00991cd 100644
--- a/Source/LibationUiBase/GridView/GridEntry[TStatus].cs
+++ b/Source/LibationUiBase/GridView/GridEntry[TStatus].cs
@@ -1,7 +1,6 @@
using ApplicationServices;
using DataLayer;
using Dinah.Core;
-using Dinah.Core.Threading;
using FileLiberator;
using LibationFileManager;
using System;
@@ -9,7 +8,7 @@ using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
-using System.Runtime.CompilerServices;
+using System.Threading;
using System.Threading.Tasks;
namespace LibationUiBase.GridView
@@ -311,6 +310,36 @@ namespace LibationUiBase.GridView
#endregion
+
+ ///
+ /// Creates for all non-episode books in an enumeration of .
+ ///
+ /// Can be called from any thread, but requires the calling thread's to be valid.
+ public static async Task> GetAllProductsAsync(IEnumerable libraryBooks, Func includeIf, Func factory)
+ where TEntry : IGridEntry
+ {
+ var products = libraryBooks.Where(includeIf).ToArray();
+ if (products.Length == 0)
+ return [];
+
+ int parallelism = int.Max(1, Environment.ProcessorCount - 1);
+
+ (int batchSize, int rem) = int.DivRem(products.Length, parallelism);
+ if (rem != 0) batchSize++;
+
+ var syncContext = SynchronizationContext.Current;
+
+ //Asynchronously create a GridEntry for every book in the library
+ var tasks = products.Chunk(batchSize).Select(batch => Task.Run(() =>
+ {
+ SynchronizationContext.SetSynchronizationContext(syncContext);
+ return batch.Select(factory).OfType().ToArray();
+ }));
+
+ return (await Task.WhenAll(tasks)).SelectMany(a => a).ToList();
+ }
+
+
~GridEntry()
{
PictureStorage.PictureCached -= PictureStorage_PictureCached;
diff --git a/Source/LibationUiBase/GridView/LibraryBookEntry[TStatus].cs b/Source/LibationUiBase/GridView/LibraryBookEntry[TStatus].cs
index 2d118e36..0dbff282 100644
--- a/Source/LibationUiBase/GridView/LibraryBookEntry[TStatus].cs
+++ b/Source/LibationUiBase/GridView/LibraryBookEntry[TStatus].cs
@@ -3,8 +3,6 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
-using System.Threading;
-using System.Linq;
namespace LibationUiBase.GridView
{
@@ -36,29 +34,9 @@ namespace LibationUiBase.GridView
///
/// Creates for all non-episode books in an enumeration of .
///
- /// Can be called from any thread, but requires the calling thread's to be valid.
+ /// Can be called from any thread, but requires the calling thread's to be valid.
public static async Task> GetAllProductsAsync(IEnumerable libraryBooks)
- {
- var products = libraryBooks.Where(lb => lb.Book.IsProduct()).ToArray();
- if (products.Length == 0)
- return [];
-
- int parallelism = int.Max(1, Environment.ProcessorCount - 1);
-
- (int batchSize, int rem) = int.DivRem(products.Length, parallelism);
- if (rem != 0) batchSize++;
-
- var syncContext = SynchronizationContext.Current;
-
- //Asynchronously create an ILibraryBookEntry for every book in the library
- var tasks = products.Chunk(batchSize).Select(batch => Task.Run(() =>
- {
- SynchronizationContext.SetSynchronizationContext(syncContext);
- return batch.Select(lb => new LibraryBookEntry(lb) as IGridEntry);
- }));
-
- return (await Task.WhenAll(tasks)).SelectMany(a => a).ToList();
- }
+ => await GetAllProductsAsync(libraryBooks, lb => lb.Book.IsProduct(), lb => new LibraryBookEntry(lb) as IGridEntry);
protected override string GetBookTags() => string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated);
}
diff --git a/Source/LibationUiBase/GridView/SeriesEntry[TStatus].cs b/Source/LibationUiBase/GridView/SeriesEntry[TStatus].cs
index 17eeff93..80627eac 100644
--- a/Source/LibationUiBase/GridView/SeriesEntry[TStatus].cs
+++ b/Source/LibationUiBase/GridView/SeriesEntry[TStatus].cs
@@ -1,4 +1,5 @@
using DataLayer;
+using Dinah.Core.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -62,56 +63,32 @@ namespace LibationUiBase.GridView
/// Can be called from any thread, but requires the calling thread's to be valid.
public static async Task> GetAllSeriesEntriesAsync(IEnumerable libraryBooks)
{
- var seriesBooks = libraryBooks.Where(lb => lb.Book.IsEpisodeParent()).ToArray();
- var allEpisodes = libraryBooks.Where(lb => lb.Book.IsEpisodeChild()).ToArray();
+ var seriesEntries = await GetAllProductsAsync(libraryBooks, lb => lb.Book.IsEpisodeParent(), lb => new SeriesEntry(lb, []) as ISeriesEntry);
+ var seriesDict = seriesEntries.ToDictionarySafe(s => s.AudibleProductId);
+ await GetAllProductsAsync(libraryBooks, lb => lb.Book.IsEpisodeChild(), CreateAndLinkEpisodeEntry);
- var seriesEntries = new ISeriesEntry[seriesBooks.Length];
- var seriesEpisodes = new ILibraryBookEntry[seriesBooks.Length][];
-
- var syncContext = SynchronizationContext.Current;
- var options = new ParallelOptions { MaxDegreeOfParallelism = int.Max(1, Environment.ProcessorCount - 1) };
-
- //Asynchronously create an ILibraryBookEntry for every episode in the library
- await Parallel.ForEachAsync(getAllEpisodes(), options, createEpisodeEntry);
-
- //Match all episode entries to their corresponding parents
- for (int i = seriesEntries.Length - 1; i >= 0; i--)
+ //sort episodes by series order descending and update SeriesEntry
+ foreach (var series in seriesEntries)
{
- var series = seriesEntries[i];
-
- //Sort episodes by series order descending, then add them to their parent's entry
- Array.Sort(seriesEpisodes[i], (a, b) => -a.SeriesOrder.CompareTo(b.SeriesOrder));
- series.Children.AddRange(seriesEpisodes[i]);
+ series.Children.Sort((a, b) => -a.SeriesOrder.CompareTo(b.SeriesOrder));
series.UpdateLibraryBook(series.LibraryBook);
}
- return seriesEntries.Where(s => s.Children.Count != 0).Cast().ToList();
+ return seriesEntries.Where(s => s.Children.Count != 0).ToList();
- //Create a LibraryBookEntry for a single episode
- ValueTask createEpisodeEntry((int seriesIndex, int episodeIndex, LibraryBook episode) data, CancellationToken cancellationToken)
+ //Create a LibraryBookEntry for an episode and link it to its series parent
+ LibraryBookEntry CreateAndLinkEpisodeEntry(LibraryBook episode)
{
- SynchronizationContext.SetSynchronizationContext(syncContext);
- var parent = seriesEntries[data.seriesIndex];
- seriesEpisodes[data.seriesIndex][data.episodeIndex] = new LibraryBookEntry(data.episode, parent);
- return ValueTask.CompletedTask;
- }
-
- //Enumeration all series episodes, along with the index to its seriesEntries entry
- //and an index to its seriesEpisodes entry
- IEnumerable<(int seriesIndex, int episodeIndex, LibraryBook episode)> getAllEpisodes()
- {
- for (int i = 0; i < seriesBooks.Length; i++)
+ foreach (var s in episode.Book.SeriesLink)
{
- var series = seriesBooks[i];
- var childEpisodes = allEpisodes.FindChildren(series);
-
- SynchronizationContext.SetSynchronizationContext(syncContext);
- seriesEntries[i] = new SeriesEntry(series, []);
- seriesEpisodes[i] = new ILibraryBookEntry[childEpisodes.Count];
-
- for (int j = 0; j < childEpisodes.Count; j++)
- yield return (i, j, childEpisodes[j]);
+ if (seriesDict.TryGetValue(s.Series.AudibleSeriesId, out var seriesParent))
+ {
+ var entry = new LibraryBookEntry(episode, seriesParent);
+ seriesParent.Children.Add(entry);
+ return entry;
+ }
}
+ return null;
}
}