using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using AudibleApi; using AudibleApiDTOs; using Dinah.Core; using Polly; using Polly.Retry; namespace InternalUtilities { public static class AudibleApiActions { /// USE THIS from within Libation. It wraps the call with correct JSONPath public static Task GetApiAsync(string username, string localeName, ILoginCallback loginCallback = null) { Serilog.Log.Logger.Information("GetApiAsync. {@DebugInfo}", new { Username = username.ToMask(), LocaleName = localeName, }); return EzApiCreator.GetApiAsync( Localization.Get(localeName), AudibleApiStorage.AccountsSettingsFile, AudibleApiStorage.GetIdentityTokensJsonPath(username, localeName), loginCallback); } /// USE THIS from within Libation. It wraps the call with correct JSONPath public static Task GetApiAsync(ILoginCallback loginCallback, Account account) { Serilog.Log.Logger.Information("GetApiAsync. {@DebugInfo}", new { Account = account?.MaskedLogEntry ?? "[null]", LocaleName = account?.Locale?.Name }); return EzApiCreator.GetApiAsync( account.Locale, AudibleApiStorage.AccountsSettingsFile, account.GetIdentityTokensJsonPath(), loginCallback); } private static AsyncRetryPolicy policy { get; } = Policy.Handle() // 2 retries == 3 total .RetryAsync(2); public static Task> GetLibraryValidatedAsync(Api api) { // 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)); } private static async Task> getItemsAsync(Api api) { var items = await api.GetAllLibraryItemsAsync(); // remove episode parents items.RemoveAll(i => i.IsEpisodes); #region // episode handling. doesn't quite work // // add individual/children episodes // var childIds = items // .Where(i => i.Episodes) // .SelectMany(ep => ep.Relationships) // .Where(r => r.RelationshipToProduct == AudibleApiDTOs.RelationshipToProduct.Child && r.RelationshipType == AudibleApiDTOs.RelationshipType.Episode) // .Select(c => c.Asin) // .ToList(); // foreach (var childId in childIds) // { // var bookResult = await api.GetLibraryBookAsync(childId, AudibleApi.LibraryOptions.ResponseGroupOptions.ALL_OPTIONS); // var bookItem = AudibleApiDTOs.LibraryDtoV10.FromJson(bookResult.ToString()).Item; // items.Add(bookItem); // } #endregion var validators = new List(); validators.AddRange(getValidators()); foreach (var v in validators) { var exceptions = v.Validate(items); if (exceptions != null && exceptions.Any()) throw new AggregateException(exceptions); } return items; } private static List getValidators() { var type = typeof(IValidator); var types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(s => s.GetTypes()) .Where(p => type.IsAssignableFrom(p) && !p.IsInterface); return types.Select(t => Activator.CreateInstance(t) as IValidator).ToList(); } } }