From 59de048ced7c60895751e34be4658379f75118dd Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 24 May 2022 15:29:17 -0600 Subject: [PATCH 1/8] Error handling network error. --- Source/AaxDecrypter/NetworkFileStream.cs | 57 +++++++++++-------- Source/LibationWinForms/GridView/GridEntry.cs | 4 +- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/Source/AaxDecrypter/NetworkFileStream.cs b/Source/AaxDecrypter/NetworkFileStream.cs index 259b3526..e0a6506f 100644 --- a/Source/AaxDecrypter/NetworkFileStream.cs +++ b/Source/AaxDecrypter/NetworkFileStream.cs @@ -217,40 +217,47 @@ namespace AaxDecrypter { var downloadPosition = WritePosition; var nextFlush = downloadPosition + DATA_FLUSH_SZ; - var buff = new byte[DOWNLOAD_BUFF_SZ]; - do + + try { - var bytesRead = _networkStream.Read(buff, 0, DOWNLOAD_BUFF_SZ); - _writeFile.Write(buff, 0, bytesRead); - - downloadPosition += bytesRead; - - if (downloadPosition > nextFlush) + do { - _writeFile.Flush(); - WritePosition = downloadPosition; - Update(); - nextFlush = downloadPosition + DATA_FLUSH_SZ; - downloadedPiece.Set(); - } + var bytesRead = _networkStream.Read(buff, 0, DOWNLOAD_BUFF_SZ); + _writeFile.Write(buff, 0, bytesRead); - } while (downloadPosition < ContentLength && !IsCancelled); + downloadPosition += bytesRead; - _writeFile.Close(); - _networkStream.Close(); - WritePosition = downloadPosition; - Update(); + if (downloadPosition > nextFlush) + { + _writeFile.Flush(); + WritePosition = downloadPosition; + Update(); + nextFlush = downloadPosition + DATA_FLUSH_SZ; + downloadedPiece.Set(); + } - downloadedPiece.Set(); - downloadEnded.Set(); + } while (downloadPosition < ContentLength && !IsCancelled); - if (!IsCancelled && WritePosition < ContentLength) - throw new WebException($"Downloaded size (0x{WritePosition:X10}) is less than {nameof(ContentLength)} (0x{ContentLength:X10})."); + _writeFile.Close(); + _networkStream.Close(); + WritePosition = downloadPosition; + Update(); - if (WritePosition > ContentLength) - throw new WebException($"Downloaded size (0x{WritePosition:X10}) is greater than {nameof(ContentLength)} (0x{ContentLength:X10})."); + downloadedPiece.Set(); + downloadEnded.Set(); + if (!IsCancelled && WritePosition < ContentLength) + throw new WebException($"Downloaded size (0x{WritePosition:X10}) is less than {nameof(ContentLength)} (0x{ContentLength:X10})."); + + if (WritePosition > ContentLength) + throw new WebException($"Downloaded size (0x{WritePosition:X10}) is greater than {nameof(ContentLength)} (0x{ContentLength:X10})."); + } + catch (Exception ex) + { + Serilog.Log.Error(ex, "An error was encountered while downloading {Uri}", Uri); + IsCancelled = true; + } } #endregion diff --git a/Source/LibationWinForms/GridView/GridEntry.cs b/Source/LibationWinForms/GridView/GridEntry.cs index 6d7e9cd8..b20c1287 100644 --- a/Source/LibationWinForms/GridView/GridEntry.cs +++ b/Source/LibationWinForms/GridView/GridEntry.cs @@ -100,9 +100,9 @@ namespace LibationWinForms.GridView { #nullable enable public static IEnumerable Series(this IEnumerable gridEntries) - => gridEntries.Where(i => i is SeriesEntry).Cast(); + => gridEntries.OfType(); public static IEnumerable LibraryBooks(this IEnumerable gridEntries) - => gridEntries.Where(i => i is LibraryBookEntry).Cast(); + => gridEntries.OfType(); public static LibraryBookEntry? FindBookByAsin(this IEnumerable gridEntries, string audibleProductID) => gridEntries.FirstOrDefault(i => i.AudibleProductId == audibleProductID); public static SeriesEntry? FindBookSeriesEntry(this IEnumerable gridEntries, IEnumerable matchSeries) From bc0009be6cee1f13beccc074485ece432899fb51 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 24 May 2022 15:47:30 -0600 Subject: [PATCH 2/8] Use event return value instead of passing a set delegate. --- Source/FileLiberator/AudioDecodable.cs | 7 ++++--- Source/FileLiberator/DownloadDecryptBook.cs | 2 +- .../LibationWinForms/ProcessQueue/ProcessBook.cs | 15 ++++++--------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Source/FileLiberator/AudioDecodable.cs b/Source/FileLiberator/AudioDecodable.cs index 3f230fb7..9b441efb 100644 --- a/Source/FileLiberator/AudioDecodable.cs +++ b/Source/FileLiberator/AudioDecodable.cs @@ -4,7 +4,8 @@ namespace FileLiberator { public abstract class AudioDecodable : Processable { - public event EventHandler> RequestCoverArt; + public delegate byte[] RequestCoverArtHandler(object sender, EventArgs eventArgs); + public event RequestCoverArtHandler RequestCoverArt; public event EventHandler TitleDiscovered; public event EventHandler AuthorsDiscovered; public event EventHandler NarratorsDiscovered; @@ -32,10 +33,10 @@ namespace FileLiberator NarratorsDiscovered?.Invoke(this, narrators); } - protected void OnRequestCoverArt(Action setCoverArtDel) + protected byte[] OnRequestCoverArt() { Serilog.Log.Logger.Debug("Event fired {@DebugInfo}", new { Name = nameof(RequestCoverArt) }); - RequestCoverArt?.Invoke(this, setCoverArtDel); + return RequestCoverArt?.Invoke(this, new()); } protected void OnCoverImageDiscovered(byte[] coverImage) diff --git a/Source/FileLiberator/DownloadDecryptBook.cs b/Source/FileLiberator/DownloadDecryptBook.cs index 1236c7a5..b54f9905 100644 --- a/Source/FileLiberator/DownloadDecryptBook.cs +++ b/Source/FileLiberator/DownloadDecryptBook.cs @@ -247,7 +247,7 @@ namespace FileLiberator if (e is not null) OnCoverImageDiscovered(e); else if (Configuration.Instance.AllowLibationFixup) - OnRequestCoverArt(abDownloader.SetCoverArt); + abDownloader.SetCoverArt(OnRequestCoverArt()); } /// Move new files to 'Books' directory diff --git a/Source/LibationWinForms/ProcessQueue/ProcessBook.cs b/Source/LibationWinForms/ProcessQueue/ProcessBook.cs index 7c9778a5..01928df9 100644 --- a/Source/LibationWinForms/ProcessQueue/ProcessBook.cs +++ b/Source/LibationWinForms/ProcessQueue/ProcessBook.cs @@ -59,7 +59,6 @@ namespace LibationWinForms.ProcessQueue private Processable CurrentProcessable => _currentProcessable ??= Processes.Dequeue().Invoke(); private Processable NextProcessable() => _currentProcessable = null; private Processable _currentProcessable; - private Func GetCoverArtDelegate; private readonly Queue> Processes = new(); private readonly LogMe Logger; @@ -231,11 +230,14 @@ namespace LibationWinForms.ProcessQueue BookText = $"{title}\r\nBy {authorNames}\r\nNarrated by {narratorNames}"; } - public void AudioDecodable_RequestCoverArt(object sender, Action setCoverArtDelegate) + private byte[] AudioDecodable_RequestCoverArt(object sender, EventArgs e) { - byte[] coverData = GetCoverArtDelegate(); - setCoverArtDelegate(coverData); + byte[] coverData = PictureStorage + .GetPictureSynchronously( + new PictureDefinition(LibraryBook.Book.PictureId, PictureSize._500x500)); + AudioDecodable_CoverImageDiscovered(this, coverData); + return coverData; } private void AudioDecodable_CoverImageDiscovered(object sender, byte[] coverArt) @@ -272,11 +274,6 @@ namespace LibationWinForms.ProcessQueue Logger.Info($"{Environment.NewLine}{((Processable)sender).Name} Step, Begin: {libraryBook.Book}"); - GetCoverArtDelegate = () => PictureStorage.GetPictureSynchronously( - new PictureDefinition( - libraryBook.Book.PictureId, - PictureSize._500x500)); - title = libraryBook.Book.Title; authorNames = libraryBook.Book.AuthorNames(); narratorNames = libraryBook.Book.NarratorNames(); From ed66019d9ae85e6f10d770b4babe0169eb64e016 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 24 May 2022 18:24:53 -0600 Subject: [PATCH 3/8] Cleanup --- .../GridView/LiberateDataGridViewImageButtonColumn.cs | 5 +---- Source/LibationWinForms/ProcessQueue/ProcessBook.cs | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs b/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs index 62c56538..1984587d 100644 --- a/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs +++ b/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs @@ -30,10 +30,7 @@ namespace LibationWinForms.GridView if (status.IsSeries) { - var imageName = status.Expanded ? "minus" : "plus"; - - var bmp = (Bitmap)Properties.Resources.ResourceManager.GetObject(imageName); - DrawButtonImage(graphics, bmp, cellBounds); + DrawButtonImage(graphics, status.Expanded ? Properties.Resources.minus: Properties.Resources.plus, cellBounds); ToolTipText = status.Expanded ? "Click to Collpase" : "Click to Expand"; } diff --git a/Source/LibationWinForms/ProcessQueue/ProcessBook.cs b/Source/LibationWinForms/ProcessQueue/ProcessBook.cs index 01928df9..6768dfe3 100644 --- a/Source/LibationWinForms/ProcessQueue/ProcessBook.cs +++ b/Source/LibationWinForms/ProcessQueue/ProcessBook.cs @@ -282,7 +282,6 @@ namespace LibationWinForms.ProcessQueue private async void Processable_Completed(object sender, LibraryBook libraryBook) { - Logger.Info($"{((Processable)sender).Name} Step, Completed: {libraryBook.Book}"); UnlinkProcessable((Processable)sender); From a7b83ad5e05ad61a325888cd28646a93b3f099b0 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 24 May 2022 18:27:20 -0600 Subject: [PATCH 4/8] Remove 10,000 book limitation and simplify episode import --- Source/AudibleUtilities/ApiExtended.cs | 69 ++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index 5a00c9a7..7d77401f 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -118,6 +118,75 @@ namespace AudibleUtilities private async Task> getItemsAsync(LibraryOptions libraryOptions, bool importEpisodes) { var items = new List(); + + Serilog.Log.Logger.Debug("Begin initial library scan"); + + await foreach (var item in Api.GetLibraryItemAsyncEnumerable(libraryOptions)) + { + if (item.IsEpisodes && importEpisodes) + { + var children = await getEpisodeChildrenAsync(item); + + // 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()) + { + items.Add(item); + continue; + } + + foreach (var child in children) + { + // use parent's 'DateAdded'. DateAdded is just a convenience prop for: PurchaseDate.UtcDateTime + child.PurchaseDate = item.PurchaseDate; + // parent is essentially a series + child.Series = new Series[] + { + new Series + { + Asin = item.Asin, + // This should properly be Single() not FirstOrDefault(), but FirstOrDefault is defensive for malformed data from audible + Sequence = item.Relationships.FirstOrDefault(r => r.Asin == child.Asin).Sort.ToString(), + Title = item.TitleWithSubtitle + } + }; + // overload (read: abuse) IsEpisodes flag + child.Relationships = new Relationship[] + { + new Relationship + { + RelationshipToProduct = RelationshipToProduct.Child, + RelationshipType = RelationshipType.Episode + } + }; + } + items.AddRange(children); + } + else + items.Add(item); + } + + Serilog.Log.Logger.Debug("Scan complete"); + +#if DEBUG +//System.IO.File.WriteAllText(library_json, AudibleApi.Common.Converter.ToJson(items)); +#endif + + var validators = new List(); + validators.AddRange(getValidators()); + foreach (var v in validators) + { + var exceptions = v.Validate(items); + if (exceptions is not null && exceptions.Any()) + throw new AggregateException(exceptions); + } + + return items; + } + + private async Task> getItemsAsync_old(LibraryOptions libraryOptions, bool importEpisodes) + { + var items = new List(); #if DEBUG //// this will not work for multi accounts //var library_json = "library.json"; From 242909b542e2ca291051279855705b81bdb45697 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 24 May 2022 18:39:47 -0600 Subject: [PATCH 5/8] Don't import empty episode --- Source/AudibleUtilities/ApiExtended.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index 7d77401f..4d2b8fc9 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -162,7 +162,7 @@ namespace AudibleUtilities } items.AddRange(children); } - else + else if (!item.IsEpisodes) items.Add(item); } @@ -184,17 +184,17 @@ namespace AudibleUtilities return items; } - private async Task> getItemsAsync_old(LibraryOptions libraryOptions, bool importEpisodes) + private async Task> getItemsAsync2(LibraryOptions libraryOptions, bool importEpisodes) { var items = new List(); #if DEBUG -//// this will not work for multi accounts -//var library_json = "library.json"; -//library_json = System.IO.Path.GetFullPath(library_json); -//if (System.IO.File.Exists(library_json)) -//{ -// items = AudibleApi.Common.Converter.FromJson>(System.IO.File.ReadAllText(library_json)); -//} + //// this will not work for multi accounts + //var library_json = "library.json"; + //library_json = System.IO.Path.GetFullPath(library_json); + //if (System.IO.File.Exists(library_json)) + //{ + // items = AudibleApi.Common.Converter.FromJson>(System.IO.File.ReadAllText(library_json)); + //} #endif Serilog.Log.Logger.Debug("Begin initial library scan"); From 8283f19d6bbbdc90bfb69192342355f6cadd5807 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 24 May 2022 21:17:59 -0600 Subject: [PATCH 6/8] Parallelize getChildEpisodesAsync --- Source/AudibleUtilities/ApiExtended.cs | 168 +++++-------------------- 1 file changed, 28 insertions(+), 140 deletions(-) diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index 4d2b8fc9..aa2625e9 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -121,93 +121,25 @@ namespace AudibleUtilities Serilog.Log.Logger.Debug("Begin initial library scan"); + List>> getChildEpisodesTasks = new(); + await foreach (var item in Api.GetLibraryItemAsyncEnumerable(libraryOptions)) { if (item.IsEpisodes && importEpisodes) { - var children = await getEpisodeChildrenAsync(item); - - // 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()) - { - items.Add(item); - continue; - } - - foreach (var child in children) - { - // use parent's 'DateAdded'. DateAdded is just a convenience prop for: PurchaseDate.UtcDateTime - child.PurchaseDate = item.PurchaseDate; - // parent is essentially a series - child.Series = new Series[] - { - new Series - { - Asin = item.Asin, - // This should properly be Single() not FirstOrDefault(), but FirstOrDefault is defensive for malformed data from audible - Sequence = item.Relationships.FirstOrDefault(r => r.Asin == child.Asin).Sort.ToString(), - Title = item.TitleWithSubtitle - } - }; - // overload (read: abuse) IsEpisodes flag - child.Relationships = new Relationship[] - { - new Relationship - { - RelationshipToProduct = RelationshipToProduct.Child, - RelationshipType = RelationshipType.Episode - } - }; - } - items.AddRange(children); + //Get child episodes asynchronously and await all at the end + getChildEpisodesTasks.Add(getChildEpisodesAsync(item)); } else if (!item.IsEpisodes) items.Add(item); } + //asait and all all episides from all parents + foreach (var epList in await Task.WhenAll(getChildEpisodesTasks)) + items.AddRange(epList); + Serilog.Log.Logger.Debug("Scan complete"); -#if DEBUG -//System.IO.File.WriteAllText(library_json, AudibleApi.Common.Converter.ToJson(items)); -#endif - - var validators = new List(); - validators.AddRange(getValidators()); - foreach (var v in validators) - { - var exceptions = v.Validate(items); - if (exceptions is not null && exceptions.Any()) - throw new AggregateException(exceptions); - } - - return items; - } - - private async Task> getItemsAsync2(LibraryOptions libraryOptions, bool importEpisodes) - { - var items = new List(); -#if DEBUG - //// this will not work for multi accounts - //var library_json = "library.json"; - //library_json = System.IO.Path.GetFullPath(library_json); - //if (System.IO.File.Exists(library_json)) - //{ - // items = AudibleApi.Common.Converter.FromJson>(System.IO.File.ReadAllText(library_json)); - //} -#endif - - Serilog.Log.Logger.Debug("Begin initial library scan"); - - if (!items.Any()) - items = await Api.GetAllLibraryItemsAsync(libraryOptions); - - Serilog.Log.Logger.Debug("Initial library scan complete. Begin episode scan"); - - await manageEpisodesAsync(items, importEpisodes); - - Serilog.Log.Logger.Debug("Episode scan complete"); - #if DEBUG //System.IO.File.WriteAllText(library_json, AudibleApi.Common.Converter.ToJson(items)); #endif @@ -225,63 +157,23 @@ namespace AudibleUtilities } #region episodes and podcasts - private async Task manageEpisodesAsync(List items, bool importEpisodes) + + private async Task> getChildEpisodesAsync(Item parent) { - // add podcasts and episodes to list. If fail, don't let it de-rail the rest of the import - try + 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 }; + + foreach (var child in children) { - // get parents - var parents = items.Where(i => i.IsEpisodes).ToList(); -#if DEBUG -//var parentsDebug = parents.Select(i => i.ToJson()).Aggregate((a, b) => $"{a}\r\n\r\n{b}"); -//System.IO.File.WriteAllText("parents.json", parentsDebug); -#endif - - if (!parents.Any()) - return; - - Serilog.Log.Logger.Information($"{parents.Count} series of shows/podcasts found"); - - // remove episode parents. even if the following stuff fails, these will still be removed from the collection - items.RemoveAll(i => i.IsEpisodes); - - if (importEpisodes) + // use parent's 'DateAdded'. DateAdded is just a convenience prop for: PurchaseDate.UtcDateTime + child.PurchaseDate = parent.PurchaseDate; + // parent is essentially a series + child.Series = new Series[] { - // add children - var children = await getEpisodesAsync(parents); - Serilog.Log.Logger.Information($"{children.Count} episodes of shows/podcasts found"); - items.AddRange(children); - } - } - catch (Exception ex) - { - Serilog.Log.Logger.Error(ex, "Error adding podcasts and episodes"); - } - } - - private async Task> getEpisodesAsync(List parents) - { - var results = new List(); - - foreach (var parent in parents) - { - 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()) - { - results.Add(parent); - continue; - } - - foreach (var child in children) - { - // use parent's 'DateAdded'. DateAdded is just a convenience prop for: PurchaseDate.UtcDateTime - child.PurchaseDate = parent.PurchaseDate; - // parent is essentially a series - child.Series = new Series[] - { new Series { Asin = parent.Asin, @@ -289,22 +181,18 @@ namespace AudibleUtilities Sequence = parent.Relationships.FirstOrDefault(r => r.Asin == child.Asin).Sort.ToString(), Title = parent.TitleWithSubtitle } - }; - // overload (read: abuse) IsEpisodes flag - child.Relationships = new Relationship[] - { + }; + // overload (read: abuse) IsEpisodes flag + child.Relationships = new Relationship[] + { new Relationship { RelationshipToProduct = RelationshipToProduct.Child, RelationshipType = RelationshipType.Episode } - }; - } - - results.AddRange(children); + }; } - - return results; + return children; } private async Task> getEpisodeChildrenAsync(Item parent) From 562496cfaa5fca2a72daf032303064a4e627b857 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 24 May 2022 21:36:56 -0600 Subject: [PATCH 7/8] Add more logging --- Source/AudibleUtilities/ApiExtended.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index aa2625e9..116ac1b9 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -119,7 +119,7 @@ namespace AudibleUtilities { var items = new List(); - Serilog.Log.Logger.Debug("Begin initial library scan"); + Serilog.Log.Logger.Debug("Begin library scan"); List>> getChildEpisodesTasks = new(); @@ -134,7 +134,9 @@ namespace AudibleUtilities items.Add(item); } - //asait and all all episides from all parents + Serilog.Log.Logger.Debug("Library scan complete. Waiting on episode scans to complete"); + + //await and add all episides from all parents foreach (var epList in await Task.WhenAll(getChildEpisodesTasks)) items.AddRange(epList); @@ -160,6 +162,8 @@ namespace AudibleUtilities private async Task> getChildEpisodesAsync(Item parent) { + Serilog.Log.Logger.Debug("Beginning episode scan for {parent}", parent); + var children = await getEpisodeChildrenAsync(parent); // actual individual episode, not the parent of a series. @@ -234,7 +238,7 @@ namespace AudibleUtilities throw; } - Serilog.Log.Logger.Debug($"Batch {i}: {childrenBatch.Count} results"); + Serilog.Log.Logger.Debug($"Batch {i}: {childrenBatch.Count} results\t({{parent}})", parent); // the service returned no results. probably indicates an error. stop running batches if (!childrenBatch.Any()) break; @@ -252,7 +256,7 @@ namespace AudibleUtilities if (childrenIds.Count != results.Count) { var ex = new ApplicationException($"Mis-match: Children defined by parent={childrenIds.Count}. Children returned by batches={results.Count}"); - Serilog.Log.Logger.Error(ex, "Quantity of series episodes defined by parent does not match quantity returned by batch fetching."); + Serilog.Log.Logger.Error(ex, "{parent} - Quantity of series episodes defined by parent does not match quantity returned by batch fetching.", parent); throw ex; } From 2b15bc6ebb6ff55dd8a5ceb0c3b7e28394523ced Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Wed, 25 May 2022 15:11:38 -0600 Subject: [PATCH 8/8] Count Items as they come in and log total. --- Source/AudibleUtilities/ApiExtended.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/AudibleUtilities/ApiExtended.cs b/Source/AudibleUtilities/ApiExtended.cs index 116ac1b9..11ff3c39 100644 --- a/Source/AudibleUtilities/ApiExtended.cs +++ b/Source/AudibleUtilities/ApiExtended.cs @@ -123,6 +123,8 @@ namespace AudibleUtilities List>> getChildEpisodesTasks = new(); + int count = 0; + await foreach (var item in Api.GetLibraryItemAsyncEnumerable(libraryOptions)) { if (item.IsEpisodes && importEpisodes) @@ -132,9 +134,11 @@ namespace AudibleUtilities } else if (!item.IsEpisodes) items.Add(item); + + count++; } - Serilog.Log.Logger.Debug("Library scan complete. Waiting on episode scans to complete"); + Serilog.Log.Logger.Debug("Library scan complete. Found {count} books. Waiting on episode scans to complete", count); //await and add all episides from all parents foreach (var epList in await Task.WhenAll(getChildEpisodesTasks)) @@ -145,7 +149,6 @@ namespace AudibleUtilities #if DEBUG //System.IO.File.WriteAllText(library_json, AudibleApi.Common.Converter.ToJson(items)); #endif - var validators = new List(); validators.AddRange(getValidators()); foreach (var v in validators)