255 lines
9.9 KiB
C#
255 lines
9.9 KiB
C#
using ApplicationServices;
|
|
using AudibleUtilities;
|
|
using Avalonia.Controls;
|
|
using LibationFileManager;
|
|
using ReactiveUI;
|
|
using System;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Avalonia.Input;
|
|
|
|
namespace LibationAvalonia.ViewModels
|
|
{
|
|
public partial class MainVM
|
|
{
|
|
private bool _autoScanChecked = Configuration.Instance.AutoScan;
|
|
private string _removeBooksButtonText = "Remove # Books from Libation";
|
|
private bool _removeBooksButtonEnabled = Design.IsDesignMode;
|
|
private bool _removeButtonsVisible = Design.IsDesignMode;
|
|
private int _numAccountsScanning = 2;
|
|
private int _accountsCount = 0;
|
|
|
|
/// <summary> Auto scanning accounts is enables </summary>
|
|
public bool AutoScanChecked { get => _autoScanChecked; set => Configuration.Instance.AutoScan = this.RaiseAndSetIfChanged(ref _autoScanChecked, value); }
|
|
/// <summary> Display text for the "Remove # Books from Libation" button </summary>
|
|
public string RemoveBooksButtonText { get => _removeBooksButtonText; set => this.RaiseAndSetIfChanged(ref _removeBooksButtonText, value); }
|
|
/// <summary> Indicates if the "Remove # Books from Libation" button is enabled </summary>
|
|
public bool RemoveBooksButtonEnabled { get => _removeBooksButtonEnabled; set { this.RaiseAndSetIfChanged(ref _removeBooksButtonEnabled, value); } }
|
|
/// <summary> Indicates if the "Remove # Books from Libation" and "Done Removing" buttons should be visible </summary>
|
|
public bool RemoveButtonsVisible
|
|
{
|
|
get => _removeButtonsVisible;
|
|
set
|
|
{
|
|
this.RaiseAndSetIfChanged(ref _removeButtonsVisible, value);
|
|
this.RaisePropertyChanged(nameof(RemoveMenuItemsEnabled));
|
|
}
|
|
}
|
|
/// <summary> Indicates if Libation is currently scanning account(s) </summary>
|
|
public bool ActivelyScanning => _numAccountsScanning > 0;
|
|
/// <summary> Indicates if the "Remove Books" menu items are enabled</summary>
|
|
public bool RemoveMenuItemsEnabled => !RemoveButtonsVisible && !ActivelyScanning;
|
|
/// <summary> The library scanning status text </summary>
|
|
public string ScanningText => _numAccountsScanning == 1 ? "Scanning..." : $"Scanning {_numAccountsScanning} accounts...";
|
|
/// <summary> There is at least one Audible account </summary>
|
|
public bool AnyAccounts => AccountsCount > 0;
|
|
/// <summary> There is exactly one Audible account </summary>
|
|
public bool OneAccount => AccountsCount == 1;
|
|
/// <summary> There are more than 1 Audible accounts </summary>
|
|
public bool MultipleAccounts => AccountsCount > 1;
|
|
/// <summary> The number of accounts added to Libation </summary>
|
|
public int AccountsCount
|
|
{
|
|
get => _accountsCount;
|
|
set
|
|
{
|
|
this.RaiseAndSetIfChanged(ref _accountsCount, value);
|
|
this.RaisePropertyChanged(nameof(AnyAccounts));
|
|
this.RaisePropertyChanged(nameof(OneAccount));
|
|
this.RaisePropertyChanged(nameof(MultipleAccounts));
|
|
}
|
|
}
|
|
|
|
|
|
public void Configure_Import()
|
|
{
|
|
MainWindow.Loaded += (_, _) =>
|
|
{
|
|
refreshImportMenu();
|
|
AccountsSettingsPersister.Saved += refreshImportMenu;
|
|
};
|
|
|
|
AutoScanChecked = Configuration.Instance.AutoScan;
|
|
|
|
setyNumScanningAccounts(0);
|
|
LibraryCommands.ScanBegin += (_, accountsLength) => setyNumScanningAccounts(accountsLength);
|
|
LibraryCommands.ScanEnd += (_, newCount) => setyNumScanningAccounts(0);
|
|
|
|
if (!Design.IsDesignMode)
|
|
RemoveButtonsVisible = false;
|
|
}
|
|
|
|
public void ToggleAutoScan() => AutoScanChecked = !AutoScanChecked;
|
|
|
|
public async Task AddAccountsAsync()
|
|
{
|
|
await MessageBox.Show("To load your Audible library, come back here to the Import menu after adding your account");
|
|
await new LibationAvalonia.Dialogs.AccountsDialog().ShowDialog(MainWindow);
|
|
}
|
|
|
|
public async Task ScanAccountAsync()
|
|
{
|
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
|
await scanLibrariesAsync(persister.AccountsSettings.GetAll().FirstOrDefault());
|
|
}
|
|
|
|
public async Task ScanAllAccountsAsync()
|
|
{
|
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
|
await scanLibrariesAsync(persister.AccountsSettings.GetAll().ToArray());
|
|
}
|
|
|
|
public async Task ScanSomeAccountsAsync()
|
|
{
|
|
var scanAccountsDialog = new LibationAvalonia.Dialogs.ScanAccountsDialog();
|
|
|
|
if (await scanAccountsDialog.ShowDialog<DialogResult>(MainWindow) != DialogResult.OK)
|
|
return;
|
|
|
|
if (!scanAccountsDialog.CheckedAccounts.Any())
|
|
return;
|
|
|
|
await scanLibrariesAsync(scanAccountsDialog.CheckedAccounts.ToArray());
|
|
}
|
|
|
|
public async Task RemoveBooksAsync()
|
|
{
|
|
// if 0 accounts, this will not be visible
|
|
// if 1 account, run scanLibrariesRemovedBooks() on this account
|
|
// if multiple accounts, another menu set will open. do not run scanLibrariesRemovedBooks()
|
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
|
var accounts = persister.AccountsSettings.GetAll();
|
|
|
|
if (accounts.Count != 1)
|
|
return;
|
|
|
|
var firstAccount = accounts.Single();
|
|
await scanLibrariesRemovedBooks(firstAccount);
|
|
}
|
|
|
|
// selectively remove books from all accounts
|
|
public async Task RemoveBooksAllAsync()
|
|
{
|
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
|
var allAccounts = persister.AccountsSettings.GetAll();
|
|
await scanLibrariesRemovedBooks(allAccounts.ToArray());
|
|
}
|
|
|
|
public async Task RemoveBooksBtn()
|
|
{
|
|
RemoveBooksButtonEnabled = false;
|
|
await ProductsDisplay.RemoveCheckedBooksAsync();
|
|
RemoveBooksButtonEnabled = true;
|
|
}
|
|
|
|
public async Task DoneRemovingBtn()
|
|
{
|
|
RemoveButtonsVisible = false;
|
|
|
|
ProductsDisplay.DoneRemovingBooks();
|
|
|
|
//Restore the filter
|
|
await PerformFilter(lastGoodFilter);
|
|
}
|
|
|
|
// selectively remove books from some accounts
|
|
public async Task RemoveBooksSomeAsync()
|
|
{
|
|
var scanAccountsDialog = new LibationAvalonia.Dialogs.ScanAccountsDialog();
|
|
|
|
if (await scanAccountsDialog.ShowDialog<DialogResult>(MainWindow) != DialogResult.OK)
|
|
return;
|
|
|
|
if (!scanAccountsDialog.CheckedAccounts.Any())
|
|
return;
|
|
|
|
await scanLibrariesRemovedBooks(scanAccountsDialog.CheckedAccounts.ToArray());
|
|
}
|
|
|
|
public async Task LocateAudiobooksAsync()
|
|
{
|
|
var locateDialog = new LibationAvalonia.Dialogs.LocateAudiobooksDialog();
|
|
await locateDialog.ShowDialog(MainWindow);
|
|
}
|
|
|
|
private void setyNumScanningAccounts(int numScanning)
|
|
{
|
|
_numAccountsScanning = numScanning;
|
|
this.RaisePropertyChanged(nameof(ActivelyScanning));
|
|
this.RaisePropertyChanged(nameof(RemoveMenuItemsEnabled));
|
|
this.RaisePropertyChanged(nameof(ScanningText));
|
|
}
|
|
|
|
private async Task scanLibrariesRemovedBooks(params Account[] accounts)
|
|
{
|
|
//This action is meant to operate on the entire library.
|
|
//For removing books within a filter set, use
|
|
//Visible Books > Remove from library
|
|
|
|
await ProductsDisplay.Filter(null);
|
|
|
|
RemoveBooksButtonEnabled = true;
|
|
RemoveButtonsVisible = true;
|
|
|
|
await ProductsDisplay.ScanAndRemoveBooksAsync(accounts);
|
|
}
|
|
|
|
private async Task scanLibrariesAsync(params Account[] accounts)
|
|
{
|
|
try
|
|
{
|
|
var (totalProcessed, newAdded) = await LibraryCommands.ImportAccountAsync(LibationAvalonia.Dialogs.Login.AvaloniaLoginChoiceEager.ApiExtendedFunc, accounts);
|
|
|
|
// this is here instead of ScanEnd so that the following is only possible when it's user-initiated, not automatic loop
|
|
if (Configuration.Instance.ShowImportedStats && newAdded > 0)
|
|
await MessageBox.Show($"Total processed: {totalProcessed}\r\nNew: {newAdded}");
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
Serilog.Log.Information("Audible login attempt cancelled by user");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await MessageBox.ShowAdminAlert(
|
|
MainWindow,
|
|
"Error importing library. Please try again. If this still happens after 2 or 3 tries, stop and contact administrator",
|
|
"Error importing library",
|
|
ex);
|
|
}
|
|
}
|
|
|
|
private void refreshImportMenu(object _ = null, EventArgs __ = null)
|
|
{
|
|
using var persister = AudibleApiStorage.GetAccountsSettingsPersister();
|
|
AccountsCount = persister.AccountsSettings.Accounts.Count;
|
|
|
|
var importMenuItem = (NativeMenuItem)NativeMenu.GetMenu(MainWindow).Items[0];
|
|
|
|
for (int i = importMenuItem.Menu.Items.Count - 1; i >= 2; i--)
|
|
importMenuItem.Menu.Items.RemoveAt(i);
|
|
|
|
if (AccountsCount < 1)
|
|
{
|
|
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "No accounts yet. Add Account...", Command = ReactiveCommand.Create(AddAccountsAsync) });
|
|
}
|
|
else if (AccountsCount == 1)
|
|
{
|
|
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Scan Library", Command = ReactiveCommand.Create(ScanAccountAsync), Gesture = new KeyGesture(Key.S, KeyModifiers.Alt | KeyModifiers.Meta)});
|
|
importMenuItem.Menu.Items.Add(new NativeMenuItemSeparator());
|
|
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Remove Library Books", Command = ReactiveCommand.Create(RemoveBooksAsync), Gesture = new KeyGesture(Key.R, KeyModifiers.Alt | KeyModifiers.Meta)});
|
|
}
|
|
else
|
|
{
|
|
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Scan Library of All Accounts", Command = ReactiveCommand.Create(ScanAllAccountsAsync), Gesture = new KeyGesture(Key.S, KeyModifiers.Alt | KeyModifiers.Meta)});
|
|
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Scan Library of Some Accounts", Command = ReactiveCommand.Create(ScanSomeAccountsAsync), Gesture = new KeyGesture(Key.S, KeyModifiers.Alt | KeyModifiers.Meta | KeyModifiers.Shift) });
|
|
importMenuItem.Menu.Items.Add(new NativeMenuItemSeparator());
|
|
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Remove Books from All Accounts", Command = ReactiveCommand.Create(RemoveBooksAllAsync), Gesture = new KeyGesture(Key.R, KeyModifiers.Alt | KeyModifiers.Meta)});
|
|
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Remove Books from Some Accounts", Command = ReactiveCommand.Create(RemoveBooksSomeAsync), Gesture = new KeyGesture(Key.R, KeyModifiers.Alt | KeyModifiers.Meta | KeyModifiers.Shift) });
|
|
}
|
|
|
|
importMenuItem.Menu.Items.Add(new NativeMenuItemSeparator());
|
|
importMenuItem.Menu.Items.Add(new NativeMenuItem { Header = "Locate Audiobooks...", Command = ReactiveCommand.Create(LocateAudiobooksAsync) });
|
|
}
|
|
}
|
|
}
|