diff --git a/AppScaffolding/AppScaffolding.csproj b/AppScaffolding/AppScaffolding.csproj
index 4afd6076..e7dcd424 100644
--- a/AppScaffolding/AppScaffolding.csproj
+++ b/AppScaffolding/AppScaffolding.csproj
@@ -3,7 +3,7 @@
net5.0
- 6.0.0.2
+ 6.0.1.1
diff --git a/ApplicationServices/LibraryCommands.cs b/ApplicationServices/LibraryCommands.cs
index 5422f987..75ae93f1 100644
--- a/ApplicationServices/LibraryCommands.cs
+++ b/ApplicationServices/LibraryCommands.cs
@@ -16,7 +16,7 @@ namespace ApplicationServices
{
private static LibraryOptions.ResponseGroupOptions LibraryResponseGroups = LibraryOptions.ResponseGroupOptions.ALL_OPTIONS;
- public static async Task> FindInactiveBooks(Func loginCallbackFactoryFunc, List existingLibrary, params Account[] accounts)
+ public static async Task> FindInactiveBooks(Func> apiExtendedfunc, List existingLibrary, params Account[] accounts)
{
logRestart();
@@ -33,7 +33,7 @@ namespace ApplicationServices
try
{
logTime($"pre {nameof(scanAccountsAsync)} all");
- var libraryItems = await scanAccountsAsync(loginCallbackFactoryFunc, accounts);
+ var libraryItems = await scanAccountsAsync(apiExtendedfunc, accounts);
logTime($"post {nameof(scanAccountsAsync)} all");
var totalCount = libraryItems.Count;
@@ -75,7 +75,7 @@ namespace ApplicationServices
}
#region FULL LIBRARY scan and import
- public static async Task<(int totalCount, int newCount)> ImportAccountAsync(Func loginCallbackFactoryFunc, params Account[] accounts)
+ public static async Task<(int totalCount, int newCount)> ImportAccountAsync(Func> apiExtendedfunc, params Account[] accounts)
{
logRestart();
@@ -85,7 +85,7 @@ namespace ApplicationServices
try
{
logTime($"pre {nameof(scanAccountsAsync)} all");
- var importItems = await scanAccountsAsync(loginCallbackFactoryFunc, accounts);
+ var importItems = await scanAccountsAsync(apiExtendedfunc, accounts);
logTime($"post {nameof(scanAccountsAsync)} all");
var totalCount = importItems.Count;
@@ -129,15 +129,13 @@ namespace ApplicationServices
}
}
- private static async Task> scanAccountsAsync(Func loginCallbackFactoryFunc, Account[] accounts)
+ private static async Task> scanAccountsAsync(Func> apiExtendedfunc, Account[] accounts)
{
var tasks = new List>>();
foreach (var account in accounts)
{
- var callback = loginCallbackFactoryFunc(account);
-
- // get APIs in serial b/c of logins
- var apiExtended = await ApiExtended.CreateAsync(callback, account);
+ // get APIs in serial b/c of logins. do NOT move inside of parallel (Task.WhenAll)
+ var apiExtended = await apiExtendedfunc(account);
// add scanAccountAsync as a TASK: do not await
tasks.Add(scanAccountAsync(apiExtended, account));
diff --git a/InternalUtilities/ApiExtended.cs b/InternalUtilities/ApiExtended.cs
index 66d4d7ce..382afabb 100644
--- a/InternalUtilities/ApiExtended.cs
+++ b/InternalUtilities/ApiExtended.cs
@@ -17,26 +17,28 @@ namespace InternalUtilities
private ApiExtended(Api api) => Api = api;
- public static async Task CreateAsync(ILoginChoice loginChoice, Account account)
+ /// Get api from existing tokens else login with 'eager' choice. External browser url is provided. Response can be external browser login or continuing with native api callbacks.
+ public static async Task CreateAsync(Account account, ILoginChoiceEager loginChoiceEager)
{
- Serilog.Log.Logger.Information("GetApiAsync. {@DebugInfo}", new
+ Serilog.Log.Logger.Information("{@DebugInfo}", new
{
- LoginType = nameof(ILoginChoice),
+ LoginType = nameof(ILoginChoiceEager),
Account = account?.MaskedLogEntry ?? "[null]",
LocaleName = account?.Locale?.Name
});
var api = await EzApiCreator.GetApiAsync(
+ loginChoiceEager,
account.Locale,
AudibleApiStorage.AccountsSettingsFile,
- loginChoice,
account.GetIdentityTokensJsonPath());
return new ApiExtended(api);
}
- public static async Task CreateAsync(ILoginCallback loginCallback, Account account)
+ /// Get api from existing tokens else login with native api callbacks.
+ public static async Task CreateAsync(Account account, ILoginCallback loginCallback)
{
- Serilog.Log.Logger.Information("GetApiAsync ILoginCallback. {@DebugInfo}", new
+ Serilog.Log.Logger.Information("{@DebugInfo}", new
{
LoginType = nameof(ILoginCallback),
Account = account?.MaskedLogEntry ?? "[null]",
@@ -44,16 +46,17 @@ namespace InternalUtilities
});
var api = await EzApiCreator.GetApiAsync(
+ loginCallback,
account.Locale,
AudibleApiStorage.AccountsSettingsFile,
- loginCallback,
account.GetIdentityTokensJsonPath());
return new ApiExtended(api);
}
- public static async Task CreateAsync(ILoginExternal loginExternal, Account account)
+ /// Get api from existing tokens else login with external browser
+ public static async Task CreateAsync(Account account, ILoginExternal loginExternal)
{
- Serilog.Log.Logger.Information("GetApiAsync ILoginExternal. {@DebugInfo}", new
+ Serilog.Log.Logger.Information("{@DebugInfo}", new
{
LoginType = nameof(ILoginExternal),
Account = account?.MaskedLogEntry ?? "[null]",
@@ -61,16 +64,31 @@ namespace InternalUtilities
});
var api = await EzApiCreator.GetApiAsync(
+ loginExternal,
account.Locale,
AudibleApiStorage.AccountsSettingsFile,
- loginExternal,
account.GetIdentityTokensJsonPath());
return new ApiExtended(api);
}
+ /// Get api from existing tokens. Assumes you have valid login tokens. Else exception
+ public static async Task CreateAsync(Account account)
+ {
+ ArgumentValidator.EnsureNotNull(account, nameof(account));
+ ArgumentValidator.EnsureNotNull(account.Locale, nameof(account.Locale));
+
+ Serilog.Log.Logger.Information("{@DebugInfo}", new
+ {
+ AccountMaskedLogEntry = account.MaskedLogEntry
+ });
+
+ return await CreateAsync(account.AccountId, account.Locale.Name);
+ }
+
+ /// Get api from existing tokens. Assumes you have valid login tokens. Else exception
public static async Task CreateAsync(string username, string localeName)
{
- Serilog.Log.Logger.Information("GetApiAsync. {@DebugInfo}", new
+ Serilog.Log.Logger.Information("{@DebugInfo}", new
{
Username = username.ToMask(),
LocaleName = localeName,
diff --git a/InternalUtilities/InternalUtilities.csproj b/InternalUtilities/InternalUtilities.csproj
index eddf21f6..bcdc48ef 100644
--- a/InternalUtilities/InternalUtilities.csproj
+++ b/InternalUtilities/InternalUtilities.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/LibationCli/Options/ScanOptions.cs b/LibationCli/Options/ScanOptions.cs
index 9c9aa825..87daca99 100644
--- a/LibationCli/Options/ScanOptions.cs
+++ b/LibationCli/Options/ScanOptions.cs
@@ -32,9 +32,7 @@ namespace LibationCli
: $"Scanning Audible library: {_accounts.Length} accounts. This may take a few minutes per account.";
Console.WriteLine(intro);
- var (TotalBooksProcessed, NewBooksAdded) = await LibraryCommands.ImportAccountAsync(
- (account) => null,
- _accounts);
+ var (TotalBooksProcessed, NewBooksAdded) = await LibraryCommands.ImportAccountAsync((a) => ApiExtended.CreateAsync(a), _accounts);
Console.WriteLine("Scan complete.");
Console.WriteLine($"Total processed: {TotalBooksProcessed}\r\nNew: {NewBooksAdded}");
diff --git a/LibationCli/Program.cs b/LibationCli/Program.cs
index 6f85c5a6..2681d195 100644
--- a/LibationCli/Program.cs
+++ b/LibationCli/Program.cs
@@ -35,7 +35,7 @@ namespace LibationCli
string input = null;
//input = " export --help";
- //input = " scan cupidneedsglasses";
+ //input = " scan rmcrackan";
//input = " liberate ";
diff --git a/LibationWinForms/Dialogs/IndexLibraryDialog.cs b/LibationWinForms/Dialogs/IndexLibraryDialog.cs
index 1adb538a..ba4e4342 100644
--- a/LibationWinForms/Dialogs/IndexLibraryDialog.cs
+++ b/LibationWinForms/Dialogs/IndexLibraryDialog.cs
@@ -32,7 +32,7 @@ namespace LibationWinForms.Dialogs
try
{
- (TotalBooksProcessed, NewBooksAdded) = await LibraryCommands.ImportAccountAsync((account) => new WinformResponder(account), _accounts);
+ (TotalBooksProcessed, NewBooksAdded) = await LibraryCommands.ImportAccountAsync((account) => ApiExtended.CreateAsync(account, new WinformLoginChoiceEager(account)), _accounts);
}
catch (Exception ex)
{
diff --git a/LibationWinForms/Dialogs/Login/AudibleLoginDialog.Designer.cs b/LibationWinForms/Dialogs/Login/LoginCallbackDialog.Designer.cs
similarity index 67%
rename from LibationWinForms/Dialogs/Login/AudibleLoginDialog.Designer.cs
rename to LibationWinForms/Dialogs/Login/LoginCallbackDialog.Designer.cs
index e725f483..558cd626 100644
--- a/LibationWinForms/Dialogs/Login/AudibleLoginDialog.Designer.cs
+++ b/LibationWinForms/Dialogs/Login/LoginCallbackDialog.Designer.cs
@@ -1,6 +1,6 @@
namespace LibationWinForms.Dialogs.Login
{
- partial class AudibleLoginDialog
+ partial class LoginCallbackDialog
{
///
/// Required designer variable.
@@ -38,25 +38,29 @@
// passwordLbl
//
this.passwordLbl.AutoSize = true;
- this.passwordLbl.Location = new System.Drawing.Point(12, 41);
+ this.passwordLbl.Location = new System.Drawing.Point(14, 47);
+ this.passwordLbl.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.passwordLbl.Name = "passwordLbl";
- this.passwordLbl.Size = new System.Drawing.Size(53, 13);
+ this.passwordLbl.Size = new System.Drawing.Size(57, 15);
this.passwordLbl.TabIndex = 2;
this.passwordLbl.Text = "Password";
//
// passwordTb
//
- this.passwordTb.Location = new System.Drawing.Point(71, 38);
+ this.passwordTb.Location = new System.Drawing.Point(83, 44);
+ this.passwordTb.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.passwordTb.Name = "passwordTb";
this.passwordTb.PasswordChar = '*';
- this.passwordTb.Size = new System.Drawing.Size(200, 20);
+ this.passwordTb.Size = new System.Drawing.Size(233, 23);
this.passwordTb.TabIndex = 3;
//
// submitBtn
//
- this.submitBtn.Location = new System.Drawing.Point(196, 64);
+ this.submitBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.submitBtn.Location = new System.Drawing.Point(229, 74);
+ this.submitBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.submitBtn.Name = "submitBtn";
- this.submitBtn.Size = new System.Drawing.Size(75, 23);
+ this.submitBtn.Size = new System.Drawing.Size(88, 27);
this.submitBtn.TabIndex = 4;
this.submitBtn.Text = "Submit";
this.submitBtn.UseVisualStyleBackColor = true;
@@ -65,36 +69,39 @@
// localeLbl
//
this.localeLbl.AutoSize = true;
- this.localeLbl.Location = new System.Drawing.Point(12, 9);
+ this.localeLbl.Location = new System.Drawing.Point(14, 10);
+ this.localeLbl.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.localeLbl.Name = "localeLbl";
- this.localeLbl.Size = new System.Drawing.Size(59, 13);
+ this.localeLbl.Size = new System.Drawing.Size(61, 15);
this.localeLbl.TabIndex = 0;
this.localeLbl.Text = "Locale: {0}";
//
// usernameLbl
//
this.usernameLbl.AutoSize = true;
- this.usernameLbl.Location = new System.Drawing.Point(12, 22);
+ this.usernameLbl.Location = new System.Drawing.Point(14, 25);
+ this.usernameLbl.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.usernameLbl.Name = "usernameLbl";
- this.usernameLbl.Size = new System.Drawing.Size(75, 13);
+ this.usernameLbl.Size = new System.Drawing.Size(80, 15);
this.usernameLbl.TabIndex = 1;
this.usernameLbl.Text = "Username: {0}";
//
- // AudibleLoginDialog
+ // LoginCallbackDialog
//
this.AcceptButton = this.submitBtn;
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(283, 99);
+ this.ClientSize = new System.Drawing.Size(330, 114);
this.Controls.Add(this.usernameLbl);
this.Controls.Add(this.localeLbl);
this.Controls.Add(this.submitBtn);
this.Controls.Add(this.passwordLbl);
this.Controls.Add(this.passwordTb);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.MaximizeBox = false;
this.MinimizeBox = false;
- this.Name = "AudibleLoginDialog";
+ this.Name = "LoginCallbackDialog";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Audible Login";
diff --git a/LibationWinForms/Dialogs/Login/AudibleLoginDialog.cs b/LibationWinForms/Dialogs/Login/LoginCallbackDialog.cs
similarity index 74%
rename from LibationWinForms/Dialogs/Login/AudibleLoginDialog.cs
rename to LibationWinForms/Dialogs/Login/LoginCallbackDialog.cs
index 2e366e88..93d2b043 100644
--- a/LibationWinForms/Dialogs/Login/AudibleLoginDialog.cs
+++ b/LibationWinForms/Dialogs/Login/LoginCallbackDialog.cs
@@ -1,28 +1,25 @@
-using Dinah.Core;
-using InternalUtilities;
-using System;
-using System.Linq;
+using System;
using System.Windows.Forms;
+using Dinah.Core;
+using InternalUtilities;
namespace LibationWinForms.Dialogs.Login
{
- public partial class AudibleLoginDialog : Form
+ public partial class LoginCallbackDialog : Form
{
- private string locale { get; }
private string accountId { get; }
public string Email { get; private set; }
public string Password { get; private set; }
- public AudibleLoginDialog(Account account)
+ public LoginCallbackDialog(Account account)
{
InitializeComponent();
- locale = account.Locale.Name;
accountId = account.AccountId;
// do not allow user to change login id here. if they do then jsonpath will fail
- this.localeLbl.Text = string.Format(this.localeLbl.Text, locale);
+ this.localeLbl.Text = string.Format(this.localeLbl.Text, account.Locale.Name);
this.usernameLbl.Text = string.Format(this.usernameLbl.Text, accountId);
}
diff --git a/LibationWinForms/Dialogs/Login/AudibleLoginDialog.resx b/LibationWinForms/Dialogs/Login/LoginCallbackDialog.resx
similarity index 98%
rename from LibationWinForms/Dialogs/Login/AudibleLoginDialog.resx
rename to LibationWinForms/Dialogs/Login/LoginCallbackDialog.resx
index e8ae276d..f298a7be 100644
--- a/LibationWinForms/Dialogs/Login/AudibleLoginDialog.resx
+++ b/LibationWinForms/Dialogs/Login/LoginCallbackDialog.resx
@@ -1,5 +1,4 @@
-
-
+
diff --git a/LibationWinForms/Dialogs/Login/LoginChoiceEagerDialog.Designer.cs b/LibationWinForms/Dialogs/Login/LoginChoiceEagerDialog.Designer.cs
new file mode 100644
index 00000000..6cda284f
--- /dev/null
+++ b/LibationWinForms/Dialogs/Login/LoginChoiceEagerDialog.Designer.cs
@@ -0,0 +1,161 @@
+namespace LibationWinForms.Dialogs.Login
+{
+ partial class LoginChoiceEagerDialog
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.passwordLbl = new System.Windows.Forms.Label();
+ this.passwordTb = new System.Windows.Forms.TextBox();
+ this.submitBtn = new System.Windows.Forms.Button();
+ this.localeLbl = new System.Windows.Forms.Label();
+ this.usernameLbl = new System.Windows.Forms.Label();
+ this.externalLoginLink = new System.Windows.Forms.LinkLabel();
+ this.externalLoginLbl2 = new System.Windows.Forms.Label();
+ this.externalLoginLbl1 = new System.Windows.Forms.Label();
+ this.SuspendLayout();
+ //
+ // passwordLbl
+ //
+ this.passwordLbl.AutoSize = true;
+ this.passwordLbl.Location = new System.Drawing.Point(14, 47);
+ this.passwordLbl.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
+ this.passwordLbl.Name = "passwordLbl";
+ this.passwordLbl.Size = new System.Drawing.Size(57, 15);
+ this.passwordLbl.TabIndex = 2;
+ this.passwordLbl.Text = "Password";
+ //
+ // passwordTb
+ //
+ this.passwordTb.Location = new System.Drawing.Point(83, 44);
+ this.passwordTb.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ this.passwordTb.Name = "passwordTb";
+ this.passwordTb.PasswordChar = '*';
+ this.passwordTb.Size = new System.Drawing.Size(233, 23);
+ this.passwordTb.TabIndex = 3;
+ //
+ // submitBtn
+ //
+ this.submitBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.submitBtn.Location = new System.Drawing.Point(293, 176);
+ this.submitBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ this.submitBtn.Name = "submitBtn";
+ this.submitBtn.Size = new System.Drawing.Size(88, 27);
+ this.submitBtn.TabIndex = 7;
+ this.submitBtn.Text = "Submit";
+ this.submitBtn.UseVisualStyleBackColor = true;
+ this.submitBtn.Click += new System.EventHandler(this.submitBtn_Click);
+ //
+ // localeLbl
+ //
+ this.localeLbl.AutoSize = true;
+ this.localeLbl.Location = new System.Drawing.Point(14, 10);
+ this.localeLbl.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
+ this.localeLbl.Name = "localeLbl";
+ this.localeLbl.Size = new System.Drawing.Size(61, 15);
+ this.localeLbl.TabIndex = 0;
+ this.localeLbl.Text = "Locale: {0}";
+ //
+ // usernameLbl
+ //
+ this.usernameLbl.AutoSize = true;
+ this.usernameLbl.Location = new System.Drawing.Point(14, 25);
+ this.usernameLbl.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
+ this.usernameLbl.Name = "usernameLbl";
+ this.usernameLbl.Size = new System.Drawing.Size(80, 15);
+ this.usernameLbl.TabIndex = 1;
+ this.usernameLbl.Text = "Username: {0}";
+ //
+ // externalLoginLink
+ //
+ this.externalLoginLink.AutoSize = true;
+ this.externalLoginLink.Location = new System.Drawing.Point(14, 93);
+ this.externalLoginLink.Name = "externalLoginLink";
+ this.externalLoginLink.Size = new System.Drawing.Size(107, 15);
+ this.externalLoginLink.TabIndex = 4;
+ this.externalLoginLink.TabStop = true;
+ this.externalLoginLink.Text = "Or click here to log";
+ this.externalLoginLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.externalLoginLink_LinkClicked);
+ //
+ // externalLoginLbl2
+ //
+ this.externalLoginLbl2.AutoSize = true;
+ this.externalLoginLbl2.Location = new System.Drawing.Point(14, 108);
+ this.externalLoginLbl2.Name = "externalLoginLbl2";
+ this.externalLoginLbl2.Size = new System.Drawing.Size(352, 45);
+ this.externalLoginLbl2.TabIndex = 6;
+ this.externalLoginLbl2.Text = "This more advanced login is recommended if you\'re experiencing\r\nerrors logging in" +
+ " the conventional way above or if you\'re not\r\ncomfortable typing your password h" +
+ "ere.";
+ //
+ // externalLoginLbl1
+ //
+ this.externalLoginLbl1.AutoSize = true;
+ this.externalLoginLbl1.Location = new System.Drawing.Point(83, 93);
+ this.externalLoginLbl1.Name = "externalLoginLbl1";
+ this.externalLoginLbl1.Size = new System.Drawing.Size(158, 15);
+ this.externalLoginLbl1.TabIndex = 5;
+ this.externalLoginLbl1.Text = "to log in using your browser.";
+ //
+ // LoginChoiceEagerDialog
+ //
+ this.AcceptButton = this.submitBtn;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(394, 216);
+ this.Controls.Add(this.externalLoginLbl2);
+ this.Controls.Add(this.externalLoginLbl1);
+ this.Controls.Add(this.externalLoginLink);
+ this.Controls.Add(this.usernameLbl);
+ this.Controls.Add(this.localeLbl);
+ this.Controls.Add(this.submitBtn);
+ this.Controls.Add(this.passwordLbl);
+ this.Controls.Add(this.passwordTb);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "LoginChoiceEagerDialog";
+ this.ShowIcon = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Audible Login";
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Label passwordLbl;
+ private System.Windows.Forms.TextBox passwordTb;
+ private System.Windows.Forms.Button submitBtn;
+ private System.Windows.Forms.Label localeLbl;
+ private System.Windows.Forms.Label usernameLbl;
+ private System.Windows.Forms.LinkLabel externalLoginLink;
+ private System.Windows.Forms.Label externalLoginLbl2;
+ private System.Windows.Forms.Label externalLoginLbl1;
+ }
+}
\ No newline at end of file
diff --git a/LibationWinForms/Dialogs/Login/LoginChoiceEagerDialog.cs b/LibationWinForms/Dialogs/Login/LoginChoiceEagerDialog.cs
new file mode 100644
index 00000000..827a8273
--- /dev/null
+++ b/LibationWinForms/Dialogs/Login/LoginChoiceEagerDialog.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Windows.Forms;
+using Dinah.Core;
+using InternalUtilities;
+
+namespace LibationWinForms.Dialogs.Login
+{
+ public partial class LoginChoiceEagerDialog : Form
+ {
+ private string accountId { get; }
+
+ public AudibleApi.LoginMethod LoginMethod { get; private set; }
+
+ public string Email { get; private set; }
+ public string Password { get; private set; }
+
+ public LoginChoiceEagerDialog(Account account)
+ {
+ InitializeComponent();
+
+ accountId = account.AccountId;
+
+ // do not allow user to change login id here. if they do then jsonpath will fail
+ this.localeLbl.Text = string.Format(this.localeLbl.Text, account.Locale.Name);
+ this.usernameLbl.Text = string.Format(this.usernameLbl.Text, accountId);
+ }
+
+ private void externalLoginLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+ {
+ LoginMethod = AudibleApi.LoginMethod.External;
+ DialogResult = DialogResult.OK;
+ this.Close();
+ }
+
+ private void submitBtn_Click(object sender, EventArgs e)
+ {
+ Email = accountId;
+ Password = this.passwordTb.Text;
+
+ Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { email = Email?.ToMask(), passwordLength = Password.Length });
+
+ LoginMethod = AudibleApi.LoginMethod.Api;
+ DialogResult = DialogResult.OK;
+ // Close() not needed for AcceptButton
+ }
+ }
+}
diff --git a/LibationWinForms/Dialogs/Login/LoginChoiceEagerDialog.resx b/LibationWinForms/Dialogs/Login/LoginChoiceEagerDialog.resx
new file mode 100644
index 00000000..f298a7be
--- /dev/null
+++ b/LibationWinForms/Dialogs/Login/LoginChoiceEagerDialog.resx
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/LibationWinForms/Dialogs/Login/LoginExternalDialog.Designer.cs b/LibationWinForms/Dialogs/Login/LoginExternalDialog.Designer.cs
new file mode 100644
index 00000000..e3355b42
--- /dev/null
+++ b/LibationWinForms/Dialogs/Login/LoginExternalDialog.Designer.cs
@@ -0,0 +1,179 @@
+namespace LibationWinForms.Dialogs.Login
+{
+ partial class LoginExternalDialog
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(LoginExternalDialog));
+ this.submitBtn = new System.Windows.Forms.Button();
+ this.localeLbl = new System.Windows.Forms.Label();
+ this.usernameLbl = new System.Windows.Forms.Label();
+ this.loginUrlLbl = new System.Windows.Forms.Label();
+ this.loginUrlTb = new System.Windows.Forms.TextBox();
+ this.copyBtn = new System.Windows.Forms.Button();
+ this.launchBrowserBtn = new System.Windows.Forms.Button();
+ this.instructionsLbl = new System.Windows.Forms.Label();
+ this.responseUrlTb = new System.Windows.Forms.TextBox();
+ this.SuspendLayout();
+ //
+ // submitBtn
+ //
+ this.submitBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.submitBtn.Location = new System.Drawing.Point(665, 400);
+ this.submitBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ this.submitBtn.Name = "submitBtn";
+ this.submitBtn.Size = new System.Drawing.Size(88, 27);
+ this.submitBtn.TabIndex = 8;
+ this.submitBtn.Text = "Submit";
+ this.submitBtn.UseVisualStyleBackColor = true;
+ this.submitBtn.Click += new System.EventHandler(this.submitBtn_Click);
+ //
+ // localeLbl
+ //
+ this.localeLbl.AutoSize = true;
+ this.localeLbl.Location = new System.Drawing.Point(14, 10);
+ this.localeLbl.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
+ this.localeLbl.Name = "localeLbl";
+ this.localeLbl.Size = new System.Drawing.Size(61, 15);
+ this.localeLbl.TabIndex = 0;
+ this.localeLbl.Text = "Locale: {0}";
+ //
+ // usernameLbl
+ //
+ this.usernameLbl.AutoSize = true;
+ this.usernameLbl.Location = new System.Drawing.Point(14, 25);
+ this.usernameLbl.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
+ this.usernameLbl.Name = "usernameLbl";
+ this.usernameLbl.Size = new System.Drawing.Size(80, 15);
+ this.usernameLbl.TabIndex = 1;
+ this.usernameLbl.Text = "Username: {0}";
+ //
+ // loginUrlLbl
+ //
+ this.loginUrlLbl.AutoSize = true;
+ this.loginUrlLbl.Location = new System.Drawing.Point(14, 61);
+ this.loginUrlLbl.Name = "loginUrlLbl";
+ this.loginUrlLbl.Size = new System.Drawing.Size(180, 15);
+ this.loginUrlLbl.TabIndex = 2;
+ this.loginUrlLbl.Text = "Paste this URL into your browser:";
+ //
+ // loginUrlTb
+ //
+ this.loginUrlTb.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.loginUrlTb.Location = new System.Drawing.Point(14, 79);
+ this.loginUrlTb.Multiline = true;
+ this.loginUrlTb.Name = "loginUrlTb";
+ this.loginUrlTb.ReadOnly = true;
+ this.loginUrlTb.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+ this.loginUrlTb.Size = new System.Drawing.Size(739, 92);
+ this.loginUrlTb.TabIndex = 3;
+ //
+ // copyBtn
+ //
+ this.copyBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.copyBtn.Location = new System.Drawing.Point(14, 177);
+ this.copyBtn.Name = "copyBtn";
+ this.copyBtn.Size = new System.Drawing.Size(165, 23);
+ this.copyBtn.TabIndex = 4;
+ this.copyBtn.Text = "Copy URL to clipboard";
+ this.copyBtn.UseVisualStyleBackColor = true;
+ this.copyBtn.Click += new System.EventHandler(this.copyBtn_Click);
+ //
+ // launchBrowserBtn
+ //
+ this.launchBrowserBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.launchBrowserBtn.Location = new System.Drawing.Point(588, 177);
+ this.launchBrowserBtn.Name = "launchBrowserBtn";
+ this.launchBrowserBtn.Size = new System.Drawing.Size(165, 23);
+ this.launchBrowserBtn.TabIndex = 5;
+ this.launchBrowserBtn.Text = "Launch in browser";
+ this.launchBrowserBtn.UseVisualStyleBackColor = true;
+ this.launchBrowserBtn.Click += new System.EventHandler(this.launchBrowserBtn_Click);
+ //
+ // instructionsLbl
+ //
+ this.instructionsLbl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.instructionsLbl.AutoSize = true;
+ this.instructionsLbl.Location = new System.Drawing.Point(14, 203);
+ this.instructionsLbl.Name = "instructionsLbl";
+ this.instructionsLbl.Size = new System.Drawing.Size(436, 90);
+ this.instructionsLbl.TabIndex = 6;
+ this.instructionsLbl.Text = resources.GetString("instructionsLbl.Text");
+ //
+ // responseUrlTb
+ //
+ this.responseUrlTb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.responseUrlTb.Location = new System.Drawing.Point(14, 296);
+ this.responseUrlTb.Multiline = true;
+ this.responseUrlTb.Name = "responseUrlTb";
+ this.responseUrlTb.ScrollBars = System.Windows.Forms.ScrollBars.Both;
+ this.responseUrlTb.Size = new System.Drawing.Size(739, 98);
+ this.responseUrlTb.TabIndex = 7;
+ //
+ // LoginExternalDialog
+ //
+ this.AcceptButton = this.submitBtn;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(766, 440);
+ this.Controls.Add(this.responseUrlTb);
+ this.Controls.Add(this.instructionsLbl);
+ this.Controls.Add(this.launchBrowserBtn);
+ this.Controls.Add(this.copyBtn);
+ this.Controls.Add(this.loginUrlTb);
+ this.Controls.Add(this.loginUrlLbl);
+ this.Controls.Add(this.usernameLbl);
+ this.Controls.Add(this.localeLbl);
+ this.Controls.Add(this.submitBtn);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "LoginExternalDialog";
+ this.ShowIcon = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Audible External Login";
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+ private System.Windows.Forms.Button submitBtn;
+ private System.Windows.Forms.Label localeLbl;
+ private System.Windows.Forms.Label usernameLbl;
+ private System.Windows.Forms.Label loginUrlLbl;
+ private System.Windows.Forms.TextBox loginUrlTb;
+ private System.Windows.Forms.Button copyBtn;
+ private System.Windows.Forms.Button launchBrowserBtn;
+ private System.Windows.Forms.Label instructionsLbl;
+ private System.Windows.Forms.TextBox responseUrlTb;
+ }
+}
\ No newline at end of file
diff --git a/LibationWinForms/Dialogs/Login/LoginExternalDialog.cs b/LibationWinForms/Dialogs/Login/LoginExternalDialog.cs
new file mode 100644
index 00000000..c4028ca9
--- /dev/null
+++ b/LibationWinForms/Dialogs/Login/LoginExternalDialog.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Windows.Forms;
+using Dinah.Core;
+using InternalUtilities;
+
+namespace LibationWinForms.Dialogs.Login
+{
+ public partial class LoginExternalDialog : Form
+ {
+ public string ResponseUrl { get; private set; }
+
+ public LoginExternalDialog(Account account, string loginUrl)
+ {
+ InitializeComponent();
+
+ // do not allow user to change login id here. if they do then jsonpath will fail
+ this.localeLbl.Text = string.Format(this.localeLbl.Text, account.Locale.Name);
+ this.usernameLbl.Text = string.Format(this.usernameLbl.Text, account.AccountId);
+
+ this.loginUrlTb.Text = loginUrl;
+ }
+
+ private void copyBtn_Click(object sender, EventArgs e) => Clipboard.SetText(this.loginUrlTb.Text);
+
+ private void launchBrowserBtn_Click(object sender, EventArgs e) => Go.To.Url(this.loginUrlTb.Text);
+
+ private void submitBtn_Click(object sender, EventArgs e)
+ {
+ ResponseUrl = this.responseUrlTb.Text?.Trim();
+
+ Serilog.Log.Logger.Information("Submit button clicked: {@DebugInfo}", new { ResponseUrl });
+ if (!Uri.TryCreate(ResponseUrl, UriKind.Absolute, out var result))
+ {
+ MessageBox.Show("Invalid response URL");
+ return;
+ }
+
+ DialogResult = DialogResult.OK;
+ // Close() not needed for AcceptButton
+ }
+ }
+}
diff --git a/LibationWinForms/Dialogs/Login/LoginExternalDialog.resx b/LibationWinForms/Dialogs/Login/LoginExternalDialog.resx
new file mode 100644
index 00000000..4a1293ad
--- /dev/null
+++ b/LibationWinForms/Dialogs/Login/LoginExternalDialog.resx
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Login with your Amazon/Audible credentials.
+After login is complete, your browser will show you an error page similar to:
+ Looking for Something?
+ We're sorry. The Web address you entered is not a functioning page on our site
+Don't worry -- this is ACTUALLY A SUCCESSFUL LOGIN.
+Copy the current url from your browser's address bar and paste it here:
+
+
\ No newline at end of file
diff --git a/LibationWinForms/Dialogs/Login/WinformLoginBase.cs b/LibationWinForms/Dialogs/Login/WinformLoginBase.cs
new file mode 100644
index 00000000..1de76464
--- /dev/null
+++ b/LibationWinForms/Dialogs/Login/WinformLoginBase.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace LibationWinForms.Dialogs.Login
+{
+ public abstract class WinformLoginBase
+ {
+ /// True if ShowDialog's DialogResult == OK
+ protected static bool ShowDialog(System.Windows.Forms.Form dialog)
+ {
+ var result = dialog.ShowDialog();
+ Serilog.Log.Logger.Debug("{@DebugInfo}", new { DialogResult = result });
+ return result == System.Windows.Forms.DialogResult.OK;
+ }
+ }
+}
diff --git a/LibationWinForms/Dialogs/Login/WinformResponder.cs b/LibationWinForms/Dialogs/Login/WinformLoginCallback.cs
similarity index 62%
rename from LibationWinForms/Dialogs/Login/WinformResponder.cs
rename to LibationWinForms/Dialogs/Login/WinformLoginCallback.cs
index 28507f4d..5e134863 100644
--- a/LibationWinForms/Dialogs/Login/WinformResponder.cs
+++ b/LibationWinForms/Dialogs/Login/WinformLoginCallback.cs
@@ -5,11 +5,11 @@ using LibationWinForms.Dialogs.Login;
namespace LibationWinForms.Login
{
- public class WinformResponder : ILoginCallback
+ public class WinformLoginCallback : WinformLoginBase, ILoginCallback
{
private Account _account { get; }
- public WinformResponder(Account account)
+ public WinformLoginCallback(Account account)
{
_account = Dinah.Core.ArgumentValidator.EnsureNotNull(account, nameof(account));
}
@@ -17,7 +17,7 @@ namespace LibationWinForms.Login
public string Get2faCode()
{
using var dialog = new _2faCodeDialog();
- if (showDialog(dialog))
+ if (ShowDialog(dialog))
return dialog.Code;
return null;
}
@@ -25,7 +25,7 @@ namespace LibationWinForms.Login
public string GetCaptchaAnswer(byte[] captchaImage)
{
using var dialog = new CaptchaDialog(captchaImage);
- if (showDialog(dialog))
+ if (ShowDialog(dialog))
return dialog.Answer;
return null;
}
@@ -33,15 +33,15 @@ namespace LibationWinForms.Login
public (string name, string value) GetMfaChoice(MfaConfig mfaConfig)
{
using var dialog = new MfaDialog(mfaConfig);
- if (showDialog(dialog))
+ if (ShowDialog(dialog))
return (dialog.SelectedName, dialog.SelectedValue);
return (null, null);
}
public (string email, string password) GetLogin()
{
- using var dialog = new AudibleLoginDialog(_account);
- if (showDialog(dialog))
+ using var dialog = new LoginCallbackDialog(_account);
+ if (ShowDialog(dialog))
return (dialog.Email, dialog.Password);
return (null, null);
}
@@ -49,15 +49,7 @@ namespace LibationWinForms.Login
public void ShowApprovalNeeded()
{
using var dialog = new ApprovalNeededDialog();
- showDialog(dialog);
- }
-
- /// True if ShowDialog's DialogResult == OK
- private static bool showDialog(System.Windows.Forms.Form dialog)
- {
- var result = dialog.ShowDialog();
- Serilog.Log.Logger.Debug("{@DebugInfo}", new { DialogResult = result });
- return result == System.Windows.Forms.DialogResult.OK;
+ ShowDialog(dialog);
}
}
}
\ No newline at end of file
diff --git a/LibationWinForms/Dialogs/Login/WinformLoginChoiceEager.cs b/LibationWinForms/Dialogs/Login/WinformLoginChoiceEager.cs
new file mode 100644
index 00000000..29245662
--- /dev/null
+++ b/LibationWinForms/Dialogs/Login/WinformLoginChoiceEager.cs
@@ -0,0 +1,43 @@
+using System;
+using AudibleApi;
+using InternalUtilities;
+using LibationWinForms.Dialogs.Login;
+
+namespace LibationWinForms.Login
+{
+ public class WinformLoginChoiceEager : WinformLoginBase, ILoginChoiceEager
+ {
+ public ILoginCallback LoginCallback { get; private set; }
+
+ private Account _account { get; }
+
+ public WinformLoginChoiceEager(Account account)
+ {
+ _account = Dinah.Core.ArgumentValidator.EnsureNotNull(account, nameof(account));
+ LoginCallback = new WinformLoginCallback(_account);
+ }
+
+ public ChoiceOut Start(ChoiceIn choiceIn)
+ {
+ using var dialog = new LoginChoiceEagerDialog(_account);
+
+ if (!ShowDialog(dialog))
+ return null;
+
+ switch (dialog.LoginMethod)
+ {
+ case LoginMethod.Api:
+ return ChoiceOut.WithApi(dialog.Email, dialog.Password);
+ case LoginMethod.External:
+ {
+ using var externalDialog = new LoginExternalDialog(_account, choiceIn.LoginUrl);
+ return ShowDialog(externalDialog)
+ ? ChoiceOut.External(externalDialog.ResponseUrl)
+ : null;
+ }
+ default:
+ throw new Exception($"Unknown {nameof(LoginMethod)} value");
+ }
+ }
+ }
+}
diff --git a/LibationWinForms/Dialogs/RemoveBooksDialog.cs b/LibationWinForms/Dialogs/RemoveBooksDialog.cs
index c4faa3f1..f905c54f 100644
--- a/LibationWinForms/Dialogs/RemoveBooksDialog.cs
+++ b/LibationWinForms/Dialogs/RemoveBooksDialog.cs
@@ -62,7 +62,7 @@ namespace LibationWinForms.Dialogs
return;
try
{
- var removedBooks = await LibraryCommands.FindInactiveBooks((account) => new WinformResponder(account), _libraryBooks, _accounts);
+ var removedBooks = await LibraryCommands.FindInactiveBooks((account) => ApiExtended.CreateAsync(account, new WinformLoginChoiceEager(account)), _libraryBooks, _accounts);
var removable = _removableGridEntries.Where(rge => removedBooks.Any(rb => rb.Book.AudibleProductId == rge.AudibleProductId)).ToList();
diff --git a/LibationWinForms/Program.cs b/LibationWinForms/Program.cs
index 66ee6630..cb9bf0ef 100644
--- a/LibationWinForms/Program.cs
+++ b/LibationWinForms/Program.cs
@@ -184,7 +184,7 @@ namespace LibationWinForms
identity.Invalidate();
// re-registers device
- ApiExtended.CreateAsync(new Login.WinformResponder(account), account).GetAwaiter().GetResult();
+ ApiExtended.CreateAsync(account, new Login.WinformLoginChoiceEager(account)).GetAwaiter().GetResult();
}
catch
{