From 35f54779f0b82f657d728e81c20eaad9d3e208ef Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Mon, 9 Aug 2021 01:08:00 -0600 Subject: [PATCH] Add library command to get books removed from library. --- ApplicationServices/LibraryCommands.cs | 53 +++++++++++++++++++++++++- InternalUtilities/AudibleApiActions.cs | 8 ++-- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/ApplicationServices/LibraryCommands.cs b/ApplicationServices/LibraryCommands.cs index 97bdae3e..4672b942 100644 --- a/ApplicationServices/LibraryCommands.cs +++ b/ApplicationServices/LibraryCommands.cs @@ -20,6 +20,57 @@ namespace ApplicationServices public static class LibraryCommands { + private static LibraryOptions.ResponseGroupOptions LibraryResponseGroups = LibraryOptions.ResponseGroupOptions.ALL_OPTIONS; + + public static async Task> FindInactiveBooks(Func loginCallbackFactoryFunc, List existingLibrary, params Account[] accounts) + { + //These are the minimum response groups required for the + //library scanner to pass all validation and filtering. + LibraryResponseGroups = + LibraryOptions.ResponseGroupOptions.ProductAttrs | + LibraryOptions.ResponseGroupOptions.ProductDesc | + LibraryOptions.ResponseGroupOptions.Relationships; + + if (accounts is null || accounts.Length == 0) + return new List(); + + try + { + var libraryItems = await scanAccountsAsync(loginCallbackFactoryFunc, accounts); + Log.Logger.Information($"GetAllLibraryItems: Total count {libraryItems.Count}"); + + var missingBookList = existingLibrary.Where(b => libraryItems.Count(i => i.DtoItem.Asin == b.Book.AudibleProductId) == 0).ToList(); + + return missingBookList; + } + catch (AudibleApi.Authentication.LoginFailedException lfEx) + { + lfEx.SaveFiles(FileManager.Configuration.Instance.LibationFiles); + + // nuget Serilog.Exceptions would automatically log custom properties + // However, it comes with a scary warning when used with EntityFrameworkCore which I'm not yet ready to implement: + // https://github.com/RehanSaeed/Serilog.Exceptions + // work-around: use 3rd param. don't just put exception object in 3rd param -- info overload: stack trace, etc + Log.Logger.Error(lfEx, "Error scanning library. Login failed. {@DebugInfo}", new + { + lfEx.RequestUrl, + ResponseStatusCodeNumber = (int)lfEx.ResponseStatusCode, + ResponseStatusCodeDesc = lfEx.ResponseStatusCode, + lfEx.ResponseInputFields, + lfEx.ResponseBodyFilePaths + }); + throw; + } + catch (Exception ex) + { + Log.Logger.Error(ex, "Error importing library"); + throw; + } + finally + { + LibraryResponseGroups = LibraryOptions.ResponseGroupOptions.ALL_OPTIONS; + } + } #region FULL LIBRARY scan and import public static async Task<(int totalCount, int newCount)> ImportAccountAsync(Func loginCallbackFactoryFunc, params Account[] accounts) { @@ -95,7 +146,7 @@ namespace ApplicationServices Account = account?.MaskedLogEntry ?? "[null]" }); - var dtoItems = await AudibleApiActions.GetLibraryValidatedAsync(api); + var dtoItems = await AudibleApiActions.GetLibraryValidatedAsync(api, LibraryResponseGroups); return dtoItems.Select(d => new ImportItem { DtoItem = d, AccountId = account.AccountId, LocaleName = account.Locale?.Name }).ToList(); } diff --git a/InternalUtilities/AudibleApiActions.cs b/InternalUtilities/AudibleApiActions.cs index 9cbe58ee..0d1e2edd 100644 --- a/InternalUtilities/AudibleApiActions.cs +++ b/InternalUtilities/AudibleApiActions.cs @@ -47,18 +47,18 @@ namespace InternalUtilities // 2 retries == 3 total .RetryAsync(2); - public static Task> GetLibraryValidatedAsync(Api api) + public static Task> GetLibraryValidatedAsync(Api api, LibraryOptions.ResponseGroupOptions responseGroups = LibraryOptions.ResponseGroupOptions.ALL_OPTIONS) { // bug on audible's side. the 1st time after a long absence, a query to get library will return without titles or authors. a subsequent identical query will be successful. this is true whether or tokens are refreshed // worse, this 1st dummy call doesn't seem to help: // var page = await api.GetLibraryAsync(new AudibleApi.LibraryOptions { NumberOfResultPerPage = 1, PageNumber = 1, PurchasedAfter = DateTime.Now.AddYears(-20), ResponseGroups = AudibleApi.LibraryOptions.ResponseGroupOptions.ALL_OPTIONS }); // i don't want to incur the cost of making a full dummy call every time because it fails sometimes - return policy.ExecuteAsync(() => getItemsAsync(api)); + return policy.ExecuteAsync(() => getItemsAsync(api, responseGroups)); } - private static async Task> getItemsAsync(Api api) + private static async Task> getItemsAsync(Api api, LibraryOptions.ResponseGroupOptions responseGroups) { - var items = await api.GetAllLibraryItemsAsync(); + var items = await api.GetAllLibraryItemsAsync(responseGroups); // remove episode parents items.RemoveAll(i => i.IsEpisodes);