Serialize getting API instances so that logins don't conflict
This commit is contained in:
parent
60f1d8117d
commit
a12391f0ab
@ -41,13 +41,23 @@ namespace ApplicationServices
|
|||||||
|
|
||||||
private static async Task<List<ImportItem>> scanAccountsAsync(ILoginCallback callback, Account[] accounts)
|
private static async Task<List<ImportItem>> scanAccountsAsync(ILoginCallback callback, Account[] accounts)
|
||||||
{
|
{
|
||||||
var tasks = accounts.Select(account => scanAccountAsync(callback, account)).ToList();
|
var tasks = new List<Task<List<ImportItem>>>();
|
||||||
|
foreach (var account in accounts)
|
||||||
|
{
|
||||||
|
// get APIs in serial, esp b/c of logins
|
||||||
|
var api = await AudibleApiActions.GetApiAsync(callback, account);
|
||||||
|
|
||||||
|
// add scanAccountAsync as a TASK: do not await
|
||||||
|
tasks.Add(scanAccountAsync(api, account));
|
||||||
|
}
|
||||||
|
|
||||||
|
// import library in parallel
|
||||||
var arrayOfLists = await Task.WhenAll(tasks);
|
var arrayOfLists = await Task.WhenAll(tasks);
|
||||||
var importItems = arrayOfLists.SelectMany(a => a).ToList();
|
var importItems = arrayOfLists.SelectMany(a => a).ToList();
|
||||||
return importItems;
|
return importItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<List<ImportItem>> scanAccountAsync(ILoginCallback callback, Account account)
|
private static async Task<List<ImportItem>> scanAccountAsync(Api api, Account account)
|
||||||
{
|
{
|
||||||
Dinah.Core.ArgumentValidator.EnsureNotNull(account, nameof(account));
|
Dinah.Core.ArgumentValidator.EnsureNotNull(account, nameof(account));
|
||||||
|
|
||||||
@ -59,7 +69,7 @@ namespace ApplicationServices
|
|||||||
LocaleName = localeName,
|
LocaleName = localeName,
|
||||||
});
|
});
|
||||||
|
|
||||||
var dtoItems = await AudibleApiActions.GetAllLibraryItemsAsync(account, callback);
|
var dtoItems = await AudibleApiActions.GetLibraryValidatedAsync(api);
|
||||||
return dtoItems.Select(d => new ImportItem { DtoItem = d, AccountId = account.AccountId, LocaleName = localeName }).ToList();
|
return dtoItems.Select(d => new ImportItem { DtoItem = d, AccountId = account.AccountId, LocaleName = localeName }).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@ namespace InternalUtilities
|
|||||||
loginCallback);
|
loginCallback);
|
||||||
|
|
||||||
/// <summary>USE THIS from within Libation. It wraps the call with correct JSONPath</summary>
|
/// <summary>USE THIS from within Libation. It wraps the call with correct JSONPath</summary>
|
||||||
public static Task<Api> GetApiAsync(Account account, ILoginCallback loginCallback = null)
|
public static Task<Api> GetApiAsync(ILoginCallback loginCallback, Account account)
|
||||||
=> EzApiCreator.GetApiAsync(
|
=> EzApiCreator.GetApiAsync(
|
||||||
account.Locale,
|
account.Locale,
|
||||||
AudibleApiStorage.AccountsSettingsFile,
|
AudibleApiStorage.AccountsSettingsFile,
|
||||||
@ -32,18 +32,17 @@ namespace InternalUtilities
|
|||||||
// 2 retries == 3 total
|
// 2 retries == 3 total
|
||||||
.RetryAsync(2);
|
.RetryAsync(2);
|
||||||
|
|
||||||
public static Task<List<Item>> GetAllLibraryItemsAsync(Account account, ILoginCallback callback)
|
public static Task<List<Item>> 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
|
// 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:
|
// 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 });
|
// 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
|
// i don't want to incur the cost of making a full dummy call every time because it fails sometimes
|
||||||
return policy.ExecuteAsync(() => getItemsAsync(account, callback));
|
return policy.ExecuteAsync(() => getItemsAsync(api));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<List<Item>> getItemsAsync(Account account, ILoginCallback callback)
|
private static async Task<List<Item>> getItemsAsync(Api api)
|
||||||
{
|
{
|
||||||
var api = await GetApiAsync(account, callback);
|
|
||||||
var items = await api.GetAllLibraryItemsAsync();
|
var items = await api.GetAllLibraryItemsAsync();
|
||||||
|
|
||||||
// remove episode parents and 'audible plus' check-outs
|
// remove episode parents and 'audible plus' check-outs
|
||||||
|
|||||||
@ -1,54 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using AudibleApi;
|
|
||||||
using AudibleApiDTOs;
|
|
||||||
|
|
||||||
//
|
|
||||||
// probably not the best place for this
|
|
||||||
// but good enough for now
|
|
||||||
//
|
|
||||||
namespace InternalUtilities
|
|
||||||
{
|
|
||||||
public static class AudibleApiExtensions
|
|
||||||
{
|
|
||||||
public static async Task<List<Item>> GetAllLibraryItemsAsync(this Api api)
|
|
||||||
{
|
|
||||||
var allItems = new List<Item>();
|
|
||||||
|
|
||||||
for (var i = 1; ; i++)
|
|
||||||
{
|
|
||||||
var page = await api.GetLibraryAsync(new LibraryOptions
|
|
||||||
{
|
|
||||||
NumberOfResultPerPage = 1000,
|
|
||||||
PageNumber = i,
|
|
||||||
PurchasedAfter = new DateTime(2000, 1, 1),
|
|
||||||
ResponseGroups = LibraryOptions.ResponseGroupOptions.ALL_OPTIONS
|
|
||||||
});
|
|
||||||
|
|
||||||
var pageStr = page.ToString();
|
|
||||||
|
|
||||||
LibraryDtoV10 libResult;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// important! use this convert method
|
|
||||||
libResult = LibraryDtoV10.FromJson(pageStr);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Serilog.Log.Logger.Error(ex, "Error converting library for importing use. Full library:\r\n" + pageStr);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!libResult.Items.Any())
|
|
||||||
break;
|
|
||||||
|
|
||||||
Serilog.Log.Logger.Information($"Page {i}: {libResult.Items.Length} results");
|
|
||||||
allItems.AddRange(libResult.Items);
|
|
||||||
}
|
|
||||||
|
|
||||||
return allItems;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -17,7 +17,9 @@ namespace LibationWinForms.Dialogs.Login
|
|||||||
{
|
{
|
||||||
Email = this.emailTb.Text;
|
Email = this.emailTb.Text;
|
||||||
Password = this.passwordTb.Text;
|
Password = this.passwordTb.Text;
|
||||||
|
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
|
// Close() not needed for AcceptButton
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,7 +25,9 @@ namespace LibationWinForms.Dialogs.Login
|
|||||||
private void submitBtn_Click(object sender, EventArgs e)
|
private void submitBtn_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Answer = this.answerTb.Text;
|
Answer = this.answerTb.Text;
|
||||||
|
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
|
// Close() not needed for AcceptButton
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user